refactor the way the middlewares are loaded (both app and engine) + tweaks some back-office texts + improve the UI by adding icons on addind a picture links + fix an annoying bug with carrierwave

This commit is contained in:
did 2011-06-28 15:38:13 +02:00
commit 5e8eac53d4
204 changed files with 2878 additions and 1684 deletions

1
.rspec Normal file
View File

@ -0,0 +1 @@
--colour

View File

@ -4,7 +4,7 @@ source :rubygems
gem 'rake', '0.8.7'
gem 'rails', '>= 3.0.8'
gem 'rails', '3.0.9'
gem 'warden'
gem 'devise', '1.3.4'
@ -21,9 +21,12 @@ gem 'formtastic', '~> 1.2.3'
gem 'inherited_resources', '~> 1.1.2'
gem 'rmagick', '2.12.2'
gem 'locomotive_carrierwave', '0.5.4.beta2'
gem 'locomotive_carrierwave', '0.5.4.beta3'
gem 'dragonfly', '~> 0.9.1'
gem 'rack-cache', :require => 'rack/cache'
gem 'custom_fields', '1.0.0.beta.18'
gem 'custom_fields', '1.0.0.beta.19'
gem 'cancan'
gem 'fog', '0.8.2'
gem 'mimetype-fu'
gem 'actionmailer-with-request', :require => 'actionmailer_with_request'

View File

@ -62,6 +62,7 @@ GEM
highline (>= 1.6.1)
json (>= 1.4.6)
rest-client (>= 1.6.1)
cancan (1.6.5)
capybara (0.4.1.2)
celerity (>= 0.7.9)
culerity (>= 0.2.4)
@ -86,7 +87,7 @@ GEM
cucumber-rails (0.3.2)
cucumber (>= 0.8.0)
culerity (0.2.15)
custom_fields (1.0.0.beta.18)
custom_fields (1.0.0.beta.19)
activesupport (>= 3.0.7)
mongoid (= 2.0.2)
daemons (1.1.4)
@ -102,6 +103,8 @@ GEM
orm_adapter (~> 0.0.3)
warden (~> 1.0.3)
diff-lcs (1.1.2)
dragonfly (0.9.4)
rack
erubis (2.6.6)
abstract (>= 1.0.0)
excon (0.6.3)
@ -132,7 +135,7 @@ GEM
heroku (1.19.1)
activesupport (>= 2.1.0)
launchy (~> 0.3.2)
rest-client (>= 1.4.0, < 1.7.0)
rest-client (< 1.7.0, >= 1.4.0)
highline (1.6.2)
httparty (0.7.8)
crack (= 0.1.8)
@ -151,7 +154,7 @@ GEM
linecache (0.43)
linecache19 (0.5.12)
ruby_core_source (>= 0.1.4)
locomotive_carrierwave (0.5.4.beta2)
locomotive_carrierwave (0.5.4.beta3)
activesupport (~> 3.0)
locomotive_jammit-s3 (0.5.4.4)
jammit (>= 0.5.4)
@ -176,7 +179,7 @@ GEM
tzinfo (~> 0.3.22)
net-ssh (2.1.4)
nokogiri (1.4.6)
open4 (1.0.1)
open4 (1.1.0)
orm_adapter (0.0.5)
pickle (0.4.7)
cucumber (>= 0.8)
@ -184,6 +187,8 @@ GEM
polyglot (0.3.1)
proxies (0.2.1)
rack (1.2.3)
rack-cache (1.0.2)
rack (>= 0.4)
rack-mount (0.6.14)
rack (>= 1.0.0)
rack-test (0.5.7)
@ -202,6 +207,7 @@ GEM
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.4)
raindrops (0.7.0)
rake (0.8.7)
rdoc (3.6.1)
responders (0.6.4)
@ -253,9 +259,10 @@ GEM
polyglot (>= 0.3.1)
trollop (1.16.2)
tzinfo (0.3.28)
unicorn (3.7.0)
kgio (~> 2.3)
unicorn (4.0.0)
kgio (~> 2.4)
rack
raindrops (~> 0.6)
warden (1.0.4)
rack (>= 1.0)
will_paginate (2.3.15)
@ -276,14 +283,16 @@ DEPENDENCIES
bson_ext (~> 1.3.0)
bushido
bushido_stub!
cancan
capybara
cucumber (= 0.8.5)
cucumber-rails
custom_fields (= 1.0.0.beta.18)
custom_fields (= 1.0.0.beta.19)
database_cleaner
delayed_job (= 2.1.4)
delayed_job_mongoid (= 1.0.2)
devise (= 1.3.4)
dragonfly (~> 0.9.1)
factory_girl_rails
fog (= 0.8.2)
formtastic (~> 1.2.3)
@ -294,7 +303,7 @@ DEPENDENCIES
inherited_resources (~> 1.1.2)
launchy
linecache (= 0.43)
locomotive_carrierwave (= 0.5.4.beta2)
locomotive_carrierwave (= 0.5.4.beta3)
locomotive_jammit-s3
locomotive_liquid (= 2.2.2)
locomotive_mongoid_acts_as_tree (= 0.1.5.7)
@ -302,7 +311,8 @@ DEPENDENCIES
mocha!
mongoid (~> 2.0.2)
pickle
rails (>= 3.0.8)
rack-cache
rails (= 3.0.9)
rake (= 0.8.7)
rmagick (= 2.12.2)
rspec-rails (= 2.3.1)

View File

@ -19,7 +19,7 @@ h2. Gems
Here is a short list of main gems used in the application.
* Rails 3.0.8
* Rails 3.0.9
* Mongoid 2.0.2 (with MongoDB 1.6)
* Liquid
* Devise

View File

@ -1,25 +0,0 @@
module Admin
class AssetCollectionsController < BaseController
sections 'assets'
before_filter :set_collections
def index
if not @asset_collections.empty?
redirect_to(edit_admin_asset_collection_url(@asset_collections.first)) and return
end
end
def show
@asset_collection = current_site.asset_collections.find(params[:id])
render :action => 'edit'
end
protected
def set_collections
@asset_collections = current_site.asset_collections.not_internal.order_by([[:name, :asc]])
end
end
end

View File

@ -1,29 +1,55 @@
module Admin
class AssetsController < BaseController
sections 'assets'
include ActionView::Helpers::SanitizeHelper
include ActionView::Helpers::TextHelper
before_filter :set_collections_and_current_collection
respond_to :json, :only => [:index, :create, :destroy]
respond_to :json, :only => :update
def create
create! { edit_admin_asset_collection_url(@asset_collection) }
def index
index! do |response|
response.json do
render :json => { :assets => @assets.collect { |asset| asset_to_json(asset) } }
end
end
end
def update
update! { edit_admin_asset_collection_url(@asset_collection) }
def create
@asset = current_site.assets.build(:name => params[:name], :source => params[:file])
create! do |success, failure|
success.json do
render :json => asset_to_json(@asset)
end
failure.json do
render :json => { :status => 'error' }
end
end
rescue Exception => e
render :json => { :status => 'error', :message => e.message }
end
protected
def begin_of_association_chain
@asset_collection
def collection
if params[:image]
@assets ||= begin_of_association_chain.assets.only_image
else
@assets ||= begin_of_association_chain.assets
end
end
def set_collections_and_current_collection
@asset_collections = current_site.asset_collections.not_internal.order_by([[:name, :asc]])
@asset_collection = current_site.asset_collections.find(params[:collection_id])
def asset_to_json(asset)
{
:status => 'success',
:filename => asset.source_filename,
:short_name => truncate(asset.name, :length => 15),
:extname => truncate(asset.extname, :length => 3),
:content_type => asset.content_type,
:url => asset.source.url,
:vignette_url => asset.vignette_url,
:destroy_url => admin_asset_url(asset, :json)
}
end
end

View File

@ -9,11 +9,13 @@ module Admin
before_filter :require_site
load_and_authorize_resource
before_filter :validate_site_membership
before_filter :set_locale
helper_method :sections, :current_site_url, :site_url, :page_url
helper_method :sections, :current_site_url, :site_url, :page_url, :current_ability
# https://rails.lighthouseapp.com/projects/8994/tickets/1905-apphelpers-within-plugin-not-being-mixed-in
Dir[File.dirname(__FILE__) + "/../../helpers/**/*_helper.rb"].each do |file|
@ -26,8 +28,24 @@ module Admin
respond_to :html
rescue_from CanCan::AccessDenied do |exception|
::Locomotive::Logger.info "[CanCan::AccessDenied] #{exception.inspect}"
if request.xhr?
render :json => { :error => exception.message }
else
flash[:alert] = exception.message
redirect_to admin_pages_url
end
end
protected
def current_ability
@current_ability ||= Ability.new(current_admin, current_site)
end
def require_admin
authenticate_admin!
end

View File

@ -5,7 +5,11 @@ module Admin
before_filter :set_content_type
respond_to :json, :only => :update
respond_to :json, :only => [:update, :sort]
skip_load_and_authorize_resource
before_filter :authorize_content
def index
@contents = @content_type.list_or_group_contents
@ -20,7 +24,7 @@ module Admin
end
def sort
@content_type.sort_contents!(params[:order])
@content_type.sort_contents!(params[:children])
respond_with(@content_type, :location => admin_contents_url(@content_type.slug))
end
@ -39,5 +43,9 @@ module Admin
set_content_type
end
def authorize_content
authorize! params[:action].to_sym, ContentInstance
end
end
end

View File

@ -9,6 +9,8 @@ module Admin
before_filter :require_admin, :only => :new
skip_load_and_authorize_resource
def new
if site = current_admin.sites.detect { |s| s._id.to_s == params[:target_id] }
if Rails.env == 'development'

View File

@ -7,6 +7,10 @@ module Admin
actions :edit, :update
skip_load_and_authorize_resource
load_and_authorize_resource :class => 'Site'
respond_to :json, :only => :update
def update

View File

@ -5,6 +5,8 @@ module Admin
before_filter :set_parent_and_fields
skip_load_and_authorize_resource
def edit
@field = @fields.find(params[:id])
render :action => "edit_#{@field.kind.downcase}"
@ -23,14 +25,9 @@ module Admin
protected
def set_parent_and_fields
if params[:parent] == 'asset_collection'
@parent = current_site.asset_collections.where(:slug => params[:slug]).first
@fields = @parent.asset_custom_fields
else
@parent = current_site.content_types.where(:slug => params[:slug]).first
@fields = @parent.content_custom_fields
end
end
end
end

View File

