icons for the theme_asset form + add tests (both rspec + features) for the roles
This commit is contained in:
parent
be6629d986
commit
2ad01833d8
app
controllers/admin
models
views/admin
doc
features
admin
authorization
theme_assets.featurestep_definitions
admin_steps.rbcontent_types_steps.rbcurrent_site_steps.rbpage_steps.rbsite_steps.rbtheme_asset_steps.rb
support
lib/locomotive/carrierwave
public
spec
@ -29,9 +29,7 @@ module Admin
|
||||
respond_to :html
|
||||
|
||||
rescue_from CanCan::AccessDenied do |exception|
|
||||
puts "exception = #{exception.inspect}"
|
||||
|
||||
logger.debug "[CanCan::AccessDenied] #{exception.inspect}"
|
||||
::Locomotive::Logger.info "[CanCan::AccessDenied] #{exception.inspect}"
|
||||
|
||||
if request.xhr?
|
||||
render :json => { :error => exception.message }
|
||||
|
@ -48,7 +48,7 @@ module Admin
|
||||
protected
|
||||
|
||||
def authorize_import
|
||||
authorize! 'new', Site
|
||||
authorize! :import, Site
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -44,6 +44,13 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
update! do |success, failure|
|
||||
@theme_asset = ThemeAsset.find(@theme_asset._id)
|
||||
puts "---> @theme_asset = #{@theme_asset.source_filename} / #{@theme_asset.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def sanitize_params
|
||||
|
@ -1,7 +1,7 @@
|
||||
class Ability
|
||||
include CanCan::Ability
|
||||
|
||||
ROLES = %w(admin author designer)
|
||||
ROLES = %w(admin designer author)
|
||||
|
||||
def initialize(account, site)
|
||||
@account, @site = account, site
|
||||
@ -45,12 +45,18 @@ class Ability
|
||||
|
||||
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
|
||||
|
||||
|
@ -26,6 +26,7 @@ class ThemeAsset
|
||||
before_validation :store_plain_text
|
||||
before_validation :sanitize_folder
|
||||
before_validation :build_local_path
|
||||
after_save :test
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :site, :source
|
||||
@ -38,7 +39,7 @@ 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 ##
|
||||
|
||||
@ -74,17 +75,29 @@ 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?
|
||||
|
||||
puts "content_type = #{content_type.inspect}"
|
||||
|
||||
data = self.performing_plain_text? ? self.plain_text : self.source.read
|
||||
|
||||
return if !self.stylesheet_or_javascript? || self.plain_text_name.blank? || data.blank?
|
||||
|
||||
sanitized_source = self.escape_shortcut_urls(data)
|
||||
|
||||
puts "filename = #{self.plain_text_name}.#{self.stylesheet? ? 'css' : 'js'}"
|
||||
|
||||
puts "sanitized_source = #{sanitized_source.inspect}"
|
||||
|
||||
self.source = CarrierWave::SanitizedFile.new({
|
||||
:tempfile => StringIO.new(sanitized_source),
|
||||
:filename => "#{self.plain_text_name}.#{self.stylesheet? ? 'css' : 'js'}"
|
||||
@ -145,4 +158,8 @@ class ThemeAsset
|
||||
self.errors.add(:source, :extname_changed) if !self.new_record? && self.content_type_changed?
|
||||
end
|
||||
|
||||
def test
|
||||
puts "self.safe_source_filename = #{self.safe_source_filename} / #{self.source_filename}"
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -9,37 +9,38 @@
|
||||
= f.input :meta_keywords
|
||||
= f.input :meta_description
|
||||
|
||||
- if manage_subdomain_or_domains?
|
||||
= f.foldable_inputs :name => :access_points, :class => 'editable-list off' do
|
||||
- if can?(:point, Site)
|
||||
- if manage_subdomain_or_domains?
|
||||
= f.foldable_inputs :name => :access_points, :class => 'editable-list off' do
|
||||
|
||||
= f.custom_input :subdomain, :css => 'path' do
|
||||
%em
|
||||
http://
|
||||
= f.text_field :subdomain, :readonly => !manage_subdomain?
|
||||
\.
|
||||
%em
|
||||
= application_domain
|
||||
|
||||
- if manage_domains?
|
||||
- @site.domains_without_subdomain.each_with_index do |name, index|
|
||||
%li{ :class => "item added #{'last' if index == @site.domains.size - 1}"}
|
||||
%em
|
||||
http://
|
||||
= text_field_tag 'site[domains][]', name, :class => 'string label void domain'
|
||||
|
||||
= error_on_domain(@site, name)
|
||||
%span.actions
|
||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
|
||||
|
||||
%li.item.template
|
||||
= f.custom_input :subdomain, :css => 'path' do
|
||||
%em
|
||||
http://
|
||||
= text_field_tag 'label', t('formtastic.hints.site.domain_name'), :class => 'string label void domain'
|
||||
|
||||
%span.actions
|
||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
|
||||
%button{ :class => 'button light add', :type => 'button' }
|
||||
%span!= t('admin.buttons.new_item')
|
||||
= f.text_field :subdomain, :readonly => !manage_subdomain?
|
||||
\.
|
||||
%em
|
||||
= application_domain
|
||||
|
||||
- if manage_domains?
|
||||
- @site.domains_without_subdomain.each_with_index do |name, index|
|
||||
%li{ :class => "item added #{'last' if index == @site.domains.size - 1}"}
|
||||
%em
|
||||
http://
|
||||
= text_field_tag 'site[domains][]', name, :class => 'string label void domain'
|
||||
|
||||
= error_on_domain(@site, name)
|
||||
%span.actions
|
||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
|
||||
|
||||
%li.item.template
|
||||
%em
|
||||
http://
|
||||
= text_field_tag 'label', t('formtastic.hints.site.domain_name'), :class => 'string label void domain'
|
||||
|
||||
%span.actions
|
||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
|
||||
%button{ :class => 'button light add', :type => 'button' }
|
||||
%span!= t('admin.buttons.new_item')
|
||||
|
||||
- if can?(:manage, Membership)
|
||||
|
||||
|
@ -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')
|
||||
|
@ -1,5 +1,7 @@
|
||||
- title t('.title', :file => @theme_asset.source_filename)
|
||||
|
||||
- puts t('.title', :file => @theme_asset.source_filename)
|
||||
|
||||
- content_for :submenu do
|
||||
= render 'admin/shared/menu/settings'
|
||||
|
||||
|
4
doc/TODO
4
doc/TODO
@ -31,7 +31,7 @@ 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"
|
||||
- cancan: (authors / designers / admin)
|
||||
x cancan: (authors / designers / admin)
|
||||
x model
|
||||
x ui
|
||||
x controllers / views:
|
||||
@ -42,7 +42,7 @@ x better hints:
|
||||
x account
|
||||
x snippet
|
||||
x theme asset
|
||||
- spec
|
||||
x features / specs
|
||||
- better ui: increase text field length + refactor error message
|
||||
- enable rack-cache only for a specific url
|
||||
- convert existing templates (the 2 of the themes section)
|
||||
|
32
features/admin/authorization/content_type.feature
Normal file
32
features/admin/authorization/content_type.feature
Normal 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
|
53
features/admin/authorization/current_site.feature
Normal file
53
features/admin/authorization/current_site.feature
Normal 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
|
31
features/admin/authorization/importing.feature
Normal file
31
features/admin/authorization/importing.feature
Normal 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
|
95
features/admin/authorization/pages.feature
Normal file
95
features/admin/authorization/pages.feature
Normal 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"
|
||||
|
43
features/admin/authorization/theme_assets.feature
Normal file
43
features/admin/authorization/theme_assets.feature
Normal 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"
|
@ -7,45 +7,64 @@ Background:
|
||||
Given I have the site: "test site" set up
|
||||
And I am an authenticated user
|
||||
|
||||
Scenario: Theme assets list is not accessible for non authenticated accounts
|
||||
Given I am not authenticated
|
||||
When I go to theme assets
|
||||
Then I should see "Log in"
|
||||
# Scenario: Theme assets list is not accessible for non authenticated accounts
|
||||
# Given I am not authenticated
|
||||
# When I go to theme assets
|
||||
# Then I should see "Log in"
|
||||
#
|
||||
# Scenario: Uploading a valid image
|
||||
# When I go to theme assets
|
||||
# And I follow "new file"
|
||||
# And I attach the file "spec/fixtures/assets/5k.png" to "File"
|
||||
# And I press "Create"
|
||||
# Then I should see "File was successfully created."
|
||||
# And I should not see "Code"
|
||||
# And I should see "images/5k.png"
|
||||
#
|
||||
# Scenario: Uploading a stylesheet
|
||||
# When I go to theme assets
|
||||
# And I follow "new file"
|
||||
# And I attach the file "spec/fixtures/assets/main.css" to "File"
|
||||
# And I press "Create"
|
||||
# Then I should see "File was successfully created."
|
||||
# And I should see "Code"
|
||||
# And I should see "stylesheets/main.css"
|
||||
|
||||
Scenario: Uploading a valid image
|
||||
Scenario: Updating a stylesheet
|
||||
Given a stylesheet asset named "application"
|
||||
When I go to theme assets
|
||||
And I follow "new file"
|
||||
And I attach the file "spec/fixtures/assets/5k.png" to "File"
|
||||
And I press "Create"
|
||||
Then I should see "File was successfully created."
|
||||
And I should not see "Code"
|
||||
And I should see "images/5k.png"
|
||||
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 stylesheet
|
||||
When I go to theme assets
|
||||
And I follow "new file"
|
||||
And I attach the file "spec/fixtures/assets/main.css" to "File"
|
||||
And I press "Create"
|
||||
Then I should see "File was successfully created."
|
||||
And I should see "Code"
|
||||
And I should see "stylesheets/main.css"
|
||||
|
||||
Scenario: Uploading a javascript
|
||||
When I go to theme assets
|
||||
And I follow "new file"
|
||||
And I fill in "Folder" with "javascripts/test"
|
||||
And I attach the file "spec/fixtures/assets/application.js" to "File"
|
||||
And I press "Create"
|
||||
Then I should see "File was successfully created."
|
||||
And I should see "Code"
|
||||
And I should see "javascripts/test/application.js"
|
||||
|
||||
Scenario: Uploading an image which already exists
|
||||
When I go to theme assets
|
||||
And I follow "new file"
|
||||
And I attach the file "spec/fixtures/assets/5k.png" to "File"
|
||||
And I press "Create"
|
||||
And I follow "new file"
|
||||
And I attach the file "spec/fixtures/assets/5k.png" to "File"
|
||||
And I press "Create"
|
||||
Then I should see "File was not created."
|
||||
# Scenario: Uploading a javascript
|
||||
# When I go to theme assets
|
||||
# And I follow "new file"
|
||||
# And I fill in "Folder" with "javascripts/test"
|
||||
# And I attach the file "spec/fixtures/assets/application.js" to "File"
|
||||
# And I press "Create"
|
||||
# Then I should see "File was successfully created."
|
||||
# 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"
|
||||
# And I attach the file "spec/fixtures/assets/5k.png" to "File"
|
||||
# And I press "Create"
|
||||
# And I follow "new file"
|
||||
# And I attach the file "spec/fixtures/assets/5k.png" to "File"
|
||||
# And I press "Create"
|
||||
# Then I should see "File was not created."
|
||||
|
@ -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"]
|
||||
@ -25,5 +35,4 @@ When /^I forget to press the button on the cross-domain notice page$/ do
|
||||
Mongoid::Persistence::Update.new(@admin).send(:update)
|
||||
end
|
||||
|
||||
### Common
|
||||
|
||||
### Common
|
7
features/step_definitions/content_types_steps.rb
Normal file
7
features/step_definitions/content_types_steps.rb
Normal 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
|
35
features/step_definitions/current_site_steps.rb
Normal file
35
features/step_definitions/current_site_steps.rb
Normal 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
|
@ -42,4 +42,12 @@ Then /^the rendered output should look like:$/ do |body_contents|
|
||||
page.body.should == body_contents
|
||||
end
|
||||
|
||||
Then /^I should see delete page buttons$/ do
|
||||
page.has_css?("ul#pages-list li .more a.remove").should be_true
|
||||
end
|
||||
|
||||
Then /^I should not see delete page buttons$/ do
|
||||
page.has_css?("ul#pages-list li .more a.remove").should be_false
|
||||
end
|
||||
|
||||
|
||||
|
@ -12,3 +12,7 @@ Given /^I have the site: "([^"]*)" set up(?: with #{capture_fields})?$/ do |site
|
||||
@admin.should_not be_nil
|
||||
end
|
||||
|
||||
Given /^I have a designer and an author$/ do
|
||||
Factory(:designer, :site => Site.first)
|
||||
Factory(:author, :site => Site.first)
|
||||
end
|
31
features/step_definitions/theme_asset_steps.rb
Normal file
31
features/step_definitions/theme_asset_steps.rb
Normal file
@ -0,0 +1,31 @@
|
||||
### Theme assets
|
||||
|
||||
# helps create a theme asset
|
||||
def create_plain_text_asset(name, type)
|
||||
asset = Factory.build(:theme_asset, {
|
||||
:site => @site,
|
||||
:plain_text_name => name,
|
||||
:plain_text => 'Lorem ipsum',
|
||||
:plain_text_type => type,
|
||||
:performing_plain_text => true
|
||||
})
|
||||
# asset.should be_valid
|
||||
asset.save!
|
||||
|
||||
end
|
||||
|
||||
# creates various theme assets
|
||||
|
||||
Given /^a javascript asset named "([^"]*)"$/ do |name|
|
||||
@asset = create_plain_text_asset(name, 'javascript')
|
||||
end
|
||||
|
||||
Given /^a stylesheet asset named "([^"]*)"$/ do |name|
|
||||
@asset = create_plain_text_asset(name, 'stylesheet')
|
||||
end
|
||||
|
||||
# other stuff
|
||||
|
||||
Then /^I should see "([^"]*)" as theme asset code$/ do |code|
|
||||
find(:css, "#theme_asset_plain_text").text.should == code
|
||||
end
|
@ -14,10 +14,22 @@ module NavigationHelpers
|
||||
new_admin_session_path
|
||||
when /logout/
|
||||
destroy_admin_session_path
|
||||
when /pages/
|
||||
when /pages( list)?/
|
||||
admin_pages_path
|
||||
when /new page/
|
||||
new_admin_page_path
|
||||
when /"(.*)" edition page/
|
||||
page = Site.first.pages.where(:slug => $1).first
|
||||
edit_admin_page_path(page)
|
||||
when /theme assets/
|
||||
admin_theme_assets_path
|
||||
when /site settings/
|
||||
edit_admin_current_site_path
|
||||
when /import page/
|
||||
new_admin_import_path
|
||||
when /the "(.*)" model edition page/
|
||||
content_type = Site.first.content_types.where(:name => $1).first
|
||||
edit_admin_content_type_path(content_type)
|
||||
|
||||
# Add more mappings here.
|
||||
# Here is an example that pulls values out of the Regexp:
|
||||
|
@ -44,6 +44,8 @@ module Locomotive
|
||||
end
|
||||
end
|
||||
|
||||
puts "content_type => #{value} / #{original_filename.inspect}"
|
||||
|
||||
model.content_type = value
|
||||
end
|
||||
|
||||
|
BIN
public/images/admin/icons/asset_add.png
Normal file
BIN
public/images/admin/icons/asset_add.png
Normal file
Binary file not shown.
After (image error) Size: 639 B |
BIN
public/images/admin/icons/asset_switch.png
Normal file
BIN
public/images/admin/icons/asset_switch.png
Normal file
Binary file not shown.
After (image error) Size: 553 B |
@ -165,6 +165,11 @@ form.formtastic fieldset ol .more { text-align: right; width: auto; margin: 10px
|
||||
form.formtastic fieldset ol .more a { text-decoration: none; color: #787A89; font-size: 0.7em; }
|
||||
form.formtastic fieldset ol .more a:hover { text-decoration: underline; }
|
||||
|
||||
form.formtastic fieldset ol .more a.picture {
|
||||
padding-left: 23px;
|
||||
background: transparent url(/images/admin/icons/asset_add.png) no-repeat left 1px;
|
||||
}
|
||||
|
||||
/* ___ pages ___ */
|
||||
|
||||
form.formtastic fieldset ol li.path em {
|
||||
@ -558,7 +563,7 @@ form.formtastic fieldset.memberships ol li select {
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
/* ___ assets ___ */
|
||||
/* ___ theme assets ___ */
|
||||
|
||||
.selector {
|
||||
position: relative;
|
||||
@ -572,8 +577,12 @@ form.formtastic fieldset.memberships ol li select {
|
||||
font-size: 0.7em;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
padding-left: 20px;
|
||||
background: transparent url(/images/admin/icons/asset_switch.png) no-repeat left 2px;
|
||||
}
|
||||
|
||||
.selector span.alt:hover { text-decoration: underline; }
|
||||
|
||||
|
||||
form.formtastic fieldset.file li.full input {
|
||||
margin-left: 20px;
|
||||
|
@ -9,7 +9,7 @@ Factory.define "test site", :parent => :site do |s|
|
||||
s.name 'Locomotive test website'
|
||||
s.subdomain 'test'
|
||||
s.after_build do |site_test|
|
||||
site_test.memberships.build :account => Account.where(:name => "Admin").first || Factory("admin user"), :admin => true
|
||||
site_test.memberships.build :account => Account.where(:name => "Admin").first || Factory("admin user"), :role => 'admin'
|
||||
end
|
||||
end
|
||||
|
||||
@ -54,8 +54,23 @@ end
|
||||
|
||||
## Memberships ##
|
||||
Factory.define :membership do |m|
|
||||
m.admin true
|
||||
m.account{ Account.where(:name => "Bart Simpson").first || Factory(:account) }
|
||||
m.role 'admin'
|
||||
m.account { Account.where(:name => "Bart Simpson").first || Factory('admin user') }
|
||||
end
|
||||
|
||||
Factory.define :admin, :parent => :membership do |m|
|
||||
m.role 'admin'
|
||||
m.account { Factory('admin user', :locale => 'en') }
|
||||
end
|
||||
|
||||
Factory.define :designer, :parent => :membership do |m|
|
||||
m.role 'designer'
|
||||
m.account { Factory('frenchy user', :locale => 'en') }
|
||||
end
|
||||
|
||||
Factory.define :author, :parent => :membership do |m|
|
||||
m.role 'author'
|
||||
m.account { Factory('brazillian user', :locale => 'en') }
|
||||
end
|
||||
|
||||
|
||||
|
130
spec/models/ability_spec.rb
Normal file
130
spec/models/ability_spec.rb
Normal file
@ -0,0 +1,130 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Ability do
|
||||
|
||||
before :each do
|
||||
@site = Factory(:site)
|
||||
@account = Factory(:account)
|
||||
|
||||
@admin = Factory(:membership, :account => Factory.stub(:account), :site => Factory.stub(:site))
|
||||
@designer = Factory(:membership, :account => Factory.stub(:account), :site => @site, :role => %(designer))
|
||||
@author = Factory(:membership, :account => Factory.stub(:account), :site => @site, :role => %(author))
|
||||
end
|
||||
|
||||
context 'pages' do
|
||||
|
||||
subject { Page.new }
|
||||
|
||||
context 'management' do
|
||||
it 'should allow management of pages from (admin, designer, author)' do
|
||||
should allow_permission_from :manage, @admin
|
||||
should allow_permission_from :manage, @designer
|
||||
should_not allow_permission_from :manage, @author
|
||||
end
|
||||
end
|
||||
|
||||
context 'touching' do
|
||||
it 'should allow touching of pages from (author)' do
|
||||
should allow_permission_from :touch, @author
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'content instance' do
|
||||
|
||||
subject { ContentInstance.new }
|
||||
|
||||
context 'management' do
|
||||
it 'should allow management of pages from (admin, designer, author)' do
|
||||
should allow_permission_from :manage, @admin
|
||||
should allow_permission_from :manage, @designer
|
||||
should allow_permission_from :manage, @author
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'content type' do
|
||||
|
||||
subject { ContentType.new }
|
||||
|
||||
context 'management' do
|
||||
it 'should allow management of pages from (admin, designer)' do
|
||||
should allow_permission_from :manage, @admin
|
||||
should allow_permission_from :manage, @designer
|
||||
should_not allow_permission_from :manage, @author
|
||||
end
|
||||
end
|
||||
|
||||
# context 'touching' do
|
||||
# it 'should allow touching of pages from (author)' do
|
||||
# should_not allow_permission_from :touch, @author
|
||||
# end
|
||||
# end
|
||||
|
||||
end
|
||||
|
||||
context 'theme assets' do
|
||||
|
||||
subject { ThemeAsset.new }
|
||||
|
||||
context 'management' do
|
||||
it 'should allow management of pages from (admin, designer)' do
|
||||
should allow_permission_from :manage, @admin
|
||||
should allow_permission_from :manage, @designer
|
||||
should_not allow_permission_from :manage, @author
|
||||
end
|
||||
end
|
||||
|
||||
context 'touching' do
|
||||
it 'should allow touching of pages from (author)' do
|
||||
should allow_permission_from :touch, @author
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'site' do
|
||||
|
||||
subject { Site.new }
|
||||
|
||||
context 'management' do
|
||||
it 'should allow management of pages from (admin)' do
|
||||
should allow_permission_from :manage, @admin
|
||||
should_not allow_permission_from :manage, @designer
|
||||
should_not allow_permission_from :manage, @author
|
||||
end
|
||||
end
|
||||
|
||||
context 'importing' do
|
||||
it 'should allow importing of sites from (designer)' do
|
||||
should allow_permission_from :import, @designer
|
||||
should_not allow_permission_from :import, @author
|
||||
end
|
||||
end
|
||||
|
||||
context 'pointing' do
|
||||
it 'should allow importing of sites from (designer)' do
|
||||
should allow_permission_from :point, @designer
|
||||
should_not allow_permission_from :point, @author
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'membership' do
|
||||
|
||||
subject { Membership.new }
|
||||
|
||||
context 'management' do
|
||||
it 'should allow management of memberships from (admin, designer)' do
|
||||
should allow_permission_from :manage, @admin
|
||||
should allow_permission_from :manage, @designer
|
||||
should_not allow_permission_from :manage, @author
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
@ -103,14 +103,14 @@ describe ThemeAsset do
|
||||
end
|
||||
|
||||
it 'should handle stylesheet' do
|
||||
@asset.content_type = 'stylesheet'
|
||||
@asset.plain_text_type = 'stylesheet'
|
||||
@asset.valid?.should be_true
|
||||
@asset.stylesheet?.should be_true
|
||||
@asset.source.should_not be_nil
|
||||
end
|
||||
|
||||
it 'should handle javascript' do
|
||||
@asset.content_type = 'javascript'
|
||||
@asset.plain_text_type = 'javascript'
|
||||
@asset.valid?.should be_true
|
||||
@asset.javascript?.should be_true
|
||||
@asset.source.should_not be_nil
|
||||
|
@ -93,6 +93,35 @@ module Locomotive
|
||||
IncludeClassMethod.new(meth)
|
||||
end
|
||||
|
||||
class PermissionsMatcher
|
||||
def initialize(permission, member)
|
||||
@permission = permission
|
||||
@member = member
|
||||
end
|
||||
|
||||
def matches?(target)
|
||||
@target = target
|
||||
@member.ability.can? @permission, @target
|
||||
end
|
||||
|
||||
def failure_message_for_should
|
||||
"expected #{show_object(@member)} to allow '#{@permission}' permission on #{show_object(@target)}"
|
||||
end
|
||||
|
||||
def failure_message_for_should_not
|
||||
"expected #{show_object(@member)} not to allow '#{@permission}' permission on #{show_object(@target)}"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def show_object(object)
|
||||
"#{object.class.name}(#{object.id.inspect})"
|
||||
end
|
||||
end
|
||||
|
||||
def allow_permission_from(permission, member)
|
||||
PermissionsMatcher.new(permission, member)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user