diff --git a/Gemfile b/Gemfile
index a132f5be..e5ad6a76 100644
--- a/Gemfile
+++ b/Gemfile
@@ -11,7 +11,7 @@ gem 'devise', '1.3.4'
gem 'devise_bushido_authenticatable', '1.0.0.alpha10', :require => 'devise_cas_authenticatable'
gem 'mongoid', '~> 2.0.2'
-gem 'bson_ext', '~> 1.3.0'
+gem 'bson_ext', '~> 1.4.0'
gem 'locomotive_mongoid_acts_as_tree', '0.1.5.7', :require => 'mongoid_acts_as_tree'
gem 'will_paginate', '~> 3.0.0'
@@ -32,7 +32,7 @@ gem 'fog', '0.8.2'
gem 'mimetype-fu'
gem 'actionmailer-with-request', :require => 'actionmailer_with_request'
gem 'heroku', '1.19.1'
-gem 'httparty', '>= 0.6.1'
+gem 'httparty', '0.7.8'
gem 'RedCloth', '4.2.8'
gem 'delayed_job', '2.1.4'
gem 'delayed_job_mongoid', '1.0.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index 41135040..8a13e225 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -7,7 +7,7 @@ GEM
Platform (0.4.0)
RedCloth (4.2.8)
SystemTimer (1.2.3)
- ZenTest (4.6.1)
+ ZenTest (4.6.2)
abstract (1.0.0)
actionmailer (3.0.10)
actionpack (= 3.0.10)
@@ -42,8 +42,8 @@ GEM
autotest (4.4.6)
ZenTest (>= 4.4.1)
bcrypt-ruby (2.1.4)
- bson (1.3.1)
- bson_ext (1.3.1)
+ bson (1.4.0)
+ bson_ext (1.4.0)
builder (2.1.2)
bushido (0.0.35)
highline (>= 1.6.1)
@@ -53,7 +53,7 @@ GEM
bushido_stub (0.0.3)
activesupport (>= 3.0.7)
cancan (1.6.5)
- capybara (1.0.1)
+ capybara (1.1.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
@@ -62,20 +62,20 @@ GEM
xpath (~> 0.1.4)
carrierwave (0.5.6)
activesupport (~> 3.0)
- cells (3.6.5)
+ cells (3.6.6)
actionpack (~> 3.0)
railties (~> 3.0)
- childprocess (0.2.1)
+ childprocess (0.2.2)
ffi (~> 1.0.6)
columnize (0.3.4)
configuration (1.3.1)
crack (0.1.8)
- cucumber (1.0.2)
+ cucumber (1.0.6)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
- gherkin (~> 2.4.5)
+ gherkin (~> 2.4.18)
json (>= 1.4.6)
- term-ansicolor (>= 1.0.5)
+ term-ansicolor (>= 1.0.6)
cucumber-rails (1.0.2)
capybara (>= 1.0.0)
cucumber (~> 1.0.0)
@@ -99,15 +99,16 @@ GEM
devise
devise (>= 1.0.6)
rubycas-client (>= 2.2.1)
- diff-lcs (1.1.2)
- dragonfly (0.9.5)
+ diff-lcs (1.1.3)
+ dragonfly (0.9.8)
rack
erubis (2.6.6)
abstract (>= 1.0.0)
- excon (0.6.5)
- factory_girl (2.0.4)
- factory_girl_rails (1.1.0)
- factory_girl (~> 2.0.0)
+ excon (0.6.6)
+ factory_girl (2.1.2)
+ activesupport
+ factory_girl_rails (1.2.0)
+ factory_girl (~> 2.1.0)
railties (>= 3.0.0)
ffi (1.0.9)
fog (0.8.2)
@@ -119,12 +120,12 @@ GEM
net-ssh (>= 2.1.3)
nokogiri (>= 1.4.4)
ruby-hmac
- formatador (0.2.0)
+ formatador (0.2.1)
formtastic (1.2.4)
actionpack (>= 2.3.7)
activesupport (>= 2.3.7)
i18n (~> 0.4)
- gherkin (2.4.6)
+ gherkin (2.4.21)
json (>= 1.4.6)
growl-glue (1.0.7)
haml (3.1.2)
@@ -132,7 +133,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)
@@ -142,8 +143,8 @@ GEM
responders (~> 0.6.0)
jammit (0.6.3)
yui-compressor (>= 0.9.3)
- json (1.5.3)
- json_pure (1.5.3)
+ json (1.6.1)
+ json_pure (1.6.1)
kgio (2.6.0)
launchy (0.3.7)
configuration (>= 0.0.5)
@@ -167,23 +168,23 @@ GEM
mimemagic (0.1.8)
mimetype-fu (0.1.2)
mocha (0.9.12)
- mongo (1.3.1)
- bson (>= 1.3.1)
+ mongo (1.4.0)
+ bson (= 1.4.0)
mongoid (2.0.2)
activemodel (~> 3.0)
mongo (~> 1.3)
tzinfo (~> 0.3.22)
- net-ssh (2.1.4)
+ net-ssh (2.2.1)
nokogiri (1.5.0)
open4 (1.1.0)
orm_adapter (0.0.5)
- pickle (0.4.8)
+ pickle (0.4.10)
cucumber (>= 0.8)
rake
polyglot (0.3.2)
proxies (0.2.1)
- rack (1.2.3)
- rack-cache (1.0.2)
+ rack (1.2.4)
+ rack-cache (1.1)
rack (>= 0.4)
rack-mount (0.6.14)
rack (>= 1.0.0)
@@ -207,7 +208,7 @@ GEM
rake (0.9.2)
rdoc (3.9.4)
responders (0.6.4)
- rest-client (1.6.3)
+ rest-client (1.6.7)
mime-types (>= 1.16)
rmagick (2.12.2)
rspec (2.6.0)
@@ -249,9 +250,9 @@ GEM
s3 (0.3.8)
proxies (~> 0.2.0)
sanitize (2.0.3)
- nokogiri (>= 1.4.4, < 1.6)
+ nokogiri (< 1.6, >= 1.4.4)
sass (3.1.2)
- selenium-webdriver (2.4.0)
+ selenium-webdriver (2.7.0)
childprocess (>= 0.2.1)
ffi (>= 1.0.7)
json_pure
@@ -263,13 +264,13 @@ GEM
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.29)
- unicorn (4.0.1)
+ unicorn (4.1.1)
kgio (~> 2.4)
rack
raindrops (~> 0.6)
warden (1.0.5)
rack (>= 1.0)
- will_paginate (3.0.0)
+ will_paginate (3.0.2)
xpath (0.1.4)
nokogiri (~> 1.3)
yui-compressor (0.9.6)
@@ -284,7 +285,7 @@ DEPENDENCIES
ZenTest
actionmailer-with-request
autotest
- bson_ext (~> 1.3.0)
+ bson_ext (~> 1.4.0)
bushido (= 0.0.35)
bushido_stub (= 0.0.3)
cancan
@@ -307,7 +308,7 @@ DEPENDENCIES
haml (= 3.1.2)
heroku (= 1.19.1)
highline
- httparty (>= 0.6.1)
+ httparty (= 0.7.8)
inherited_resources (~> 1.1.2)
launchy
linecache (= 0.43)
diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb
index c7efd941..048234c7 100644
--- a/app/controllers/admin/base_controller.rb
+++ b/app/controllers/admin/base_controller.rb
@@ -15,6 +15,8 @@ module Admin
before_filter :set_locale
+ before_filter :set_current_thread_variables
+
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
@@ -42,6 +44,11 @@ module Admin
protected
+ def set_current_thread_variables
+ Thread.current[:admin] = current_admin
+ Thread.current[:site] = current_site
+ end
+
def current_ability
@current_ability ||= Ability.new(current_admin, current_site)
end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index e4838493..745bb03d 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -65,7 +65,9 @@ class Ability
can :manage, Membership
- cannot :change_role, Membership do |membership|
+ cannot :grant_admin, Membership
+
+ cannot [:update, :destroy], Membership do |membership|
@membership.account_id == membership.account_id || # can not edit myself
membership.admin? # can not modify an administrator
end
@@ -74,7 +76,7 @@ class Ability
def setup_admin_permissions!
can :manage, :all
- cannot :change_role, Membership do |membership|
+ cannot [:update, :destroy], Membership do |membership|
@membership.account_id == membership.account_id # can not edit myself
end
end
diff --git a/app/models/account.rb b/app/models/account.rb
index 5e0b6600..b50e9aa3 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -47,12 +47,12 @@ class Account
def remove_memberships!
self.sites.each do |site|
- site.memberships.delete_if { |m| m.account_id == self._id }
+ membership = site.memberships.where(:account_id => self._id).first
- if site.admin_memberships.empty?
+ if site.admin_memberships.size == 1 && membership.admin?
raise I18n.t('errors.messages.needs_admin_account')
else
- site.save
+ membership.destroy
end
end
end
diff --git a/app/models/content_instance.rb b/app/models/content_instance.rb
index 0322ff86..527c76ba 100644
--- a/app/models/content_instance.rb
+++ b/app/models/content_instance.rb
@@ -50,6 +50,14 @@ class ContentInstance
def visible?
self._visible || self._visible.nil?
end
+
+ def next
+ content_type.contents.where(:_position_in_list => _position_in_list + 1).first()
+ end
+
+ def previous
+ content_type.contents.where(:_position_in_list => _position_in_list - 1).first()
+ end
def errors_to_hash
Hash.new.replace(self.errors)
diff --git a/app/models/membership.rb b/app/models/membership.rb
index 834c0f53..208e7feb 100644
--- a/app/models/membership.rb
+++ b/app/models/membership.rb
@@ -11,6 +11,7 @@ class Membership
## validations ##
validates_presence_of :account
+ validate :can_change_role, :if => :role_changed?
## callbacks ##
before_save :define_role
@@ -55,4 +56,18 @@ class Membership
self.role = Ability::ROLES.include?(role.downcase) ? role.downcase : Ability::ROLES.first
end
+ # Users should not be able to set the role of another user to be higher than
+ # their own. A designer for example should not be able to set another user to
+ # be an administrator
+ def can_change_role
+ current_site = Thread.current[:site]
+ current_membership = current_site.memberships.where(:account_id => Thread.current[:admin].id).first if current_site.present?
+
+ if current_membership.present?
+ # The role cannot be set higher than the current one (we use the index in
+ # the roles array to check role presidence)
+ errors.add(:role, :invalid) if Ability::ROLES.index(role) < Ability::ROLES.index(current_membership.role)
+ end
+ end
+
end
diff --git a/app/views/admin/current_site/_form.html.haml b/app/views/admin/current_site/_form.html.haml
index a864df52..cb0426fb 100644
--- a/app/views/admin/current_site/_form.html.haml
+++ b/app/views/admin/current_site/_form.html.haml
@@ -54,11 +54,14 @@
%em.email= account.email
- - if can?(:change_role, membership)
+ - if can?(:update, membership)
.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
+ - if can?(:grant_admin, membership)
+ = fm.select :role, Ability::ROLES.map { |r| [t("admin.memberships.roles.#{r}"), r] }, :include_blank => false
+ - else
+ = fm.select :role, (Ability::ROLES - ['admin']).map { |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
@@ -73,4 +76,4 @@
= f.custom_input :robots_txt, :css => 'code full', :with_label => false do
= f.label :robots_txt
%code{ :class => 'html' }
- = f.text_area :robots_txt, :class => 'small'
\ No newline at end of file
+ = f.text_area :robots_txt, :class => 'small'
diff --git a/config/locales/default.ru.yml b/config/locales/default.ru.yml
index c8d93dd5..58fc5f8b 100644
--- a/config/locales/default.ru.yml
+++ b/config/locales/default.ru.yml
@@ -29,7 +29,7 @@ ru:
title: "Страница не найдена"
body: "Содержимое страницы 404"
other:
- body: "{% расширяет 'parent' %}"
+ body: "{% extends 'parent' %}"
mongoid:
attributes:
diff --git a/features/admin/authorization/current_site.feature b/features/admin/authorization/current_site.feature
index 591b6f6b..f16312ab 100644
--- a/features/admin/authorization/current_site.feature
+++ b/features/admin/authorization/current_site.feature
@@ -35,7 +35,7 @@ Background:
And I should see "Access points"
And I should not see the role dropdown on myself
And I should not see the role dropdown on the "admin"
- And I should see the role dropdown on the "author"
+ And I should see the role dropdown on the "author" without the "Administrator" option
And I should not see delete on the "admin"
And I should not see delete on myself
And I should see delete on the "author"
diff --git a/features/step_definitions/current_site_steps.rb b/features/step_definitions/current_site_steps.rb
index bfb76706..e2dc0545 100644
--- a/features/step_definitions/current_site_steps.rb
+++ b/features/step_definitions/current_site_steps.rb
@@ -1,5 +1,9 @@
Then /^I should see the role dropdown on the "([^"]*)"$/ do |user|
- find(:css, "li.membership[data-role=#{user}] select").text.should == 'AdministratorDesignerAuthor'
+ find(:css, "li.membership[data-role=#{user}] select").should be_present
+end
+
+Then /^I should see the role dropdown on the "([^"]*)" without the "([^"]*)" option$/ do |user, option|
+ find(:css, "li.membership[data-role=#{user}] select").text.should_not include option
end
Then /^I should see the role dropdown on myself$/ do
@@ -33,3 +37,7 @@ end
Then /^I should not see any delete buttons$/ do
page.has_css?('li.membership .actions a.remove').should be_false
end
+
+When /^I select the "([^"]*)" role for the "author" user/ do |role|
+ Given %{I select "#{role}" from "site[memberships_attributes][2][role]"}
+end
diff --git a/features/step_definitions/site_steps.rb b/features/step_definitions/site_steps.rb
index 748d929c..075a451d 100644
--- a/features/step_definitions/site_steps.rb
+++ b/features/step_definitions/site_steps.rb
@@ -5,6 +5,7 @@
# - I have the site: "some site" set up with name: "Something", domain: "test2"
#
Given /^I have the site: "([^"]*)" set up(?: with #{capture_fields})?$/ do |site_factory, fields|
+ Thread.current[:site] = nil
@site = FactoryGirl.create(site_factory, parse_fields(fields))
@site.should_not be_nil
diff --git a/features/support/selectors.rb b/features/support/selectors.rb
index 32339920..6b5a6bc2 100644
--- a/features/support/selectors.rb
+++ b/features/support/selectors.rb
@@ -22,6 +22,9 @@ module HtmlSelectorsHelpers
when "the has many selector"
".has-many-selector ul li.template"
+
+ when 'the role'
+ '.role'
# Add more mappings here.
# Here is an example that pulls values out of the Regexp:
diff --git a/lib/locomotive/import/content_types.rb b/lib/locomotive/import/content_types.rb
index 4e5feeb3..f2e17000 100644
--- a/lib/locomotive/import/content_types.rb
+++ b/lib/locomotive/import/content_types.rb
@@ -210,7 +210,7 @@ module Locomotive
end
def set_highlighted_field_name(content_type)
- field = content_type.content_custom_fields.detect { |f| f._alias == content_type.highlighted_field_name }
+ field = content_type.content_custom_fields.detect { |f| f._alias == content_type.highlighted_field_name.to_s }
content_type.highlighted_field_name = field._name if field
end
diff --git a/lib/locomotive/liquid/drops/content.rb b/lib/locomotive/liquid/drops/content.rb
index 1aefc759..2e3fcb37 100644
--- a/lib/locomotive/liquid/drops/content.rb
+++ b/lib/locomotive/liquid/drops/content.rb
@@ -8,6 +8,32 @@ module Locomotive
def _id
self._source._id.to_s
end
+
+ # Returns the next content for the parent content type.
+ # If no content is found, nil is returned.
+ #
+ # Usage:
+ #
+ # {% if article.next %}
+ # Read next article
+ # {% endif %}
+ #
+ def next
+ self._source.next.to_liquid
+ end
+
+ # Returns the previous content for the parent content type.
+ # If no content is found, nil is returned.
+ #
+ # Usage:
+ #
+ # {% if article.previous %}
+ # Read previous article
+ # {% endif %}
+ #
+ def previous
+ self._source.previous.to_liquid
+ end
def before_method(meth)
return '' if self._source.nil?
diff --git a/lib/locomotive/liquid/tags/with_scope.rb b/lib/locomotive/liquid/tags/with_scope.rb
index ea23b339..7f79675d 100644
--- a/lib/locomotive/liquid/tags/with_scope.rb
+++ b/lib/locomotive/liquid/tags/with_scope.rb
@@ -23,9 +23,9 @@ module Locomotive
def decode(attributes, context)
attributes.each_pair do |key, value|
attributes[key] = (case value
- when /true|false/ then value == 'true'
- when /[0-9]+/ then value.to_i
- when /'(\S+)'/ then $1
+ when /^true|false$/i then value == 'true'
+ when /^[0-9]+$/ then value.to_i
+ when /^["|'](.+)["|']$/ then $1.gsub(/^["|']/, '').gsub(/["|']$/, '')
else
context[value]
end)
diff --git a/locomotive_cms.gemspec b/locomotive_cms.gemspec
index d4a3559b..11f824a6 100644
--- a/locomotive_cms.gemspec
+++ b/locomotive_cms.gemspec
@@ -17,12 +17,12 @@ Gem::Specification.new do |s|
s.required_rubygems_version = '>= 1.3.6'
s.rubyforge_project = 'nowarning'
- s.add_dependency 'rails', '>= 3.0.10'
+ s.add_dependency 'rails', '~> 3.0.10'
s.add_dependency 'warden'
s.add_dependency 'devise', '1.3.4'
s.add_dependency 'devise_bushido_authenticatable', '1.0.0.alpha10'
- s.add_dependency 'mongoid', '2.0.2'
- s.add_dependency 'bson_ext', '~> 1.3.0'
+ s.add_dependency 'mongoid', '~> 2.0.2'
+ s.add_dependency 'bson_ext', '~> 1.4.0'
s.add_dependency 'locomotive_mongoid_acts_as_tree', '0.1.5.7'
s.add_dependency 'will_paginate', '~> 3.0.0'
@@ -49,7 +49,7 @@ Gem::Specification.new do |s|
s.add_dependency 'fog', '0.8.2'
s.add_dependency 'mimetype-fu'
s.add_dependency 'actionmailer-with-request'
- s.add_dependency 'httparty', '>= 0.6.1'
+ s.add_dependency 'httparty', '0.7.8'
s.add_dependency 'RedCloth', '4.2.8'
s.add_dependency 'delayed_job', '2.1.4'
s.add_dependency 'delayed_job_mongoid', '1.0.2'
diff --git a/spec/fixtures/themes/default.zip b/spec/fixtures/themes/default.zip
index 8489be3d..47258673 100644
Binary files a/spec/fixtures/themes/default.zip and b/spec/fixtures/themes/default.zip differ
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
index 1b43d581..237426ae 100644
--- a/spec/models/ability_spec.rb
+++ b/spec/models/ability_spec.rb
@@ -125,6 +125,15 @@ describe Ability do
end
end
+ context 'granting admin' do
+ it 'should allow only admins to grant admin role' do
+ should allow_permission_from :grant_admin, @admin
+ should_not allow_permission_from :grant_admin, @designer
+ should_not allow_permission_from :grant_admin, @author
+ end
+
+ end
+
end
end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index b57d614a..c8f429a8 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -47,13 +47,14 @@ describe Account do
end
it 'should also delete memberships' do
- Site.any_instance.stubs(:admin_memberships).returns(['junk'])
+ Site.any_instance.stubs(:admin_memberships).returns(['junk', 'dirt'])
+ @site_1.memberships.first.expects(:destroy)
+ @site_2.memberships.first.expects(:destroy)
@account.destroy
- @site_1.memberships.should be_empty
- @site_2.memberships.should be_empty
end
it 'should raise an exception if account is the only remaining admin' do
+ @site_1.memberships.first.stubs(:admin?).returns(true)
@site_1.stubs(:admin_memberships).returns(['junk'])
lambda {
@account.destroy
diff --git a/spec/models/content_instance_spec.rb b/spec/models/content_instance_spec.rb
index f34e2957..9669ab61 100644
--- a/spec/models/content_instance_spec.rb
+++ b/spec/models/content_instance_spec.rb
@@ -42,6 +42,34 @@ describe ContentInstance do
end
+ describe "#navigation" do
+ before(:each) do
+ %w(first second third).each_with_index do |item,index|
+ content = build_content({:title => item.to_s})
+ content._position_in_list = index
+ instance_variable_set "@#{item}", content
+ end
+ end
+
+ it 'should find previous item when available' do
+ @second.previous.custom_field_1.should == "first"
+ @second.previous._position_in_list.should == 0
+ end
+
+ it 'should find next item when available' do
+ @second.next.custom_field_1.should == "third"
+ @second.next._position_in_list.should == 2
+ end
+
+ it 'should return nil when fetching previous item on first in list' do
+ @first.previous.should == nil
+ end
+
+ it 'should return nil when fetching next item on last in list' do
+ @third.next.should == nil
+ end
+ end
+
describe '#permalink' do
before(:each) do