@ -1,55 +0,0 @@
module Admin
class ImagesController < BaseController
include ActionView::Helpers::SanitizeHelper
include ActionView::Helpers::TextHelper
defaults :collection_name => 'assets', :instance_name => 'asset'
respond_to :json, :only => [:index, :create, :destroy]
def index
index! do |response|
response.json do
render :json => { :images => @assets.collect { |image| image_to_json(image) } }
end
end
end
def create
params[:asset] = { :name => params[:name], :source => params[:file] } if params[:file]
create! do |success, failure|
success.json do
render :json => image_to_json(@asset)
end
failure.json do
render :json => { :status => 'error' }
end
end
rescue Exception => e
render :json => { :status => 'error', :message => e.message }
end
protected
def collection
@assets ||= begin_of_association_chain.assets
end
def begin_of_association_chain
@asset_collection ||= AssetCollection.find_or_create_internal(current_site)
end
def image_to_json(image)
{
:status => 'success',
:name => truncate(image.name, :length => 15),
:url => image.source.url,
:vignette_url => image.vignette_url,
:destroy_url => admin_image_url(image, :json)
}
end
end
end

View File

@ -5,6 +5,10 @@ module Admin
actions :show, :new, :create
skip_load_and_authorize_resource
before_filter :authorize_import
def show
@job = Delayed::Job.where({ :job_type => 'import', :site_id => current_site.id }).last
@ -41,5 +45,11 @@ module Admin
end
end
protected
def authorize_import
authorize! :import, Site
end
end
end

View File

@ -15,6 +15,8 @@ module Admin
before_filter :allow_installation?
skip_load_and_authorize_resource
def show
request.get? ? self.handle_get : self.handle_post
end

View File

@ -7,6 +7,8 @@ module Admin
respond_to :json, :only => :update
skip_load_and_authorize_resource
def update
update! { edit_admin_my_account_url }
end

View File

@ -33,7 +33,7 @@ module Admin
end
def get_path
page = current_site.pages.build(:parent => current_site.pages.find(params[:parent_id]), :slug => params[:slug].slugify)
page = current_site.pages.build(:parent => current_site.pages.find(params[:parent_id]), :slug => params[:slug].permalink)
render :json => { :url => page_url(page), :slug => page.slug }
end

View File

@ -7,6 +7,8 @@ module Admin
respond_to :xml
skip_load_and_authorize_resource
def show
@pages = current_site.pages.published
end

View File

@ -49,8 +49,8 @@ module Admin
def sanitize_params
params[:theme_asset] = { :source => params[:file] } if params[:file]
performing_plain_text = params[:theme_asset][:performing_plain_text]
params[:theme_asset].delete(:content_type) if performing_plain_text.blank? || performing_plain_text == 'false'
# performing_plain_text = params[:theme_asset][:performing_plain_text]
# params[:theme_asset].delete(:content_type) if performing_plain_text.blank? || performing_plain_text == 'false'
end
end

View File

@ -1,7 +1,7 @@
module Admin::AccountsHelper
def admin_on?(site = current_site)
site.memberships.detect { |a| a.admin? && a.account == current_admin }
site.memberships.detect { |m| m.admin? && m.account == current_admin }
end
end

View File

@ -45,15 +45,43 @@ module Admin::CustomFieldsHelper
end.compact
end
def options_for_has_one(field)
target_contents_from_field(field).collect { |c| [c._label, c._id] }
def options_for_has_one(field, value)
self.options_for_has_one_or_has_many(field) do |groups|
grouped_options_for_select(groups.collect do |g|
if g[:items].empty?
nil
else
[g[:name], g[:items].collect { |c| [c._label, c._id] }]
end
end.compact, value)
end
end
alias :options_for_has_many :options_for_has_one
def options_for_has_many(field)
self.options_for_has_one_or_has_many(field)
end
def target_contents_from_field(field)
def options_for_has_one_or_has_many(field, &block)
content_type = field.target.constantize._parent
content_type.ordered_contents
if content_type.groupable?
grouped_contents = content_type.list_or_group_contents
if block_given?
block.call(grouped_contents)
else
grouped_contents.collect do |g|
if g[:items].empty?
nil
else
{ :name => g[:name], :items => g[:items].collect { |c| [c._label, c._id] } }
end
end.compact
end
else
contents = content_type.ordered_contents
contents.collect { |c| [c._label, c._id] }
end
end
end

66
app/models/ability.rb Normal file
View File

@ -0,0 +1,66 @@
class Ability
include CanCan::Ability
ROLES = %w(admin designer author)
def initialize(account, site)
@account, @site = account, site
alias_action :index, :show, :edit, :update, :to => :touch
@membership = @site.memberships.where(:account_id => @account.id).first
return false if @membership.blank?
if @membership.admin?
setup_admin_permissions!
else
setup_default_permissions!
setup_designer_permissions! if @membership.designer?
setup_author_permissions! if @membership.author?
end
end
def setup_default_permissions!
cannot :manage, :all
end
def setup_author_permissions!
can :touch, [Page, ThemeAsset]
can :sort, Page
can :manage, [ContentInstance, Asset]
can :touch, Site do |site|
site == @site
end
end
def setup_designer_permissions!
can :manage, Page
can :manage, ContentInstance
can :manage, ContentType
can :manage, Snippet
can :manage, ThemeAsset
can :manage, Site do |site|
site == @site
end
can :import, Site
can :point, Site
can :manage, Membership
end
def setup_admin_permissions!
can :manage, :all
end
end

View File

@ -3,12 +3,11 @@ class Asset
include Mongoid::Document
include Mongoid::Timestamps
## Extensions ##
## extensions ##
include Extensions::Asset::Types
include Extensions::Asset::Vignette
include CustomFields::ProxyClassEnabler
## fields ##
field :name, :type => String
field :content_type, :type => String
field :width, :type => Integer
field :height, :type => Integer
@ -17,32 +16,24 @@ class Asset
mount_uploader :source, AssetUploader
## associations ##
embedded_in :collection, :class_name => 'AssetCollection', :inverse_of => :assets
referenced_in :site
## validations ##
validates_presence_of :name, :source
validates_presence_of :source
## behaviours ##
## methods ##
%w{image stylesheet javascript pdf media}.each do |type|
define_method("#{type}?") do
self.content_type.to_s == type
end
end
alias :name :source_filename
def extname
return nil unless self.source?
File.extname(self.source_filename).gsub(/^\./, '')
end
def site_id # needed by the uploader of custom fields
self.collection.site_id
end
def to_liquid
Locomotive::Liquid::Drops::Asset.new(self)
{ :url => self.source.url }.merge(self.attributes).stringify_keys
end
end

View File

@ -1,81 +0,0 @@
class AssetCollection
include Locomotive::Mongoid::Document
## fields ##
field :name
field :slug
field :internal, :type => Boolean, :default => false
## associations ##
referenced_in :site
embeds_many :assets, :validate => false
## behaviours ##
custom_fields_for :assets
liquid_methods :name, :ordered_assets
## callbacks ##
before_validation :normalize_slug
before_save :store_asset_positions!
after_destroy :remove_uploaded_files
## validations ##
validates_presence_of :site, :name, :slug
validates_uniqueness_of :slug, :scope => :site_id
## named scopes ##
scope :internal, :where => { :internal => true }
scope :not_internal, :where => { :internal => false }
## methods ##
def ordered_assets
self.assets.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
end
def assets_order
self.ordered_assets.collect(&:id).join(',')
end
def assets_order=(order)
@assets_order = order
end
def self.find_or_create_internal(site)
site.asset_collections.internal.first || site.asset_collections.create(:name => 'system', :slug => 'system', :internal => true)
end
protected
def normalize_slug
self.slug = self.name.clone if self.slug.blank? && self.name.present?
self.slug.slugify! if self.slug.present?
end
def store_asset_positions!
return if @assets_order.nil?
ids = @assets_order.split(',').collect { |id| BSON::ObjectId(id) }
ids.each_with_index do |asset_id, index|
self.assets.find(asset_id).position = index
end
self.assets.clone.each do |asset|
if !ids.include?(asset._id)
self.assets.delete(asset)
asset.send(:delete)
end
end
end
def remove_uploaded_files # callbacks are not called on each asset so we do it manually
self.assets.each do |asset|
self.asset_custom_fields.each do |field|
asset.send(:"remove_#{field._name}!") if field.kind == 'file'
end
end
end
end

View File

@ -14,12 +14,14 @@ class ContentInstance
## validations ##
validate :require_highlighted_field
validate :validate_uniqueness_of_slug
validates_presence_of :_slug
## associations ##
embedded_in :content_type, :inverse_of => :contents
## callbacks ##
before_save :set_slug
before_validation :set_slug
before_save :set_visibility
before_create :add_to_list_bottom
after_create :send_notifications
@ -67,8 +69,8 @@ class ContentInstance
protected
def set_slug
_alias = self.highlighted_field_alias
self._slug = self.send(_alias).parameterize('_')
self._slug = self.highlighted_field_value.clone if self._slug.blank? && self.highlighted_field_value.present?
self._slug.permalink! if self._slug.present?
end
def set_visibility
@ -77,7 +79,6 @@ class ContentInstance
end
def add_to_list_bottom
Rails.logger.debug "add_to_list_bottom"
self._position_in_list = self.content_type.contents.size
end
@ -88,6 +89,12 @@ class ContentInstance
end
end
def validate_uniqueness_of_slug
if self._parent.contents.any? { |c| c._id != self._id && c._slug == self._slug }
self.errors.add(:_slug, :taken)
end
end
def highlighted_field_alias
self.content_type.highlighted_field._alias.to_sym
end

View File

@ -107,8 +107,8 @@ class ContentType
self.asc_order? ? list : list.reverse
end
def sort_contents!(order)
order.split(',').each_with_index do |id, position|
def sort_contents!(ids)
ids.each_with_index do |id, position|
self.contents.find(BSON::ObjectId(id))._position_in_list = position
end
self.save
@ -131,7 +131,7 @@ class ContentType
def normalize_slug
self.slug = self.name.clone if self.slug.blank? && self.name.present?
self.slug.slugify! if self.slug.present?
self.slug.permalink! if self.slug.present?
end
def remove_uploaded_files # callbacks are not called on each content so we do it manually

View File

@ -0,0 +1,19 @@
module Extensions
module Asset
module Types
extend ActiveSupport::Concern
included do
%w{media image stylesheet javascript font pdf}.each do |type|
scope :"only_#{type}", where(:content_type => type)
define_method("#{type}?") do
self.content_type.to_s == type
end
end
end
end
end
end

View File

@ -7,7 +7,7 @@ module Extensions
if self.width < 80 && self.height < 80
self.source.url
else
self.source.url(:medium)
Locomotive::Dragonfly.resize_url(self.source, '80x80#')
end
end
end

View File

@ -42,14 +42,6 @@ module Extensions
@item_parsing_errors.try(:each) { |msg| self.errors.add :item_template, msg }
end
# def item_template
# self.read_attribute(:default_item_template) || self.default_item_template
# end
#
# def default_item_template
# '{{ content.highlighted_field_value }}'
# end
end
end

View File

@ -4,6 +4,7 @@ module Extensions
extend ActiveSupport::Concern
included do
field :seo_title, :type => String
field :meta_keywords, :type => String
field :meta_description, :type => String
end

View File

@ -6,7 +6,7 @@ module Extensions
def create_first_one(attributes)
site = self.new(attributes)
site.memberships.build :account => Account.first, :admin => true
site.memberships.build :account => Account.first, :role => 'admin'
site.save

View File

@ -3,7 +3,8 @@ class Membership
include Locomotive::Mongoid::Document
## fields ##
field :admin, :type => Boolean, :default => false
# field :admin, :type => Boolean, :default => false
field :role, :default => 'author'
## associations ##
referenced_in :account, :validate => false
@ -12,8 +13,17 @@ class Membership
## validations ##
validates_presence_of :account
## callbacks ##
before_save :define_role
## methods ##
Ability::ROLES.each do |_role|
define_method("#{_role}?") do
self.role == _role
end
end
def email; @email; end
def email=(email)
@ -36,4 +46,14 @@ class Membership
end
end
def ability
@ability ||= Ability.new(self.account, self.site)
end
protected
def define_role
self.role = Ability::ROLES.include?(role.downcase) ? role.downcase : Ability::ROLES.first
end
end

View File

@ -97,7 +97,7 @@ class Page
def normalize_slug
self.slug = self.title.clone if self.slug.blank? && self.title.present?
self.slug.slugify!(:without_extension => true) if self.slug.present?
self.slug.permalink! if self.slug.present?
end
def set_default_raw_template

View File

@ -5,17 +5,17 @@ class Site
## Extensions ##
extend Extensions::Site::SubdomainDomains
extend Extensions::Site::FirstInstallation
extend Extensions::Site::FirstInstallation
include Extensions::Shared::Seo
## fields ##
field :name
field :meta_keywords
field :meta_description
## associations ##
references_many :pages, :validate => false
references_many :snippets, :dependent => :destroy, :validate => false
references_many :theme_assets, :dependent => :destroy, :validate => false
references_many :asset_collections, :dependent => :destroy, :validate => false
references_many :assets, :dependent => :destroy, :validate => false
references_many :content_types, :dependent => :destroy, :validate => false
embeds_many :memberships
@ -28,6 +28,7 @@ class Site
## behaviours ##
enable_subdomain_n_domains_if_multi_sites
accepts_nested_attributes_for :memberships
## methods ##

View File

@ -26,7 +26,7 @@ class Snippet
def normalize_slug
# TODO: refactor it
self.slug = self.name.clone if self.slug.blank? && self.name.present?
self.slug.slugify!(:without_extension => true, :downcase => true) if self.slug.present?
self.slug.permalink! if self.slug.present?
end
def update_templates

View File

@ -2,6 +2,9 @@ class ThemeAsset
include Locomotive::Mongoid::Document
## extensions ##
include Extensions::Asset::Types
## fields ##
field :local_path
field :content_type
@ -35,16 +38,10 @@ class ThemeAsset
scope :visible, lambda { |all| all ? {} : { :where => { :hidden => false } } }
## accessors ##
attr_accessor :plain_text_name, :plain_text, :performing_plain_text
attr_accessor :plain_text_name, :plain_text, :plain_text_type, :performing_plain_text
## methods ##
%w{media image stylesheet javascript font}.each do |type|
define_method("#{type}?") do
self.content_type.to_s == type
end
end
def stylesheet_or_javascript?
self.stylesheet? || self.javascript?
end
@ -77,11 +74,17 @@ class ThemeAsset
end
end
def plain_text_type
@plain_text_type || (stylesheet_or_javascript? ? self.content_type : nil)
end
def performing_plain_text?
Boolean.set(self.performing_plain_text) || false
end
def store_plain_text
self.content_type ||= @plain_text_type if self.performing_plain_text?
data = self.performing_plain_text? ? self.plain_text : self.source.read
return if !self.stylesheet_or_javascript? || self.plain_text_name.blank? || data.blank?
@ -95,7 +98,7 @@ class ThemeAsset
end
def to_liquid
{ :url => self.source.url }.merge(self.attributes)
{ :url => self.source.url }.merge(self.attributes).stringify_keys
end
def self.all_grouped_by_folder(site, include_all = true)

View File

@ -2,26 +2,10 @@
class AssetUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
include Locomotive::CarrierWave::Uploader::Asset
version :thumb, :if => :image? do
process :resize_to_fill => [50, 50]
process :convert => 'png'
end
version :medium, :if => :image? do
process :resize_to_fill => [80, 80]
process :convert => 'png'
end
version :preview, :if => :image? do
process :resize_to_fit => [880, 1100]
process :convert => 'png'
end
def store_dir
self.build_store_dir('sites', model.collection.site_id, 'assets', model.id)
self.build_store_dir('sites', model.site_id, 'assets', model.id)
end
end

View File

@ -1,36 +0,0 @@
- title link_to(@asset_collection.name.blank? ? @asset_collection.name_was : @asset_collection.name, '#', :rel => 'asset_collection_name', :title => t('.ask_for_name'), :class => 'editable')
- content_for :head do
= include_javascripts :asset_collections
= include_stylesheets :fancybox
- content_for :submenu do
= render 'admin/shared/menu/assets'
- content_for :actions do
= render 'admin/shared/actions/assets'
%p!= t('.help')
- content_for :buttons do
= admin_button_tag :add_asset, new_admin_asset_url(@asset_collection), :class => 'new'
%p.no-items{ :style => "#{'display: none' unless @asset_collection.assets.empty? }" }
!= t('.no_items', :url => new_admin_asset_url(@asset_collection))
%ul#assets.assets.sortable
= render :partial => 'asset', :collection => @asset_collection.ordered_assets
%li.clear
= semantic_form_for @asset_collection, :url => admin_asset_collection_url(@asset_collection), :html => { :multipart => true } do |f|
= f.hidden_field :assets_order
= f.foldable_inputs :name => :options do
= f.input :name
= f.input :slug, :required => false
= render 'admin/custom_fields/index', :form => f, :collection_name => 'assets'
= render 'admin/shared/form_actions', :delete_button => link_to(content_tag(:em, escape_once('&nbsp;')) + t('.destroy'), admin_asset_collection_url(@asset_collection), :confirm => t('admin.messages.confirm'), :method => :delete, :class => 'button small remove'), :button_label => :update
= render 'admin/custom_fields/edit_field'

View File

@ -1,11 +0,0 @@
- title t('.title')
- content_for :submenu do
= render 'admin/shared/menu/assets'
- content_for :actions do
= render 'admin/shared/actions/assets'
%p!= t('.help')
%p.no-items!= t('.no_items', :url => new_admin_asset_collection_url)

View File

@ -1,20 +0,0 @@
- title t('.title')
- content_for :head do
= include_javascripts :asset_collections
- content_for :submenu do
= render 'admin/shared/menu/assets'
- content_for :actions do
= render 'admin/shared/actions/assets'
%p!= t('.help')
= semantic_form_for @asset_collection, :url => admin_asset_collections_url do |f|
= f.inputs :name => :information do
= f.input :name
= f.input :slug, :required => false
= render 'admin/shared/form_actions', :back_url => admin_asset_collections_url, :button_label => :create

View File

@ -1,5 +1,5 @@
%li{ :id => "asset-#{asset.id}", :class => "asset #{'last' if (asset_counter + 1) % 6 == 0}"}
%h4= link_to truncate(asset.name, :length => 17), edit_admin_asset_path(@asset_collection, asset)
%h4= link_to truncate(asset.name, :length => 17), edit_admin_asset_path(asset)
= vignette_tag(asset)
.actions
= link_to image_tag('admin/list/icons/cross.png'), '#', :class => 'remove', :confirm => t('admin.messages.confirm')

View File

@ -1,17 +0,0 @@
- content_for :head do
= include_javascripts :edit_custom_fields, :assets
= include_stylesheets :fancybox
= f.inputs :name => :information do
= f.input :name
= f.input :source, :hint_options => @asset.persisted? ? { :url => link_to(@asset.source_filename, @asset.source.url), :escaping => false } : {}
- unless @asset.custom_fields.blank?
= render 'admin/custom_fields/custom_form', :form => f, :title => :other_fields, :parent => @asset_collection
- if @asset.image? && @asset.valid?
= f.foldable_inputs :name => "#{t('formtastic.titles.preview')} #{image_dimensions_and_size(@asset)}", :class => 'preview' do
%li
.image
.inside
= image_tag(@asset.source.url(:preview))

View File

@ -1,18 +0,0 @@
- title t('.title')
- content_for :submenu do
= render 'admin/shared/menu/assets'
- content_for :actions do
= render 'admin/shared/actions/assets'
- content_for :buttons do
= admin_button_tag t('admin.asset_collections.edit.add_asset'), new_admin_asset_url(@asset_collection), :class => 'new'
%p!= t('.help')
= semantic_form_for @asset, :url => admin_asset_url(@asset_collection, @asset), :html => { :multipart => true, :class => 'save-with-shortcut' } do |form|
= render 'form', :f => form
= render 'admin/shared/form_actions', :back_url => edit_admin_asset_collection_url(@asset_collection), :button_label => :update

View File

@ -1,15 +0,0 @@
- title t('.title')
- content_for :submenu do
= render 'admin/shared/menu/assets'
- content_for :actions do
= render 'admin/shared/actions/assets'
%p!= t('.help')
= semantic_form_for @asset, :url => admin_assets_url(@asset_collection), :html => { :multipart => true } do |form|
= render 'form', :f => form
= render 'admin/shared/form_actions', :back_url => edit_admin_asset_collection_url(@asset_collection), :button_label => :create

View File

@ -2,9 +2,11 @@
= include_javascripts :edit_custom_fields, :contents
= include_stylesheets :fancybox
= f.foldable_inputs :name => :meta do
= render 'admin/custom_fields/custom_form', :form => f, :title => :attributes, :parent => @content_type
= f.foldable_inputs :name => :advanced_options do
= f.input :_slug
= f.input :seo_title
= f.input :meta_keywords
= f.input :meta_description
= render 'admin/custom_fields/custom_form', :form => f, :title => :attributes, :parent => @content_type

View File

@ -1,7 +1,7 @@
- if contents.empty?
%p.no-items!= t('.no_items', :url => new_admin_content_url(@content_type.slug))
- else
%ul{ :id => 'contents-list', :class => "list #{'sortable' if @content_type.order_by == '_position_in_list'}" }
%ul{ :id => 'contents-list', :class => "list #{'sortable' if @content_type.order_by == '_position_in_list'}", :'data-url' => sort_admin_contents_path(@content_type.slug, :json) }
- contents.each do |content|
%li.content{ :id => "content-#{content._id}" }
%em

View File

@ -7,7 +7,9 @@
= render 'admin/shared/actions/contents'
- content_for :buttons do
- if can?(:manage, ContentType)
= admin_button_tag t('admin.contents.index.edit'), edit_admin_content_type_url(@content_type), :class => 'edit'
= admin_button_tag t('admin.contents.index.new'), new_admin_content_url(@content_type.slug), :class => 'new'
%p= @content_type.description

View File

@ -10,7 +10,9 @@
= include_javascripts :contents
- content_for :buttons do
- if can?(:manage, ContentType)
= admin_button_tag :edit, edit_admin_content_type_url(@content_type), :class => 'edit'
= admin_button_tag :new, new_admin_content_url(@content_type.slug), :class => 'new'
- if @content_type.description.present?
@ -24,7 +26,7 @@
- else
= render 'list', :contents => @contents
= form_tag sort_admin_contents_path(@content_type.slug), :method => :put, :class => 'formtastic' do
= hidden_field_tag :order
= render 'admin/shared/form_actions', :delete_button => link_to(content_tag(:em, escape_once('&nbsp;')) + t('.destroy'), admin_content_type_url(@content_type), :confirm => t('admin.messages.confirm'), :method => :delete, :class => 'button small remove'), :button_label => :update
- if can?(:manage, ContentType)
#local-actions-bottom-bar
%p.tleft
= link_to(content_tag(:em, escape_once('&nbsp;')) + t('.destroy'), admin_content_type_url(@content_type), :confirm => t('admin.messages.confirm'), :method => :delete, :class => 'button small remove')

View File

@ -6,6 +6,7 @@
- content_for :actions do
= render 'admin/shared/actions/contents'
- if can?(:manage, ContentType)
- content_for :buttons do
= admin_button_tag t('admin.contents.index.edit'), edit_admin_content_type_url(@content_type), :class => 'edit'

View File

@ -4,11 +4,12 @@
= f.foldable_inputs :name => :information, :style => "#{'display: none' unless @site.new_record?}" do
= f.input :name, :required => false
= f.foldable_inputs :name => :meta do
= f.foldable_inputs :name => :seo do
= f.input :seo_title
= f.input :meta_keywords
= f.input :meta_description
- if can?(:point, Site)
- if manage_subdomain_or_domains?
= f.foldable_inputs :name => :access_points, :class => 'editable-list off' do
@ -41,12 +42,27 @@
%button{ :class => 'button light add', :type => 'button' }
%span!= t('admin.buttons.new_item')
= f.foldable_inputs :name => :memberships, :class => 'memberships' do
- @site.memberships.each_with_index do |membership, index|
- account = membership.account
%li{ :class => "item #{'last' if index == @site.memberships.size - 1}" }
- if can?(:manage, Membership)
= f.foldable_inputs :name => :memberships, :class => 'memberships off' do
= f.semantic_fields_for :memberships do |fm|
- membership, account = fm.object, fm.object.account
%li.item.membership{ :'data-role' => membership.role }
%strong= account.name
%em= account.email
- if account != current_admin
%em.email= account.email
- if current_admin != account
.role
%em.editable= t("admin.memberships.roles.#{membership.role}")
= fm.select :role, Ability::ROLES.collect { |r| [t("admin.memberships.roles.#{r}"), r] }, :include_blank => false
%span.actions
= link_to image_tag('admin/form/icons/trash.png'), admin_membership_url(membership), :class => 'remove first', :confirm =>t('admin.messages.confirm'), :method => :delete
- else
.role
%em.locked= t("admin.memberships.roles.#{membership.role}")

View File

@ -3,6 +3,7 @@
- content_for :submenu do
= render 'admin/shared/menu/settings'
- if can?(:manage, @site)
- content_for :buttons do
= admin_button_tag :import, new_admin_import_url, :class => 'new'
= admin_button_tag t('.new_membership'), new_admin_membership_url, :class => 'new'

View File

@ -29,7 +29,7 @@
&mdash;
%em {{kind_name}}
%em.editable {{kind_name}}
= select_tag '{{base_name}}[kind]', options_for_select(options_for_field_kind), :'data-field' => 'kind'

View File

@ -1,4 +1,6 @@
- field.target.constantize.reload_parent! # to make sure all the contents from the parent are loaded
= form.input field._alias.to_sym, :label => field.label, :hint => field.hint, :input_html => { :class => 'has_one' }, :as => :select, :collection => options_for_has_one(field), :selected => form.object.send(field._alias.to_sym).try(:_id)
- selected_id = form.object.send(field._alias.to_sym).try(:_id)
= form.input field._alias.to_sym, :label => field.label, :hint => field.hint, :input_html => { :class => 'has_one' }, :as => :select, :collection => options_for_has_one(field, selected_id), :selected => selected_id

View File

@ -9,7 +9,7 @@
%p!= t('.help')
%ul{ :id => 'import-steps', :class => 'list', :'data-url' => admin_import_url(:json), :'data-success-message' => t('.messages.success'), :'data-failure-message' => t('.messages.failure') }
- %w(site content_types assets asset_collections snippets pages).each do |step|
- %w(site content_types assets snippets pages).each do |step|
%li{ :id => "#{step}-step" }
%em
%strong

View File

@ -8,7 +8,7 @@
- if multi_sites?
- content_for :buttons do
= admin_button_tag t('.new_site'), new_admin_site_url, :class => 'new'
= admin_button_tag t('.new_site'), new_admin_site_url, :class => 'new' if can?(:manage, Site)
%p= t('.help')

View File

@ -2,6 +2,8 @@
= include_javascripts :image_picker, :edit_page
= include_stylesheets :editable_elements, :fancybox
- if can?(:manage, @page)
= f.foldable_inputs :name => :information do
= f.input :title
@ -9,13 +11,18 @@
- if not @page.index? and not @page.not_found?
= f.input :parent_id, :as => :select, :collection => parent_pages_options, :include_blank => false
= f.input :slug, :required => false, :hint => @page.slug.blank? ? '&nbsp;' : page_url(@page), :input_html => { :data_url => get_path_admin_pages_url, :disabled => @page.index? || @page.not_found? }, :wrapper_html => { :style => "#{'display: none' if @page.templatized?}; height: 50px" }
= f.input :slug, :required => false, :hint => @page.slug.blank? ? '&nbsp;' : page_url(@page), :input_html => { :'data-url' => get_path_admin_pages_url, :disabled => @page.index? || @page.not_found? }, :wrapper_html => { :style => "#{'display: none' if @page.templatized?}; height: 50px" }
= f.foldable_inputs :name => :meta do
= render 'editable_elements', :page => @page
= f.foldable_inputs :name => :seo do
= f.input :seo_title
= f.input :meta_keywords
= f.input :meta_description
- if can?(:manage, @page)
= f.foldable_inputs :name => :advanced_options do
= f.input :content_type_id, :as => :select, :collection => current_site.content_types.all.to_a, :include_blank => false, :wrapper_html => { :style => "#{'display: none' unless @page.templatized?}; height: 50px" }
@ -36,7 +43,6 @@
= f.input :redirect_url, :required => true, :wrapper_html => { :style => "#{'display: none' unless @page.redirect?}" }
= render 'editable_elements', :page => @page
= f.foldable_inputs :name => :raw_template do
= f.custom_input :value, :css => 'code full', :with_label => false do
@ -45,4 +51,4 @@
= f.text_area :raw_template
= f.errors_on :template
.more
= link_to t('admin.image_picker.link'), admin_theme_assets_path, :id => 'image-picker-link'
= link_to t('admin.image_picker.link'), admin_theme_assets_path, :id => 'image-picker-link', :class => 'picture'

View File

@ -1,5 +1,8 @@
%li{ :id => "item-#{page.id}", :class => "#{'not-found' if page.not_found? } #{'templatized' if page.templatized?}"}
- with_children = !page.children.empty?
- children = can?(:manage, page) ? page.children : page.children.find_all { |p| !p.templatized? }
- with_children = !children.empty?
- if not page.index? and with_children
= image_tag 'admin/list/icons/node_closed.png', :class => 'toggler'
@ -10,9 +13,10 @@
%span!= t('.updated_at')
= l page.updated_at, :format => :short
- if not page.index? and not page.not_found?
- if !page.index_or_not_found? && can?(:manage, page)
= link_to image_tag('admin/list/icons/trash.png'), admin_page_url(page), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete
- if with_children
%ul{ :id => "folder-#{page._id}", :class => "folder depth-#{(page.depth || 0) + 1}", :data_url => sort_admin_page_url(page), :style => "display: #{cookies["folder-#{page._id}"] || 'block'}" }
= render page.children
%ul{ :id => "folder-#{page._id}", :class => "folder depth-#{(page.depth || 0) + 1}", :'data-url' => sort_admin_page_url(page), :style => "display: #{cookies["folder-#{page._id}"] || 'block'}" }
= render children

View File

@ -9,6 +9,7 @@
- content_for :actions do
= render 'admin/shared/actions/contents'
- if can? :create, Page
- content_for :buttons do
= admin_button_tag :new, new_admin_page_url, :class => 'new'

View File

@ -1,4 +1,4 @@
.actions
#local-actions-bottom-bar
.span-12
%p
- if defined?(back_url)

View File

@ -1,5 +1,4 @@
%ul#menu
= admin_menu_item('contents', admin_pages_url)
= admin_menu_item('assets', admin_asset_collections_url)
= admin_menu_item('settings', edit_admin_current_site_url)
%li.clear

View File

@ -1 +0,0 @@
= link_to content_tag(:span, t('admin.asset_collections.index.new')), new_admin_asset_collection_url

View File

@ -1 +1,2 @@
- if can? :manage, ContentType
= link_to content_tag(:em) + content_tag(:span, t('admin.content_types.index.new')), new_admin_content_type_url

View File

@ -1,3 +0,0 @@
- @asset_collections.each do |c|
%li
= link_to content_tag(:span, truncate(c.name, :length => 20)), edit_admin_asset_collection_url(c), :class => "#{'on' if @asset_collection.id == c.id}"

View File

@ -1,4 +1,5 @@
= admin_submenu_item 'pages', admin_pages_url do
- if can? :manage, @page
.header
%p= link_to t('admin.pages.index.new'), new_admin_page_url
.inner
@ -12,6 +13,10 @@
- each_content_type_menu_item do |content_type|
.header
%p= link_to t('admin.contents.index.new'), new_admin_content_url(content_type.slug)
- if can? :manage, content_type
%p.edit= link_to t('admin.contents.index.edit'), edit_admin_content_type_url(content_type)
.inner
%h2!= t('admin.contents.index.lastest_items')
%ul

View File

@ -11,4 +11,4 @@
%code{ :class => 'html' }
= f.text_area :template
.more
= link_to t('admin.image_picker.link'), admin_theme_assets_path, :id => 'image-picker-link'
= link_to t('admin.image_picker.link'), admin_theme_assets_path, :id => 'image-picker-link', :class => 'picture'

View File

@ -6,7 +6,7 @@
- content_for :buttons do
= admin_button_tag t('admin.snippets.index.new'), new_admin_snippet_url, :class => 'new'
%p!= t('.help')
%p!= t('.help', :slug => @snippet.slug)
= semantic_form_for @snippet, :url => admin_snippet_url(@snippet), :html => { :class => 'save-with-shortcut' } do |form|

View File

@ -19,14 +19,14 @@
- if @theme_asset.new_record?
= f.input :plain_text_name
= f.custom_input :content_type do
= f.select :content_type, %w(stylesheet javascript)
= f.custom_input :plain_text_type do
= f.select :plain_text_type, %w(stylesheet javascript)
= f.custom_input :plain_text, :css => 'full', :with_label => false do
%code{ :class => (@theme_asset.size && @theme_asset.size > 40000 ? 'nude' : (@theme_asset.content_type || 'stylesheet')) }
= f.text_area :plain_text
.more
= link_to t('admin.image_picker.link'), admin_theme_assets_path, :id => 'image-picker-link'
= link_to t('admin.image_picker.link'), admin_theme_assets_path, :id => 'image-picker-link', :class => 'picture'
%span.alt
!= t('admin.theme_assets.form.choose_file')

View File

@ -3,10 +3,16 @@
- content_for :submenu do
= render 'admin/shared/menu/settings'
- if can?(:manage, ThemeAsset)
- content_for :buttons do
= admin_button_tag t('admin.theme_assets.index.new'), new_admin_theme_asset_url, :class => 'new'
%p!= t('.help', :url => @theme_asset.source.url)
%p
- if %w(image javascript stylesheet).include?(@theme_asset.content_type.to_s)
!= t(".help_#{@theme_asset.content_type}", :path => @theme_asset.local_path(true), :width => @theme_asset.width, :height => @theme_asset.height)
!= t('.help', :url => @theme_asset.source.url)
= semantic_form_for @theme_asset, :url => admin_theme_asset_url(@theme_asset), :html => { :multipart => true, :class => 'save-with-shortcut' } do |form|

View File

@ -4,12 +4,13 @@
= render 'admin/shared/menu/settings'
- content_for :buttons do
= admin_button_tag t('admin.theme_assets.index.all'), all_admin_theme_assets_url, :class => 'show'
= admin_button_tag t('admin.snippets.index.new'), new_admin_snippet_url, :class => 'new'
= admin_button_tag :new, new_admin_theme_asset_url, :class => 'new'
= admin_button_tag t('admin.theme_assets.index.all'), all_admin_theme_assets_url, :class => 'show' if can?(:manage, ThemeAsset)
= admin_button_tag t('admin.snippets.index.new'), new_admin_snippet_url, :class => 'new' if can?(:manage, Snippet)
= admin_button_tag :new, new_admin_theme_asset_url, :class => 'new' if can?(:manage, ThemeAsset)
%p!= t('.help')
- if can?(:manage, Snippet)
%h3!= t('.snippets')
- if @snippets.empty?
%p.no-items!= t('admin.snippets.index.no_items', :url => new_admin_snippet_url)
@ -19,6 +20,7 @@
%br
- if can?(:manage, ThemeAsset)
%h3!= t('.css_and_js')
- if @js_and_css_assets.empty?
%p.no-items!= t('.no_items', :url => new_admin_theme_asset_url)
@ -35,6 +37,7 @@
%ul.list.theme-assets
= render :partial => 'asset', :collection => @assets[:images]
- if can?(:manage, ThemeAsset)
- if @assets[:fonts]
%br

View File

@ -45,7 +45,5 @@ module Locomotive
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters << :password
config.middleware.insert_after Rack::Lock, '::Locomotive::Middlewares::Fonts', :path => %r{^/fonts}
end
end

View File

@ -38,13 +38,6 @@ javascripts:
- public/javascripts/admin/plugins/mustache.js
- public/javascripts/admin/custom_fields/category.js
- public/javascripts/admin/custom_fields/has_many.js
asset_collections:
- public/javascripts/admin/plugins/fancybox.js
- public/javascripts/admin/plugins/mustache.js
- public/javascripts/admin/custom_fields.js
- public/javascripts/admin/asset_collections.js
assets:
- public/javascripts/admin/assets.js
contents:
- public/javascripts/admin/plugins/tiny_mce/tinymce.js
- public/javascripts/admin/contents.js

View File

@ -0,0 +1,31 @@
require 'locomotive'
unless Locomotive.engine?
require 'dragonfly'
## initialize Dragonfly ##
app = Dragonfly[:images]
app.configure_with(:rails)
app.configure_with(:imagemagick)
## configure it ##
Dragonfly[:images].configure do |c|
# Convert absolute location needs to be specified
# to avoid issues with Phusion Passenger not using $PATH
convert = `which convert`.strip.presence || "/usr/local/bin/convert"
c.convert_command = convert
c.identify_command = convert
c.allow_fetch_url = true
c.allow_fetch_file = true
c.url_format = '/images/dynamic/:job/:basename.:format'
end
end

View File

@ -62,4 +62,18 @@ Locomotive.configure do |config|
# config.mailer_sender = 'support'
# # => 'support@heroku.com' (Heroku), 'support@bushi.do' (Bushido), 'support@example.com' (Dev) or 'support@<your_hosting_platform>' (Multi-sites)
config.mailer_sender = 'support'
# Rack-cache settings, mainly used for the inline resizing image module. Default options:
# config.rack_cache = {
# :verbose => true,
# :metastore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/meta"), # URI encoded in case of spaces
# :entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")
# }
# If you do want to disable it for good, just use the following syntax
# config.rack_cache = false
#
# Note: by default, rack/cache is disabled in the Heroku platform
end unless Locomotive.engine? || Rails.env.test?
# Rails.application.middleware.use '::Locomotive::Middlewares::SeoTrailingSlash'

View File

@ -61,8 +61,10 @@ de:
title: Optionen
help: Organisiere alle Optionen für die Auswahlbox.
collection_label: Liste der Optionen
custom_form:
types:
category:
edit_categories: Optionen
file:
delete_file: Datei löschen
index:
is_required: ist erforderlich
@ -190,22 +192,6 @@ de:
title: Bilder anzeigen
no_items: "Momentan gibt es keine Bilder."
asset_collections:
index:
title: Galerie
help: "Der Name der Galerie kann durch darauf klicken bearbeitet werden. Du kannst die Bilder in einer Galerie bearbeiten, indem du neue Felder hinzufügst."
new: neue Galerie
no_items: "Momentan gibt es keine Galerien. Klicke einfach <a href='%{url}'>hier</a>, um eine neue Galerie zu erstellen."
new:
title: Neue Galerie
help: "Gib erst mal einen Namen ein. Alle anderen Einstellungen kannst du vornehmen, sobald das Formular abgesendet ist."
edit:
help: "Der Name der Galerie kann durch darauf klicken bearbeitet werden. Du kannst Bilder in einer Galerie bearbeiten, indem du neue Felder hinzufügst."
add_asset: Bild hinzufügen
destroy: Galerie löschen
no_items: "Momentan gibt es keine Bilder. Klicke einfach <a href='%{url}'>hier</a>, um das erste Bild zu erstellen."
ask_for_name: "Gib den neuen Namen ein"
assets:
new:
title: Neues Bild
@ -287,7 +273,6 @@ de:
site: Webseiten-Informationen
content_types: Benutzerdefinierte Inhalts-Typen
assets: Template-Dateien
asset_collections: Galerien
snippets: Snippets
pages: Seiten
messages:

View File

@ -60,8 +60,10 @@ en:
title: Edit options
help: Manage the list of options for your select box.
collection_label: List of options
custom_form:
types:
category:
edit_categories: Edit options
file:
delete_file: Delete file
index:
is_required: is required
@ -99,7 +101,7 @@ en:
updated_at: updated at
edit:
show: show
help: "The page title can be updated by clicking it."
help: "The page title can be updated by clicking it. To apply your changes, click on the \"Update\" button."
ask_for_title: "Please type the new page title"
form:
delete_file: Delete file
@ -123,7 +125,7 @@ en:
help: "Fill in the form below to update your snippet."
edit:
title: Editing snippet
help: "Fill in the form below to update your snippet."
help: "Include your snippet in your page templates with the following liquid code : <span class='code'>{% include '%{slug}' %}</span>."
snippet:
updated_at: Updated at
@ -136,10 +138,14 @@ en:
edit:
import: import
new_membership: add account
help: "The site name can be updated by clicking it."
help: "The site name can be updated by clicking it. To apply your changes, click on the \"Update\" button."
ask_for_name: "Please type the new site name"
memberships:
roles:
admin: Administrator
designer: Designer
author: Author
new:
title: New membership
help: "Please give the account email to add. If it does not exist, you will be redirected to the account creation form."
@ -151,7 +157,7 @@ en:
my_accounts:
edit:
help: "Your name can be updated by clicking it."
help: "Your name can be updated by clicking it. To apply your changes, click on the \"Update\" button."
new_site: new site
en: English
de: German
@ -163,7 +169,7 @@ en:
theme_assets:
index:
title: Listing theme files
help: "The theme files section is the place where you manage the files needed by your layout, ...etc. If you need to manage an image gallery, go to the Assets section instead."
help: "The theme files section is the place where you manage the files needed by your layout, snippets...etc. If you need to manage an image gallery, create a new content type instead.<br/><b>Warning:</b> you may not see all the assets depending on your rights."
all: all assets
new: new file
snippets: Snippets
@ -171,7 +177,7 @@ en:
fonts: Fonts
images: Images
medias: Medias
no_items: "There are no files for now. Just click <a href=\"%{url}\">here</a> to create the first one."
no_items: "There are no files for now."
asset:
updated_at: Updated at
new:
@ -179,7 +185,10 @@ en:
help: "You have the choice to either upload any file or to copy/paste a stylesheet or a javascript in plain text."
edit:
title: "Editing %{file}"
help: "This asset is accessible from the following url: <a href='%{url}'>%{url}</a>"
help: "This asset is accessible directly from the following url: <a href='%{url}'>%{url}</a>"
help_image: "Include your image in your page templates or snippets with the following liquid code : <span class='code'>{{ '%{path}' | theme_image_tag }}</span>.<br/>Your current image dimensions : <b>%{width}px x %{height}px</b>.<br/>"
help_javascript: "Include your javascript file in your page templates or snippets with the following code : <span class='code'>{{ '%{path}' | javascript_tag }}</span>.<br/>"
help_stylesheet: "Include your stylesheet file in your page templates or snippets with the following code : <span class='code'>{{ '%{path}' | stylesheet_tag }}</span>.<br/>"
form:
picker_link: Insert a file into the code
choose_file: Choose file
@ -188,22 +197,6 @@ en:
title: Listing images
no_items: "There are no files for now."
asset_collections:
index:
title: Asset collections
help: "The collection name can be updated by clicking it. You can customize assets in a collection by adding new fields."
new: new collection
no_items: "There are no collections for now. Just click <a href=\"%{url}\">here</a> to create the first one."
new:
title: New collection
help: "For now, just type a name. Other settings will come once the form is sent."
edit:
help: "The collection name can be updated by clicking it. You can customize assets in a collection by adding new fields."
add_asset: add asset
destroy: remove collection
no_items: "There are no assets for now. Just click <a href=\"%{url}\">here</a> to create the first one."
ask_for_name: "Please type the new name"
assets:
new:
title: New asset
@ -225,8 +218,8 @@ en:
new_item: new item
form:
order_by:
created_at: 'By "created at" date'
updated_at: 'By "updated at" date'
created_at: 'By creation date'
updated_at: 'By updating date'
position_in_list: Manually
order_direction:
asc: Ascending
@ -268,7 +261,6 @@ en:
site: Site information
content_types: Custom content types
assets: Theme files
asset_collections: Asset collections
snippets: Snippets
pages: Pages
messages:

View File

@ -60,8 +60,10 @@ fr:
title: Editer options
help: Gèrer la liste des options de votre liste déroulante
collection_label: Liste des options
custom_form:
types:
category:
edit_categories: Editer options
file:
delete_file: Supprimer fichier
index:
is_required: est obligatoire
@ -99,7 +101,7 @@ fr:
updated_at: Mise à jour le
edit:
show: voir
help: "Le titre de la page est modifiable en cliquant dessus."
help: "Le titre de la page est modifiable en cliquant dessus. Pour appliquer votre modification, cliquez après sur le bouton \"Modifier\""
ask_for_title: "Veuillez entrer le nouveau titre"
form:
delete_file: Supprimer fichier
@ -124,6 +126,7 @@ fr:
edit:
title: Edition snippet
help: "Remplissez le formulaire ci-dessous pour mettre à jour votre snippet."
help: "Utilisez votre snippet dans le template d'une page avec le code Liquid suivant : <span class='code'>{% include '%{slug}' %}</span>."
snippet:
updated_at: Mis à jour le
@ -136,7 +139,7 @@ fr:
edit:
import: importer
new_membership: ajouter compte
help: "Le nom du site est modifiable en cliquant dessus."
help: "Le nom du site est modifiable en cliquant dessus. Pour appliquer votre modification, cliquez après sur le bouton \"Modifier\""
ask_for_name: "Veuillez entrer le nouveau nom"
memberships:
@ -151,7 +154,7 @@ fr:
my_accounts:
edit:
help: "Votre nom est modifiable en cliquant dessus."
help: "Votre nom est modifiable en cliquant dessus. Pour appliquer votre modification, cliquez après sur le bouton \"Modifier\""
new_site: nouveau site
en: en Anglais
de: en Allemand
@ -163,7 +166,7 @@ fr:
theme_assets:
index:
title: Liste des fichiers du thème
help: "Les fichiers du thème sont utilisés pour construire le gabarit de vos pages. Si vous avez besoin d'une galerie d'images, la section Média est plus adéquate."
help: "Les fichiers du thème sont utilisés pour construire le gabarit de vos pages. Si vous avez besoin d'une galerie d'images, créer un modèle sera plus adéquate.<br/><b>Attention : </b> Suivant les droits que vous avez, il se peut que vous ne puissiez pas voir tous les fichiers."
all: tous les fichiers
new: nouveau fichier
snippets: Snippets
@ -171,7 +174,7 @@ fr:
images: Images
medias: Médias
fonts: Polices
no_items: "Il n'existe pas de fichiers. Vous pouvez commencer par créer un <a href='%{url}'>ici</a>."
no_items: "Il n'existe pas de fichiers."
asset:
updated_at: Mis à jour le
new:
@ -180,6 +183,11 @@ fr:
edit:
title: "Edition %{file}"
help: "L'url du fichier est <a href='%{url}'>%{url}</a>"
help: "L'url du fichier est directement disponible : <a href='%{url}'>%{url}</a>"
help_image: "Utilisez votre image dans le template de vos pages ou snippets avec le code Liquid suivant : <span class='code'>{{ '%{path}' | theme_image_tag }}</span>.<br/>. Votre image a comme dimensions : <b>%{width}px x %{height}px</b>.<br/>"
help_javascript: "Utilisez votre javascript dans le template de vos pages avec le code Liquid suivant : <span class='code'>{{ '%{path}' | javascript_tag }}</span>.<br/>"
help_stylesheet: "Utilisez votre stylesheet dans le template de vos pages avec le code Liquid suivant : <span class='code'>{{ '%{path}' | stylesheet_tag }}</span>.<br/>"
form:
choose_file: Choisir fichier
choose_plain_text: Passer en mode texte
@ -187,22 +195,6 @@ fr:
title: Liste des images
no_items: "Il n'y a pas d'images."
asset_collections:
index:
title: Collections
help: "Le nom de la collection est modifiable en cliquant dessus. Vous pouvez personnaliser une collection en ajoutant d'autres champs à vos médias."
new: nouvelle collection
no_items: "Il n'existe pas de collections. Vous pouvez commencer par créer une <a href='%{url}'>ici</a>."
new:
title: Nouvelle collection
help: "Pour le moment, veuillez rentrer le nom. Les autres options viendront dans le formulaire suivant."
edit:
help: "Le nom de la collection est modifiable en cliquant dessus. Vous pouvez personnaliser une collection en ajoutant d'autres champs à vos médias."
add_asset: ajouter média
destroy: supprimer collection
no_items: "Il n'existe pas de médias. Vous pouvez commencer par créer un <a href='%{url}'>ici</a>."
ask_for_name: "Veuillez entrer le nouveau nom"
assets:
new:
title: "Nouveau média"
@ -224,7 +216,7 @@ fr:
new_item: nouvel élément
form:
order_by:
created_at: 'Par date création'
created_at: 'Par date de création'
updated_at: 'Par date de mise à jour'
position_in_list: Manuellement
order_direction:
@ -267,7 +259,6 @@ fr:
site: Informations sur le site
content_types: Modèles de données personnalisés
assets: Fichiers du thème
asset_collections: Collections de média
snippets: Snippets
pages: Pages
messages:

View File

@ -60,8 +60,10 @@ it:
title: Modifica opzioni
help: Amministra la lista opzioni del select box.
collection_label: Lista delle opzioni
custom_form:
types:
category:
edit_categories: Modifica opzioni
file:
delete_file: Elimina file
index:
is_required: è richiesto
@ -188,22 +190,6 @@ it:
title: Lista immagini
no_items: "Per ora non ci sono file."
asset_collections:
index:
title: Collezioni di risorse
help: "Puoi cambiare il nome della collezione cliccandoci sopra. Puoi personalizzare le risorse in una collezione aggiugendo nuovi campi."
new: nuov collezione
no_items: "Per ora non ci sono collezioni. Clicca <a href=\"%{url}\">qui</a> per creare la prima."
new:
title: Nuova collezione
help: "Per ora inserisci solo in nome. Altre opzioni appariranno dopo aver inviato il modulo."
edit:
help: "Puoi cambiare il nome della collezione cliccandoci sopra. Puoi personalizzare le risorse in una collezione aggiugendo nuovi campi."
add_asset: aggiungi risorsa
destroy: elimina collezione
no_items: "Per ora non ci sono risorse. Clicca <a href=\"%{url}\">qui</a> per creare la prima."
ask_for_name: "Prego, digita il nuovo nome"
assets:
new:
title: Nuova risorsa
@ -268,7 +254,6 @@ it:
site: Informazioni sito
content_types: Tipi di contenuti personalizzati
assets: File del tema
asset_collections: Collezioni di risorse
snippets: Frammenti
pages: Pagine
messages:

View File

@ -60,8 +60,10 @@ pt-BR:
title: Editar opções
help: Gerenciar a lista de opções da sua caixa de seleçõa.
collection_label: Lista de opções
custom_form:
types:
category:
edit_categories: Editar opções
file:
delete_file: Excluir arquivo
sessions:
@ -185,22 +187,6 @@ pt-BR:
title: Listando imagens
no_items: "Não existem imagens ainda."
asset_collections:
index:
title: Coleções de arquivos
help: "O nome da coleção pode ser alterado clicando nele. Você pode customizar os arquivos nas coleções adicionando campos."
new: nova coleção
no_items: "Não existem coleções ainda. Clique <a href=\"%{url}\">aqui</a> para criar a primeira."
new:
title: Nova Coleção
help: "Por enquanto, apenas insira o nome. Outras opções irão aparecer após o formulário ser enviado."
edit:
help: "O nome da coleção pode ser alterado clicando nele. Você pode customizar os arquivos nas coleções adicionando campos."
add_asset: adicionar arquivo
destroy: excluir coleção
no_items: "Não existem coleções ainda. Clique <a href=\"%{url}\">aqui</a> para criar a primeira."
ask_for_name: Por favor preencha o novo nome"
assets:
new:
title: Novo arquivo
@ -262,7 +248,6 @@ pt-BR:
site: Informações do site
content_types: Tipos de Conteúdo customizados
assets: Arquivos do Tema
asset_collections: Coleções de Arquivos
snippets: Blocos
pages: Páginas
messages:

View File

@ -72,11 +72,7 @@ de:
highlighted_field_name: Hervorgehobener Feld-Name
group_by_field_name: Gruppierung nach Feld-Name
api_enabled: API aktiviert
asset_collection:
name: Name
slug: Slug
asset:
name: Name
source: Quelle
account:
email: Email

View File

@ -63,6 +63,7 @@ fr:
redirect: Redirection
redirect_url: Url de redirection
cache_strategy: Cache
seo_title: Titre SEO
content_type:
name: Nom
description: Description
@ -71,11 +72,7 @@ fr:
highlighted_field_name: Champ mis en avant
group_by_field_name: Champ pour grouper
api_enabled: Activation API
asset_collection:
name: Nom
slug: Raccourci
asset:
name: Nom
source: Fichier
account:
email: E-mail

View File

@ -77,11 +77,7 @@ it:
highlighted_field_name: Campo evidenziato
group_by_field_name: Raggruppa per nome campo
api_enabled: API Attiva
asset_collection:
name: Nome
slug: Slug
asset:
name: Nome
source: Sorgente
account:
email: Email

View File

@ -71,11 +71,7 @@ pt-BR:
highlighted_field_name: Nome do Campo em destaque
group_by_field_name: Agrupar por name do campo
api_enabled: Activation API
asset_collection:
name: Nome
slug: Slug
asset:
name: Nome
source: Arquivo
account:
email: Email

View File

@ -73,16 +73,6 @@ de:
alert: "Account wurde nicht erstellt."
already_created: "Account wurde bereits zu dieser Webseite hinzugefügt."
asset_collections:
create:
notice: "Sammlung wurde erfolgreich erstellt."
alert: "Sammlung wurde nicht erstellt."
update:
notice: "Sammlung wurde erfolreich aktualisiert."
alert: "Sammlung wurde nicht aktualisiert."
destroy:
notice: "Sammlung wurde erfolgreich gelöscht."
assets:
create:
notice: "Asset wurde erfolgreich erstellt."

View File

@ -73,16 +73,6 @@ en:
alert: "Membership was not created."
already_created: "Account was already added the current site."
asset_collections:
create:
notice: "Collection was successfully created."
alert: "Collection was not created."
update:
notice: "Collection was successfully updated."
alert: "Collection was not updated."
destroy:
notice: "Collection was successfully deleted."
assets:
create:
notice: "Asset was successfully created."

View File

@ -73,16 +73,6 @@ fr:
alert: "Le compte n'a pas été ajouté."
already_created: "Le compte a déjà été ajouté pour ce site."
asset_collections:
create:
notice: "La collection a été créée avec succès."
alert: "La collection n'a pas été créée."
update:
notice: "La collection a été mise à jour avec succès."
alert: "La collection n'a pas été mise à jour."
destroy:
notice: "La collection a été supprimée avec succès."
assets:
create:
notice: "Le média a été crée avec succès."

View File

@ -73,16 +73,6 @@ it:
alert: "La partecipazione non è stata creata."
already_created: "L'account è già stato aggiunto al corrente sito."
asset_collections:
create:
notice: "La collezione è stata creata con successo."
alert: "La collezione non è stata creata."
update:
notice: "La collezione è stata modificata con successo."
alert: "La collezione non è stata modificata."
destroy:
notice: "La collezione è stata eliminata con successo."
assets:
create:
notice: "La risorsa è stata creata con successo."

View File

@ -73,16 +73,6 @@ pt-BR:
alert: "Adesão não foi criada."
already_created: "Conta já foi adicionada ao site atual."
asset_collections:
create:
notice: "Coleção criada com sucesso."
alert: "Coleção não foi criada."
update:
notice: "Coleção foi atualizada com sucesso."
alert: "Coleção não foi atualizada."
destroy:
notice: "Coleção foi apagada com sucesso."
assets:
create:
notice: "Asset foi criada com sucesso."

View File

@ -4,6 +4,7 @@ en:
information: General information
advanced_options: Advanced options
meta: SEO Metadata
seo: SEO settings
code: Code
raw_template: Template
credentials: Credentials
@ -40,10 +41,14 @@ en:
content_type:
raw_item_template: Item template
api_accounts: Notified Accounts
content_instance:
_slug: Permalink
account:
edit:
password: New password
password_confirmation: New password confirmation
page:
seo_title: Title
hints:
page:
@ -52,11 +57,13 @@ en:
templatized: "Use the page as a template for a model you defined."
listed: "Control whether to show the page from generated menus."
content_type_id: "The type of content this page will be a template for."
seo_title: "Define a page title which should be used as the value for the title tag in the head section. Leave it empty if you want to use the default value from the site settings."
meta_keywords: "Overrides the site's meta keywords used within the head tag of the page. They are separated by a comma."
meta_description: "Overrides the site's meta description used within the head tag of the page."
snippet:
slug: "You need to know it in order to insert the snippet inside a page"
site:
seo_title: "Define a global value here which should be used as the value for the title tag in the head section."
meta_keywords: "Meta keywords used within the head tag of the page. They are separated by a comma. Required for SEO."
meta_description: "Meta description used within the head tag of the page. Required for SEO."
domain_name: "ex: locomotiveapp.org"
@ -74,6 +81,11 @@ en:
field:
_alias: "Property available in liquid templates"
hint: "Text displayed in the model form just below the field"
content_instance:
_slug: "Property used to generate the url of a page working as a template for this content type (ex: \"template_page/{{ your_object._permalink }})\"."
seo_title: "The value you fill in will replace the SEO title of the templatized page related to your model."
meta_keywords: "Overrides the site's meta keywords used within the head tag of the page. They are separated by a comma."
meta_description: "Overrides the site's meta description used within the head tag of the page."
import:
source: "A zipfile containing a database.yml along with assets and templates"
samples: "If enabled, the import process will also copy contents and assets"

View File

@ -4,6 +4,7 @@ fr:
information: Informations générales
advanced_options: Options avancées
meta: SEO Metadata
seo: Paramètres SEO
code: Code
raw_template: Gabarit
credentials: Informations de connexion
@ -42,10 +43,14 @@ fr:
content_type:
raw_item_template: Template d'affichage
api_accounts: Comptes à notifier
content_instance:
_slug: Permalink
account:
edit:
password: Nouveau mot de passe
password_confirmation: Confirmation nouveau mot de passe
page:
seo_title: Titre
hints:
page:
@ -54,6 +59,7 @@ fr:
templatized: "Utilise la page comme un template pour un modèle défini."
listed: "Controle si la page doit être visible depuis les menus automatiquement générés."
content_type_id: "Le type du contenu pour lequel cette page est un template."
seo_title: "Définit un titre de page à mettre dans la balise TITLE de la page. Laissez le blanc pour utiliser la valeur par défaut (voir configuration du site)."
meta_keywords: "Redéfinit les mots-clés du site. Utilisés à l'intérieur de la balise HEAD. Ils sont séparés par une virgule."
meta_description: "Redéfinit la description du site. Utilisée à l'intérieur de la balise HEAD."
snippet:
@ -76,6 +82,11 @@ fr:
field:
_alias: "Champ utilisable dans les templates liquid"
hint: "Texte affiché dans le formulaire de l'élément juste en dessous du champ."
content_instance:
_slug: "Propriété utilisée pour générer l'url d'une page faisant office de template pour ce modèle (ex: \"template_de_la_page/{{ votre_object._permalink }})\"."
seo_title: "La valeur que vous rentrez sera utilisée comme titre SEO pour la page faisant office de template pour ce modèle."
meta_keywords: "Redéfinit les mots-clés du site. Utilisés à l'intérieur de la balise HEAD. Ils sont séparés par une virgule."
meta_description: "Redéfinit la description du site. Utilisée à l'intérieur de la balise HEAD."
import:
source: "Un fichier zip contenant database.yml, les fichiers du thème et les templates de page"
samples: "Si activé, les contenus et les média seront aussi copiés lors de l'import"

View File

@ -33,11 +33,9 @@ Rails.application.routes.draw do
get :all, :action => 'index', :on => :collection, :defaults => { :all => true }
end
resources :asset_collections
resources :assets
resources :assets, :path => 'asset_collections/:collection_id/assets'
resources :images
resources :images, :controller => 'assets', :defaults => { :image => true }
resources :content_types

View File

@ -1,8 +1,59 @@
BOARD:
x bushido version
x default template
~ editable_elements: inheritable: false (Mattias) => seems to be fixed by Dirk's last pull request (#44) => content tag
- bushido version
- default template
x locomedia tinyMCE plugin (Bernd)
x remove asset_collections
x site templates
x tinyMCE plugin
x vignette.rb
x code
x helpers
x ui
x rake task
x internal collection
x assign same _id
x pick up a theme_asset
x pull request locomedia
x refactor slugify method (use parameterize + create a module)
x contents permalink (UI)
x BUG: has_one / has_many. Delete an author
x bushido changes in the master
? edit sidebar (inline editor). Unable to reset it
x SEO: support and support/ should be 2 different pages. Remove trailing slash
x issue #91: httparty
x issue #90: seo metadata
x issue #57: seo page title
x issue #56
x tweak ui: form, quick link to edit a model in the popup menu
x Has_one => group by in the select
x better hints:
x notify the user that after changing the page title, they still have to click "update" for the change to be saved
x created_by ASC => "Creation date ascending"
x cancan: (authors / designers / admin)
x model
x ui
x controllers / views:
x page
x content / content type
x asset
x site
x account
x snippet
x theme asset
x features / specs
x enable rack-cache only for a specific url
- convert existing templates (the 2 of the themes section)
=> MERGE
- bugs
- heroku: unable to upload a new file
- import
- delete an item
- liquid tag: Date.today (now), add a test to compare 2 dates
- better ui: increase text field length + refactor error message
BACKLOG:
@ -19,11 +70,16 @@ BACKLOG:
- icon for redirection page in the pages section (back-office)
- write my first tutorial about locomotive
- upgrade warning if new version of locomotive (maybe based on the commit id)
- deploying workflow:
- roll back a bad update
- conflicts with content types
- dev -> staging -> production
- sync data
- import only theme assets
REFACTORING:
- refactor slugify method (use parameterize + create a module)
- move content_type and content_instances in the CustomFields plugin (much more appropriate)
BUGS:

View File

@ -0,0 +1,32 @@
Feature: Editing a content type
In order to edit a content type
As an admin, designer, or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And I have a custom project model
And I have a designer and an author
Scenario: As an unauthenticated user
Given I am not authenticated
When I go to the "Projects" model edition page
Then I should see "Log in"
Scenario: Accessing edition functionality as an Admin
Given I am an authenticated "admin"
When I go to the "Projects" model edition page
Then I should see "Editing model"
And I should see "Custom fields"
Scenario: Accessing edition functionality as a Designer
Given I am an authenticated "designer"
When I go to the "Projects" model edition page
Then I should see "Editing model"
And I should see "Custom fields"
Scenario: Accessing edition functionality as an Author
Given I am an authenticated "author"
When I go to the "Projects" model edition page
Then I should be on the pages list
And I should see the access denied message

View File

@ -0,0 +1,53 @@
Feature: Site Settings
In order to ensure site settings are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And I have a designer and an author
Scenario: As an unauthenticated user
Given I am not authenticated
When I go to site settings
Then I should see "Log in"
Scenario: Accessing site settings as an Admin
Given I am an authenticated "admin"
When I go to site settings
Then I should see "import"
And I should see "add account"
And I should see "SEO settings"
And I should see "Access points"
And I should not see the role dropdown on myself
And I should see the role dropdown on the "designer"
And I should see the role dropdown on the "author"
And I should not see delete on myself
And I should see delete on the "designer"
And I should see delete on the "author"
Scenario: Accessing site settings as a Designer
Given I am an authenticated "designer"
When I go to site settings
Then I should see "import"
And I should see "add account"
And I should see "SEO settings"
And I should see "Access points"
And I should not see the role dropdown on myself
And I should see the role dropdown on the "admin"
And I should see the role dropdown on the "author"
And I should see delete on the "admin"
And I should not see delete on myself
And I should see delete on the "author"
Scenario: Accessing site settings as an Author
Given I am an authenticated "author"
When I go to site settings
Then I should not see "import"
And I should not see "add account"
And I should see "SEO settings"
And I should not see "Access points"
And I should not see "Accounts"
# Paranoid Checks
And I should not see any role dropdowns
And I should not see any delete buttons

View File

@ -0,0 +1,31 @@
Feature: Importing a Site
In order to populate the site with data and assets
As an admin, designer, or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And I have a designer and an author
Scenario: As an unauthenticated user
Given I am not authenticated
When I go to import page
Then I should see "Log in"
Scenario: Accessing importing functionality as an Admin
Given I am an authenticated "admin"
When I go to the import page
Then I should see "Import"
And I should see "Upload"
Scenario: Accessing importing functionality as a Designer
Given I am an authenticated "designer"
When I go to the import page
Then I should see "Import"
And I should see "Upload"
Scenario: Accessing importing functionality as an Author
Given I am an authenticated "author"
When I go to the import page
Then I should be on the pages list
And I should see the access denied message

View File

@ -0,0 +1,95 @@
Feature: Pages
In order to ensure pages are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And I have a custom project model
And I have a designer and an author
And a page named "hello-world" with the template:
"""
Hello World
"""
Scenario: As an unauthenticated user
Given I am not authenticated
When I go to pages
Then I should see "Log in"
# listing pages
Scenario: Accessing pages as an Admin
Given I am an authenticated "admin"
When I go to pages
Then I should see "new page"
And I should see "new model"
And I should see "Projects"
And I should see "edit model"
And I should see delete page buttons
Scenario: Accessing pages as a Designer
Given I am an authenticated "designer"
When I go to pages
Then I should see "new page"
And I should see "new model"
And I should see "Projects"
And I should see "edit model"
And I should see delete page buttons
Scenario: Accessing pages as an Author
Given I am an authenticated "author"
When I go to pages
Then I should not see "new page"
And I should not see "new model"
And I should see "Projects"
And I should not see "edit model"
And I should not see delete page buttons
# new page
Scenario: Accessing new page as an Admin
Given I am an authenticated "admin"
When I go to the new page
Then I should see "New page"
Scenario: Accessing new page as a Designer
Given I am an authenticated "designer"
When I go to the new page
Then I should see "New page"
Scenario: Accessing new page as an Author
Given I am an authenticated "author"
When I go to the new page
Then I should be on the pages list
And I should see the access denied message
# edit page
Scenario: Accessing edit page as an Admin
Given I am an authenticated "admin"
When I go to the "hello-world" edition page
Then I should see "some title"
And I should see "General information"
And I should see "SEO settings"
And I should see "Advanced options"
And I should see "Template"
Scenario: Accessing edit page as a Designer
Given I am an authenticated "designer"
When I go to the "hello-world" edition page
Then I should see "some title"
And I should see "General information"
And I should see "SEO settings"
And I should see "Advanced options"
And I should see "Template"
Scenario: Accessing edit page as an Author
Given I am an authenticated "author"
When I go to the "hello-world" edition page
Then I should see "some title"
And I should not see "General Information"
And I should see "SEO settings"
And I should not see "Advanced options"
And I should not see "Template"

View File

@ -0,0 +1,43 @@
Feature: Theme Assets
In order to ensure theme assets are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And I have a designer and an author
Scenario: As an unauthenticated user
Given I am not authenticated
When I go to theme assets
Then I should see "Log in"
Scenario: Accessing theme assets as an Admin
Given I am an authenticated "admin"
When I go to theme assets
Then I should see "all assets"
And I should see "new snippet"
And I should see "new file"
And I should see "Snippets"
And I should see "Style and javascript"
And I should see "Images"
Scenario: Accessing theme assets as a Designer
Given I am an authenticated "designer"
When I go to theme assets
Then I should see "all assets"
And I should see "new snippet"
And I should see "new file"
And I should see "Snippets"
And I should see "Style and javascript"
And I should see "Images"
Scenario: Accessing theme assets as an Author
Given I am an authenticated "author"
When I go to theme assets
Then I should not see "all assets"
And I should not see "new snippet"
And I should not see "new file"
And I should not see "Snippets"
And I should not see "Style and javascript"
And I should see "Images"

View File

@ -15,7 +15,7 @@ Scenario: Pages list is not accessible for non authenticated accounts
Scenario: Creating a valid page
When I go to pages
And I follow "new page"
And I fill in "Title" with "Test"
And I fill in "page_title" with "Test"
And I fill in "Slug" with "test"
And I select "Home page" from "Parent"
And I fill in "Raw template" with "Lorem ipsum...."
@ -26,7 +26,7 @@ Scenario: Creating a valid page
Scenario: Updating a valid page
When I go to pages
And I follow "Home page"
And I fill in "Title" with "Home page !"
And I fill in "page_title" with "Home page !"
And I fill in "Raw template" with "My new content is here"
And I press "Update"
Then I should see "Page was successfully updated."

View File

@ -30,6 +30,17 @@ Scenario: Uploading a stylesheet
And I should see "Code"
And I should see "stylesheets/main.css"
Scenario: Updating a stylesheet
Given a stylesheet asset named "application"
When I go to theme assets
And I follow "stylesheets/application.css"
And I fill in "theme_asset[plain_text]" with "Lorem ipsum (updated)"
And I press "Update"
Then I should see "File was successfully updated."
And I should see "Editing application.css"
And I should see "application.css"
And I should see "Lorem ipsum (updated)" as theme asset code
Scenario: Uploading a javascript
When I go to theme assets
And I follow "new file"
@ -40,6 +51,14 @@ Scenario: Uploading a javascript
And I should see "Code"
And I should see "javascripts/test/application.js"
Scenario: Updating a javascript
Given a javascript asset named "application"
When I go to theme assets
And I follow "javascripts/application.js"
And I fill in "theme_asset[plain_text]" with "Lorem ipsum (updated)"
And I press "Update"
Then I should see "File was successfully updated."
Scenario: Uploading an image which already exists
When I go to theme assets
And I follow "new file"

View File

@ -4,13 +4,23 @@ Given /^I am not authenticated$/ do
visit('/admin/sign_out')
end
Given /^I am an authenticated user$/ do
Given /^I am an authenticated "([^"]*)"$/ do |role|
@member = Site.first.memberships.where(:role => role.downcase).first || Factory(role.downcase.to_sym, :site => Site.first)
Given %{I go to login}
And %{I fill in "Email" with "admin@locomotiveapp.org"}
And %{I fill in "Email" with "#{@member.account.email}"}
And %{I fill in "Password" with "easyone"}
And %{I press "Log in"}
end
Given /^I am an authenticated user$/ do
Given %{I am an authenticated "admin"}
end
Then /^I should see the access denied message$/ do
Then %{I should see "You are not authorized to access this page"}
end
Then /^I am redirected to "([^\"]*)"$/ do |url|
assert [301, 302].include?(@integration_session.status), "Expected status to be 301 or 302, got #{@integration_session.status}"
location = @integration_session.headers["Location"]
@ -27,3 +37,7 @@ end
### Common
Then /^I debug$/ do
debugger
0
end

View File

@ -0,0 +1,7 @@
Given /^I have a custom project model/ do
site = Site.first
@content_type = Factory.build(:content_type, :site => site, :name => 'Projects')
@content_type.content_custom_fields.build :label => 'Name', :kind => 'string'
@content_type.content_custom_fields.build :label => 'Description', :kind => 'text'
@content_type.save.should be_true
end

View File

@ -0,0 +1,35 @@
Then /^I should see the role dropdown on the "([^"]*)"$/ do |user|
find(:css, "li.membership[data-role=#{user}] select").text.should == 'AdministratorDesignerAuthor'
end
Then /^I should see the role dropdown on myself$/ do
Then %{I should see the role dropdown on the "#{@member.role}"}
end
Then /^I should not see the role dropdown on the "([^"]*)"$/ do |user|
page.has_css?("li.membership[data-role=#{user}] select").should be_false
end
Then /^I should not see the role dropdown on myself$/ do
Then %{I should not see the role dropdown on the "#{@member.role}"}
end
Then /^I should not see any role dropdowns$/ do
page.has_css?('li.membership select').should be_false
end
Then /^I should see delete on the "([^"]*)"$/ do |role|
page.has_css?("li.membership[data-role=#{role}] .actions a.remove").should be_true
end
Then /^I should not see delete on the "([^"]*)"$/ do |role|
page.has_css?("li.membership[data-role=#{role}] .actions a.remove").should be_false
end
Then /^I should not see delete on myself$/ do
Then %{I should not see delete on the "#{@member.role}"}
end
Then /^I should not see any delete buttons$/ do
page.has_css?('li.membership .actions a.remove').should be_false
end

Some files were not shown because too many files have changed in this diff Show More