From 0d46d2880c42ad25999bc99f1607cc780bcb16b1 Mon Sep 17 00:00:00 2001 From: Ng Joo Keing Date: Sun, 7 Oct 2018 20:43:04 +0800 Subject: [PATCH 1/2] Role Access --- .../views/roles/edit_view.js.coffee | 17 +++++ .../locomotive/views/roles/new_view.js.coffee | 17 +++++ .../locomotive/accounts_controller.rb | 2 +- .../concerns/membership_controller.rb | 5 ++ .../editable_elements_controller.rb | 10 +++ .../locomotive/memberships_controller.rb | 6 +- .../locomotive/roles_controller.rb | 62 +++++++++++++++++++ app/helpers/locomotive/accounts_helper.rb | 9 +++ app/helpers/locomotive/dashboard_helper.rb | 3 + app/helpers/locomotive/memberships_helper.rb | 15 ++--- app/helpers/locomotive/roles_helper.rb | 22 +++++++ app/helpers/locomotive/shared/pages_helper.rb | 2 +- app/inputs/locomotive/tree_view_input.rb | 40 ++++++++++++ app/models/locomotive/account.rb | 1 + .../locomotive/concerns/membership/role.rb | 27 ++++++++ .../locomotive/concerns/role/role_models.rb | 15 +++++ .../locomotive/concerns/role/role_pages.rb | 19 ++++++ app/models/locomotive/membership.rb | 20 +++--- app/models/locomotive/role.rb | 22 +++++++ app/models/locomotive/site.rb | 3 +- app/policies/locomotive/membership_policy.rb | 7 +-- app/policies/locomotive/role_policy.rb | 37 +++++++++++ app/policies/locomotive/site_policy.rb | 4 ++ app/services/locomotive/membership_service.rb | 12 ++-- app/services/locomotive/page_tree_service.rb | 13 ++-- app/services/locomotive/role_service.rb | 30 +++++++++ app/services/locomotive/site_service.rb | 6 +- app/views/locomotive/accounts/new.html.slim | 1 + .../current_site/_membership.html.slim | 2 +- .../locomotive/current_site/_role.html.slim | 10 +++ .../current_site/form/_panes.html.slim | 2 +- .../current_site/form/_role.html.slim | 4 ++ .../current_site/form/_tabs.html.slim | 4 ++ .../locomotive/memberships/edit.html.slim | 2 +- .../locomotive/memberships/new.html.slim | 1 + .../locomotive/roles/_role_models.html.slim | 11 ++++ .../locomotive/roles/_role_pages.html.slim | 1 + app/views/locomotive/roles/edit.html.slim | 20 ++++++ app/views/locomotive/roles/new.html.slim | 19 ++++++ config/locales/en.yml | 10 +++ config/locales/simple_form.en.yml | 4 ++ config/routes.rb | 5 ++ 42 files changed, 472 insertions(+), 50 deletions(-) create mode 100644 app/assets/javascripts/locomotive/views/roles/edit_view.js.coffee create mode 100644 app/assets/javascripts/locomotive/views/roles/new_view.js.coffee create mode 100644 app/controllers/locomotive/roles_controller.rb create mode 100644 app/helpers/locomotive/roles_helper.rb create mode 100644 app/inputs/locomotive/tree_view_input.rb create mode 100644 app/models/locomotive/concerns/membership/role.rb create mode 100644 app/models/locomotive/concerns/role/role_models.rb create mode 100644 app/models/locomotive/concerns/role/role_pages.rb create mode 100644 app/models/locomotive/role.rb create mode 100644 app/policies/locomotive/role_policy.rb create mode 100644 app/services/locomotive/role_service.rb create mode 100644 app/views/locomotive/current_site/_role.html.slim create mode 100644 app/views/locomotive/current_site/form/_role.html.slim create mode 100644 app/views/locomotive/roles/_role_models.html.slim create mode 100644 app/views/locomotive/roles/_role_pages.html.slim create mode 100644 app/views/locomotive/roles/edit.html.slim create mode 100644 app/views/locomotive/roles/new.html.slim diff --git a/app/assets/javascripts/locomotive/views/roles/edit_view.js.coffee b/app/assets/javascripts/locomotive/views/roles/edit_view.js.coffee new file mode 100644 index 0000000000..7ed118fc45 --- /dev/null +++ b/app/assets/javascripts/locomotive/views/roles/edit_view.js.coffee @@ -0,0 +1,17 @@ +#= require ../shared/form_view + +Locomotive.Views.Roles ||= {} + +class Locomotive.Views.Roles.EditView extends Locomotive.Views.Shared.FormView + + el: '.main' + + initialize: -> + tree = @$('.tree-view-div').tree( + primaryKey: 'id', + uiLibrary: 'bootstrap', + dataSource: JSON.parse(@$('.tree-view-div').attr('data-source')), + checkboxes: true) + + @$('.edit_role').on 'submit', (e) -> + $('.role-pages-input').val(tree.getCheckedNodes()) \ No newline at end of file diff --git a/app/assets/javascripts/locomotive/views/roles/new_view.js.coffee b/app/assets/javascripts/locomotive/views/roles/new_view.js.coffee new file mode 100644 index 0000000000..f7f4b9e036 --- /dev/null +++ b/app/assets/javascripts/locomotive/views/roles/new_view.js.coffee @@ -0,0 +1,17 @@ +#= require ../shared/form_view + +Locomotive.Views.Roles ||= {} + +class Locomotive.Views.Roles.NewView extends Locomotive.Views.Shared.FormView + + el: '.main' + + initialize: -> + tree = @$('.tree-view-div').tree( + primaryKey: 'id', + uiLibrary: 'bootstrap', + dataSource: JSON.parse(@$('.tree-view-div').attr('data-source')), + checkboxes: true) + + @$('.edit_role').on 'submit', (e) -> + $('.role-pages-input').val(tree.getCheckedNodes()) \ No newline at end of file diff --git a/app/controllers/locomotive/accounts_controller.rb b/app/controllers/locomotive/accounts_controller.rb index 101c1422a7..14344370ab 100644 --- a/app/controllers/locomotive/accounts_controller.rb +++ b/app/controllers/locomotive/accounts_controller.rb @@ -12,7 +12,7 @@ def new def create authorize Membership @account = Account.create(account_params) - service.create(@account) if @account.errors.empty? + service.create(@account,params[:account][:role_id]) if @account.errors.empty? respond_with @account, location: edit_current_site_path(current_site) end diff --git a/app/controllers/locomotive/concerns/membership_controller.rb b/app/controllers/locomotive/concerns/membership_controller.rb index 87edd782af..71dbacaccf 100644 --- a/app/controllers/locomotive/concerns/membership_controller.rb +++ b/app/controllers/locomotive/concerns/membership_controller.rb @@ -22,6 +22,11 @@ def current_membership end end + def current_role + return nil if current_membership.nil? + current_membership.role + end + def validate_site_membership return true if current_membership.try(:site).present? diff --git a/app/controllers/locomotive/editable_elements_controller.rb b/app/controllers/locomotive/editable_elements_controller.rb index 2bfac12bac..d20bcb5711 100644 --- a/app/controllers/locomotive/editable_elements_controller.rb +++ b/app/controllers/locomotive/editable_elements_controller.rb @@ -8,6 +8,7 @@ class EditableElementsController < BaseController before_action :load_page after_action :store_location_if_content_entry + before_action :validate_pages_access, only: [:index] layout :editable_elements_layout @@ -72,5 +73,14 @@ def store_location_if_content_entry store_location if @content_entry end + protected + def validate_pages_access + return true if current_role.is_admin? + if current_role.role_pages.exclude? @page.id.to_s + flash[:alert] = "You have no access to #{ @page.title.to_s.titleize }" + redirect_to dashboard_url(current_site) and return false + end + end + end end diff --git a/app/controllers/locomotive/memberships_controller.rb b/app/controllers/locomotive/memberships_controller.rb index a5d5d2eb0c..cc3c059d53 100644 --- a/app/controllers/locomotive/memberships_controller.rb +++ b/app/controllers/locomotive/memberships_controller.rb @@ -13,10 +13,10 @@ def new def create authorize Membership - if @membership = service.create(membership_params[:email]) + if @membership = service.create(membership_params[:email],membership_params[:role_id]) respond_with @membership, location: edit_current_site_path(current_site), flash: true else - redirect_to new_account_path(email: membership_params[:email]) + redirect_to new_account_path(email: membership_params[:email],role_id: membership_params[:role_id]) end end @@ -26,7 +26,7 @@ def edit def update authorize @membership - self.service.change_role(@membership, membership_params[:role]) + self.service.change_role(@membership, membership_params[:role_id]) respond_with @membership, location: edit_current_site_path end diff --git a/app/controllers/locomotive/roles_controller.rb b/app/controllers/locomotive/roles_controller.rb new file mode 100644 index 0000000000..9a5e0e72aa --- /dev/null +++ b/app/controllers/locomotive/roles_controller.rb @@ -0,0 +1,62 @@ +module Locomotive + class RolesController < BaseController + + account_required & within_site + + before_action :load_role, only: [:edit, :update, :destroy] + + def new + authorize Role + @role = current_site.roles.build + respond_with @role + end + + def create + authorize Role + if @role = service.create(role_params) + respond_with @role, location: edit_current_site_path(:anchor => "role"), flash: true + else + redirect_to new_role_path(current_site) + end + end + + def edit + respond_with @role + end + + def update + authorize @role + self.service.update(@role, role_params) + respond_with @role, location: edit_current_site_path(:anchor => "role") + end + + def destroy + authorize @role + @role.destroy + respond_with @role, location: edit_current_site_path(:anchor => "role") + end + + def new_model + if params[:role_model].present? + render partial: 'role_models', locals: { role_model: params[:role_model] } + else + head :unprocessable_entity + end + end + + protected + + def service + @service ||= Locomotive::RoleService.new(current_site, current_locomotive_account) + end + + def load_role + @role = current_site.roles.find(params[:id]) + end + + def role_params + params.require(:role).permit(*policy(@role || Role).permitted_attributes) + end + + end +end \ No newline at end of file diff --git a/app/helpers/locomotive/accounts_helper.rb b/app/helpers/locomotive/accounts_helper.rb index 80dd709e70..cd832c856b 100644 --- a/app/helpers/locomotive/accounts_helper.rb +++ b/app/helpers/locomotive/accounts_helper.rb @@ -5,5 +5,14 @@ def options_for_account current_site.accounts.collect { |a| ["#{a.name} <#{a.email}>", a.id.to_s] } end + def options_for_membership_roles + roles_arr = [].tap do |options| + current_site.roles.each do |role| + options << [role.name.capitalize, role.id.to_s] + end + end + roles_arr.sort! + end + end end diff --git a/app/helpers/locomotive/dashboard_helper.rb b/app/helpers/locomotive/dashboard_helper.rb index a49ff1fd23..a40add43fa 100644 --- a/app/helpers/locomotive/dashboard_helper.rb +++ b/app/helpers/locomotive/dashboard_helper.rb @@ -21,6 +21,7 @@ def activity_to_icon(activity) when 'content_entry' then activity.action == 'created_public' ? 'fa-comment' : 'fa-archive' when 'content_asset' then 'fa-image' when 'membership' then 'fa-user' + when 'role' then 'fa-user' when 'site_metafields' then current_site_metafields_ui[:icon] end end @@ -36,6 +37,8 @@ def render_activity_sentence(activity) when 'content_asset.destroyed' then { name: activity_emphasize(params[:name]) } when 'membership.created' then { name: activity_emphasize(params[:name]) } when 'site_metafields.updated' then { label: current_site_metafields_ui[:label].downcase } + when 'role.created' then { name: activity_emphasize(params[:name]) } + when 'role.updated' then { name: activity_emphasize(params[:name]) } end activity_key_to_sentence(activity.key, options) diff --git a/app/helpers/locomotive/memberships_helper.rb b/app/helpers/locomotive/memberships_helper.rb index 19a28664b6..ee76ea9fcd 100644 --- a/app/helpers/locomotive/memberships_helper.rb +++ b/app/helpers/locomotive/memberships_helper.rb @@ -1,18 +1,13 @@ module Locomotive module MembershipsHelper - def options_for_membership_roles(membership) - current_role = membership.role - - [].tap do |options| - Locomotive::Membership::ROLES.each do |role| - membership.role = role - if policy(membership).change_role? - options << [t("locomotive.memberships.roles.#{role}"), role.to_s] - end + def options_for_membership_roles + roles_arr = [].tap do |options| + current_site.roles.each do |role| + options << [role.name.capitalize, role.id.to_s] end - membership.role = current_role end + roles_arr.sort! end end diff --git a/app/helpers/locomotive/roles_helper.rb b/app/helpers/locomotive/roles_helper.rb new file mode 100644 index 0000000000..afcafde837 --- /dev/null +++ b/app/helpers/locomotive/roles_helper.rb @@ -0,0 +1,22 @@ +module Locomotive + module RolesHelper + + def options_for_role_models + [].tap do |options| + Locomotive::ContentTypeService.new(current_site).list.each do |model| + options << [model.slug.titleize,model.slug] + end + end + end + + def options_for_role_pages(role, pages) + collection = [] + pages.each do |page| + children_page = page.children + collection << { id: page.id.to_s, text: page.title, checked: (role.role_pages.to_a.include? page.id.to_s) , hasChildren: children_page.present? , children: options_for_role_pages(role, children_page) } + end + collection + end + + end +end \ No newline at end of file diff --git a/app/helpers/locomotive/shared/pages_helper.rb b/app/helpers/locomotive/shared/pages_helper.rb index 907f393535..6316aab238 100644 --- a/app/helpers/locomotive/shared/pages_helper.rb +++ b/app/helpers/locomotive/shared/pages_helper.rb @@ -39,7 +39,7 @@ def render_pages end def build_page_tree - @page_tree ||= Locomotive::PageTreeService.new(current_site).build_tree + @page_tree ||= Locomotive::PageTreeService.new(current_site,current_membership).build_tree end class Node < Struct.new(:page, :children, :controller) diff --git a/app/inputs/locomotive/tree_view_input.rb b/app/inputs/locomotive/tree_view_input.rb new file mode 100644 index 0000000000..75ab16a20e --- /dev/null +++ b/app/inputs/locomotive/tree_view_input.rb @@ -0,0 +1,40 @@ +module Locomotive + class TreeViewInput < ::SimpleForm::Inputs::Base + + include Locomotive::SimpleForm::BootstrapHelpers + include Locomotive::SimpleForm::HeaderLink + include Locomotive::SimpleForm::Inputs::FasterTranslate + + def input(wrapper_options) + hidden_input + tree_view_wrapper + end + + def hidden_input + _template = options[:template] + template_path = _template.respond_to?(:has_key?) ? _template[:path].to_s : _template.to_s + template.render(template_path).html_safe + end + + def tree_view_wrapper + row_wrapping do + template.content_tag :div, + tree_view_html, + class: tree_view_wrapper_class + end + end + + def tree_view_wrapper_class + %w(col-md-12).tap do |wrapper_class| + end.join(' ') + end + + def tree_view_html + template.content_tag :div, '', class: 'tree-view-div', 'data-source': options[:source_options].to_json + end + + def link(wrapper_options) + + end + + end +end diff --git a/app/models/locomotive/account.rb b/app/models/locomotive/account.rb index 8c0ca5c39b..5949d3b0cd 100644 --- a/app/models/locomotive/account.rb +++ b/app/models/locomotive/account.rb @@ -26,6 +26,7 @@ class Account field :name field :locale, default: Locomotive.config.default_locale.to_s or 'en' field :super_admin, type: Boolean, default: false + attr_reader :role_id ## validations ## validates_presence_of :name diff --git a/app/models/locomotive/concerns/membership/role.rb b/app/models/locomotive/concerns/membership/role.rb new file mode 100644 index 0000000000..dab5c1cdcb --- /dev/null +++ b/app/models/locomotive/concerns/membership/role.rb @@ -0,0 +1,27 @@ +module Locomotive + module Concerns + module Membership + module Role + + extend ActiveSupport::Concern + + included do + + end + + def to_role + role_name.to_sym + end + + def role_name + self.role.try(:name).to_s + end + + def role_name=(role) + self.role.name = role + end + + end + end + end +end \ No newline at end of file diff --git a/app/models/locomotive/concerns/role/role_models.rb b/app/models/locomotive/concerns/role/role_models.rb new file mode 100644 index 0000000000..499a83ee96 --- /dev/null +++ b/app/models/locomotive/concerns/role/role_models.rb @@ -0,0 +1,15 @@ +module Locomotive + module Concerns + module Role + module RoleModels + + extend ActiveSupport::Concern + + included do + field :role_models, type: ::RawArray, default: [] + end + + end + end + end +end \ No newline at end of file diff --git a/app/models/locomotive/concerns/role/role_pages.rb b/app/models/locomotive/concerns/role/role_pages.rb new file mode 100644 index 0000000000..d954032624 --- /dev/null +++ b/app/models/locomotive/concerns/role/role_pages.rb @@ -0,0 +1,19 @@ +module Locomotive + module Concerns + module Role + module RolePages + + extend ActiveSupport::Concern + + included do + field :role_pages, type: ::RawArray, default: [] + end + + def role_pages_str=(pages_str) + self.role_pages = pages_str.split(',') + end + + end + end + end +end \ No newline at end of file diff --git a/app/models/locomotive/membership.rb b/app/models/locomotive/membership.rb index 95eb02d862..4e24e5d20d 100644 --- a/app/models/locomotive/membership.rb +++ b/app/models/locomotive/membership.rb @@ -2,34 +2,34 @@ module Locomotive class Membership include Locomotive::Mongoid::Document + include Concerns::Membership::Role ROLES = %w(author designer admin) - ## fields ## - field :role, default: 'author' - ## associations ## belongs_to :account, class_name: 'Locomotive::Account', validate: false embedded_in :site, class_name: 'Locomotive::Site', inverse_of: :memberships + belongs_to :role, class_name: 'Locomotive::Role', validate: false ## validations ## validates_presence_of :account validate :account_is_unique - + validates_presence_of :role_id ## virtual attributes ## attr_accessor :email - ## callbacks ## - before_save :define_role - ## methods ## ROLES.each do |_role| define_method("#{_role}?") do - self.role == _role + self.role_name == _role end end + def is_admin? + self.role.try(:name) == 'admin' + end + def to_role self.role.to_sym end @@ -44,9 +44,5 @@ def account_is_unique end end - def define_role - self.role = ROLES.include?(role.downcase) ? role.downcase : ROLES.first - end - end end diff --git a/app/models/locomotive/role.rb b/app/models/locomotive/role.rb new file mode 100644 index 0000000000..eb7960b18d --- /dev/null +++ b/app/models/locomotive/role.rb @@ -0,0 +1,22 @@ +module Locomotive + class Role + + include Locomotive::Mongoid::Document + + ## Extensions ## + include Concerns::Role::RoleModels + include Concerns::Role::RolePages + + belongs_to :site, class_name: 'Locomotive::Site' + + ## behaviours ## + #accepts_nested_attributes_for :role_models,:role_pages, allow_destroy: true + + field :name , type: String, default: '' + + def is_admin? + self.try(:name) == 'admin' + end + + end +end \ No newline at end of file diff --git a/app/models/locomotive/site.rb b/app/models/locomotive/site.rb index b456080d4a..ceb6ff0481 100644 --- a/app/models/locomotive/site.rb +++ b/app/models/locomotive/site.rb @@ -32,6 +32,7 @@ class Site has_many :content_entries, class_name: 'Locomotive::ContentEntry', dependent: :destroy, validate: false, autosave: false has_many :translations, class_name: 'Locomotive::Translation', dependent: :destroy, validate: false, autosave: false has_many :activities, class_name: 'Locomotive::Activity', dependent: :destroy, validate: false, autosave: false + has_many :roles, class_name: 'Locomotive::Role', dependent: :destroy, validate: false, order: :name.asc ## validations ## validates_presence_of :name @@ -41,7 +42,7 @@ class Site before_destroy :destroy_pages ## behaviours ## - accepts_nested_attributes_for :memberships, allow_destroy: true + accepts_nested_attributes_for :memberships, :roles, allow_destroy: true ## methods ## diff --git a/app/policies/locomotive/membership_policy.rb b/app/policies/locomotive/membership_policy.rb index a7a2d117e5..824b481d43 100644 --- a/app/policies/locomotive/membership_policy.rb +++ b/app/policies/locomotive/membership_policy.rb @@ -17,16 +17,13 @@ def destroy? site_admin? && change_role? end - # The role cannot be set higher than the current one (we use the index in - # the roles array to check role presidence) def change_role? - roles = Locomotive::Membership::ROLES - roles.index(resource.role.to_s) <= roles.index(membership.role.to_s) + site_admin? end def permitted_attributes if site_admin? - [:email, :role] + [:email, :role_id] else [] end diff --git a/app/policies/locomotive/role_policy.rb b/app/policies/locomotive/role_policy.rb new file mode 100644 index 0000000000..2b92514214 --- /dev/null +++ b/app/policies/locomotive/role_policy.rb @@ -0,0 +1,37 @@ +module Locomotive + class RolePolicy < ApplicationPolicy + + def index? + site_admin? + end + + def new? + site_admin? + end + + def create? + site_admin? + end + + def edit? + site_admin? and !@resource.is_admin? + end + + def update? + site_admin? + end + + def destroy? + site_admin? + end + + def permitted_attributes + if site_admin? + [ :name, :role_pages_str, :superior_id, role_models: [] ] + else + [] + end + end + + end +end diff --git a/app/policies/locomotive/site_policy.rb b/app/policies/locomotive/site_policy.rb index 0909a1adaf..437c8b8fe3 100644 --- a/app/policies/locomotive/site_policy.rb +++ b/app/policies/locomotive/site_policy.rb @@ -41,6 +41,10 @@ def update_advanced? super_admin? || site_admin? end + def update_role? + super_admin? || site_admin? + end + def show_developers_documentation? super_admin? || site_admin? end diff --git a/app/services/locomotive/membership_service.rb b/app/services/locomotive/membership_service.rb index b176494c21..9107bbd913 100644 --- a/app/services/locomotive/membership_service.rb +++ b/app/services/locomotive/membership_service.rb @@ -12,7 +12,7 @@ class MembershipService < Struct.new(:site, :policy) # # @return [ Object ] A new membership (with errors or not) or nil (no account found) # - def create(email_or_account) + def create(email_or_account,role_id) _account = if email_or_account.respond_to?(:email) email_or_account else @@ -20,7 +20,7 @@ def create(email_or_account) end if _account - site.memberships.create(account: _account, email: _account.email).tap do |success| + site.memberships.create(account: _account, email: _account.email,role_id: role_id).tap do |success| if success track_activity 'membership.created', parameters: { name: _account.name, email: _account.email } end @@ -42,15 +42,15 @@ def create(email_or_account) # # @return [ Boolean] True if everything went well # - def change_role(membership, role) - membership.role = role if role.present? - - if role.present? && policy.change_role? + def change_role(membership, role_id) + if site.roles.where(id:role_id).present? && policy.change_role? + membership.role_id = role_id membership.save else membership.errors.add(:role, :invalid) false end + end def account diff --git a/app/services/locomotive/page_tree_service.rb b/app/services/locomotive/page_tree_service.rb index 488028243f..0704a9dd3c 100644 --- a/app/services/locomotive/page_tree_service.rb +++ b/app/services/locomotive/page_tree_service.rb @@ -1,6 +1,6 @@ module Locomotive - class PageTreeService < Struct.new(:site) + class PageTreeService < Struct.new(:site,:membership) # Returns the tree of pages from the site with the most minimal amount of queries. # This method should only be used for read-only purpose since @@ -10,7 +10,7 @@ class PageTreeService < Struct.new(:site) # @return [ Array ] The first array of pages (index + page not found + pages with depth == 1) # def build_tree - pages, page_not_found = pages_with_minimun_attributes, nil + pages, page_not_found = pages_with_minimun_attributes, site.pages.where(:title.in => ["Page not found"]).first [].tap do |tree| while page = pages.shift @@ -33,10 +33,11 @@ def build_tree #:nodoc: def pages_with_minimun_attributes - site.pages.unscoped. - minimal_attributes. - order_by_depth_and_position. - to_a + if membership.role.is_admin? + site.pages.unscoped.minimal_attributes.order_by_depth_and_position.to_a + else + site.pages.unscoped.in(id:membership.role.role_pages).minimal_attributes.order_by_depth_and_position.to_a + end end #:nodoc: diff --git a/app/services/locomotive/role_service.rb b/app/services/locomotive/role_service.rb new file mode 100644 index 0000000000..5f2718999b --- /dev/null +++ b/app/services/locomotive/role_service.rb @@ -0,0 +1,30 @@ +module Locomotive + class RoleService < Struct.new(:site, :account) + + include Locomotive::Concerns::ActivityService + + def create(attributes, raise_if_not_valid = false) + attributes[:name] = attributes[:name].to_s.downcase + site.roles.new(attributes).tap do |role| + success = raise_if_not_valid ? role.save! : role.save + track_activity 'role.created', parameters: { name: role.name } if success + end + end + + def create!(attributes) + create(attributes, true) + end + + def update(role, attributes, raise_if_not_valid = false) + attributes[:name] = attributes[:name].to_s.downcase + role.attributes = attributes + success = raise_if_not_valid ? role.save! : role.save + track_activity 'role.updated', parameters: { name: role.name } if success + end + + def update!(role, attributes) + update(site, attributes, true) + end + + end +end \ No newline at end of file diff --git a/app/services/locomotive/site_service.rb b/app/services/locomotive/site_service.rb index 491698269d..e245fdbc07 100644 --- a/app/services/locomotive/site_service.rb +++ b/app/services/locomotive/site_service.rb @@ -31,8 +31,10 @@ def create(attributes, raise_if_not_valid = false) Site.new(attributes).tap do |site| site.created_by = account - site.memberships.build account: account, role: 'admin' - + admin_role = site.roles.build name: 'admin' + site.memberships.build account: account, role: admin_role + site.roles.build name: 'author' + site.roles.build name: 'designer' success = raise_if_not_valid ? site.save! : site.save ActiveSupport::Notifications.instrument 'locomotive.site.created', account: account, site: site diff --git a/app/views/locomotive/accounts/new.html.slim b/app/views/locomotive/accounts/new.html.slim index e3596f7fd6..3a7ec8eeb1 100644 --- a/app/views/locomotive/accounts/new.html.slim +++ b/app/views/locomotive/accounts/new.html.slim @@ -12,6 +12,7 @@ - if f.object.respond_to?(:password) = f.input :email + = f.input :role_id, as: :select, collection: options_for_membership_roles, selected: params[:role_id] ,include_blank: false = f.input :password, input_html: { autocomplete: 'off' } = f.input :password_confirmation, input_html: { autocomplete: 'off' } diff --git a/app/views/locomotive/current_site/_membership.html.slim b/app/views/locomotive/current_site/_membership.html.slim index db6d4fab31..8a02d98ba7 100644 --- a/app/views/locomotive/current_site/_membership.html.slim +++ b/app/views/locomotive/current_site/_membership.html.slim @@ -11,7 +11,7 @@ |   small span.label.label-default - = t(membership.role, scope: 'locomotive.memberships.roles') + = membership.role.name.capitalize br small = membership.account.email diff --git a/app/views/locomotive/current_site/_role.html.slim b/app/views/locomotive/current_site/_role.html.slim new file mode 100644 index 0000000000..8d7de6027d --- /dev/null +++ b/app/views/locomotive/current_site/_role.html.slim @@ -0,0 +1,10 @@ +.item.row + .inner-row.col-md-12 + .text + strong= role.name.to_s.titleize + .buttons + - if policy(role).edit? + = link_to t(:edit, scope: 'locomotive.inputs.array'), edit_role_path(current_site, role), class: 'choose btn btn-primary btn-sm' + |   + = link_to role_path(current_site, role), class: 'delete', data: { confirm: t('locomotive.messages.confirm') }, method: :delete do + i.far.fa-trash-alt \ No newline at end of file diff --git a/app/views/locomotive/current_site/form/_panes.html.slim b/app/views/locomotive/current_site/form/_panes.html.slim index c7cd525f3a..d9dc421ad4 100644 --- a/app/views/locomotive/current_site/form/_panes.html.slim +++ b/app/views/locomotive/current_site/form/_panes.html.slim @@ -1,4 +1,4 @@ .tab-content - - %w(main access_points seo url_redirections advanced).each_with_index do |name, index| + - %w(main access_points seo url_redirections advanced role).each_with_index do |name, index| = form_tab_pane name.to_sym, index == 0 do = render "locomotive/current_site/form/#{name}", f: f diff --git a/app/views/locomotive/current_site/form/_role.html.slim b/app/views/locomotive/current_site/form/_role.html.slim new file mode 100644 index 0000000000..8492be5993 --- /dev/null +++ b/app/views/locomotive/current_site/form/_role.html.slim @@ -0,0 +1,4 @@ += f.inputs :information do + = f.input :roles, as: :array, template: :role, new_item: policy(Locomotive::Role).create? ? { label: t('.new_role'), url: new_role_path(current_site) } : nil, wrapper_html: { class: 'roles' } += f.actions do + = f.action diff --git a/app/views/locomotive/current_site/form/_tabs.html.slim b/app/views/locomotive/current_site/form/_tabs.html.slim index dde0bc576c..6946560feb 100644 --- a/app/views/locomotive/current_site/form/_tabs.html.slim +++ b/app/views/locomotive/current_site/form/_tabs.html.slim @@ -15,3 +15,7 @@ ul.nav.nav-tabs role='tablist' - if policy(@site).update_advanced? = form_nav_tab :advanced do = link_to t('simple_form.titles.locomotive.site.advanced'), '#advanced', role: 'tab', data: { toggle: 'tab' } + + - if policy(@site).update_role? + = form_nav_tab :role do + = link_to t('simple_form.titles.locomotive.site.role'), '#role', role: 'tab', data: { toggle: 'tab' } \ No newline at end of file diff --git a/app/views/locomotive/memberships/edit.html.slim b/app/views/locomotive/memberships/edit.html.slim index 2a58ac88ad..a903159e02 100644 --- a/app/views/locomotive/memberships/edit.html.slim +++ b/app/views/locomotive/memberships/edit.html.slim @@ -5,7 +5,7 @@ = locomotive_form_for @membership, url: membership_path(current_site, @membership) do |f| = f.inputs :membership_email, class: 'inputs email' do - = f.input :role, as: :select, collection: options_for_membership_roles(@membership), include_blank: false + = f.association :role, collection: options_for_membership_roles, include_blank: false = f.actions do .row diff --git a/app/views/locomotive/memberships/new.html.slim b/app/views/locomotive/memberships/new.html.slim index 00d38cb647..d45b6df998 100644 --- a/app/views/locomotive/memberships/new.html.slim +++ b/app/views/locomotive/memberships/new.html.slim @@ -6,6 +6,7 @@ = f.inputs :membership_email, class: 'inputs email' do = f.input :email, input_html: { class: 'input-lg' } + = f.association :role, collection: options_for_membership_roles, include_blank: false = f.actions do .row diff --git a/app/views/locomotive/roles/_role_models.html.slim b/app/views/locomotive/roles/_role_models.html.slim new file mode 100644 index 0000000000..f2a5c51664 --- /dev/null +++ b/app/views/locomotive/roles/_role_models.html.slim @@ -0,0 +1,11 @@ +.item.row data-id=role_model + + = hidden_field_tag 'role[role_models][]', role_model + + .inner-row.col-md-12 + + .text + = role_model.titleize + .buttons + = link_to '#', class: 'delete' do + i.fa.fa-trash-o \ No newline at end of file diff --git a/app/views/locomotive/roles/_role_pages.html.slim b/app/views/locomotive/roles/_role_pages.html.slim new file mode 100644 index 0000000000..256e2506fa --- /dev/null +++ b/app/views/locomotive/roles/_role_pages.html.slim @@ -0,0 +1 @@ += hidden_field_tag 'role[role_pages_str]', '', class: 'role-pages-input' \ No newline at end of file diff --git a/app/views/locomotive/roles/edit.html.slim b/app/views/locomotive/roles/edit.html.slim new file mode 100644 index 0000000000..f12a8ba58c --- /dev/null +++ b/app/views/locomotive/roles/edit.html.slim @@ -0,0 +1,20 @@ +link rel="stylesheet" media="screen, projection" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" +link rel="stylesheet" media="screen, projection" href="https://cdn.jsdelivr.net/npm/gijgo@1.9.10/css/gijgo.min.css" +script src='https://cdnjs.cloudflare.com/ajax/libs/gijgo/1.9.6/combined/js/gijgo.min.js' + +- title t('.title', name: @role.name.titleize) + += help t('.help', default: '') + += locomotive_form_for @role, url: role_path(current_site, @role) do |f| + = f.inputs :role, class: 'inputs' do + = f.input :name + = f.input :role_models, as: :array, label: t('.models_access'), template: :role_models, select_options: options_for_role_models, template_url: new_model_roles_path(current_site) + = f.input :role_pages, as: :tree_view, label: t('.pages_access'), source_options: options_for_role_pages(f.object,current_site.pages.where(depth:0).where(:title.nin => ["Page not found"]).order_by(name: 'asc')), template: :role_pages + + = f.actions do + .row + .col-md-6.text-left + = link_to "← #{t('.back')}".html_safe, edit_current_site_path(current_site) + .col-md-6.text-right + = f.action \ No newline at end of file diff --git a/app/views/locomotive/roles/new.html.slim b/app/views/locomotive/roles/new.html.slim new file mode 100644 index 0000000000..34aa1a5dc3 --- /dev/null +++ b/app/views/locomotive/roles/new.html.slim @@ -0,0 +1,19 @@ +link rel="stylesheet" media="screen, projection" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" +link rel="stylesheet" media="screen, projection" href="https://cdn.jsdelivr.net/npm/gijgo@1.9.10/css/gijgo.min.css" +script src='https://cdnjs.cloudflare.com/ajax/libs/gijgo/1.9.6/combined/js/gijgo.min.js' + +- title t('.title') += help t('.help', default: '') + += locomotive_form_for @role, url: roles_path(current_site) do |f| + = f.inputs :role, class: 'inputs' do + = f.input :name + = f.input :role_models, as: :array, label: 'Models Access', template: :role_models, select_options: options_for_role_models, template_url: new_model_roles_path(current_site) + = f.input :role_pages, as: :tree_view, label: 'Pagges Access', source_options: options_for_role_pages(f.object,current_site.pages.where(depth:0).where(:title.nin => ["Page not found"]).order_by(name: 'asc')), template: :role_pages + + = f.actions do + .row + .col-md-6.text-left + = link_to "← #{t('.back')}".html_safe, edit_current_site_path(current_site) + .col-md-6.text-right + = f.action \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index 8ead90890c..eb14d69958 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -265,6 +265,8 @@ en: advanced: destroy: "Destroy this site" confirm_destroy: "Please confirm by typing the site name in the following input" + role: + new_role: "Add role" current_site_metafields: index: @@ -284,6 +286,14 @@ en: title: "Edit membership for %{name}" back: "Back to your site settings" + roles: + new: + title: 'Add a new role' + back: "Back to your site settings" + edit: + title: "Edit role for %{name}" + back: "Back to your site settings" + accounts: new: title: New account diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 1aefb404a8..f5824c7496 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -14,6 +14,7 @@ en: seo: SEO advanced: Advanced url_redirections: URL redirections + role: Role page: content: Content settings: Settings @@ -22,6 +23,9 @@ en: content: Content advanced: Advanced seo: SEO + role: + models_access: Models Access + pages_access: Pages Access labels: locomotive: diff --git a/config/routes.rb b/config/routes.rb index 678890896f..e1a540c3fe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,6 +52,11 @@ resources :memberships + resources :roles, controller: 'roles' do + collection do + get :new_model + end + end resources :translations resources :search_for_resources, only: [:index] From 4abf61a5e0f863d25a9f5928db0a0ff2fc5aa274 Mon Sep 17 00:00:00 2001 From: Ng Joo Keing Date: Tue, 9 Oct 2018 17:03:19 +0800 Subject: [PATCH 2/2] Role Test Case --- app/services/locomotive/role_service.rb | 4 ++-- config/locales/en.yml | 3 +++ spec/controllers/locomotive/roles_controller_spec.rb | 5 +++++ spec/factories/role.rb | 10 ++++++++++ spec/factories/site.rb | 1 + spec/models/locomotive/role_spec.rb | 12 ++++++++++++ spec/services/locomotive/site_service_spec.rb | 3 ++- 7 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 spec/controllers/locomotive/roles_controller_spec.rb create mode 100644 spec/factories/role.rb create mode 100644 spec/models/locomotive/role_spec.rb diff --git a/app/services/locomotive/role_service.rb b/app/services/locomotive/role_service.rb index 5f2718999b..30a43f3ba2 100644 --- a/app/services/locomotive/role_service.rb +++ b/app/services/locomotive/role_service.rb @@ -7,7 +7,7 @@ def create(attributes, raise_if_not_valid = false) attributes[:name] = attributes[:name].to_s.downcase site.roles.new(attributes).tap do |role| success = raise_if_not_valid ? role.save! : role.save - track_activity 'role.created', parameters: { name: role.name } if success + track_activity 'role.created', parameters: { name: role.name.capitalize } if success end end @@ -19,7 +19,7 @@ def update(role, attributes, raise_if_not_valid = false) attributes[:name] = attributes[:name].to_s.downcase role.attributes = attributes success = raise_if_not_valid ? role.save! : role.save - track_activity 'role.updated', parameters: { name: role.name } if success + track_activity 'role.updated', parameters: { name: role.name.capitalize } if success end def update!(role, attributes) diff --git a/config/locales/en.yml b/config/locales/en.yml index eb14d69958..d21a44fdae 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -117,6 +117,9 @@ en: created: 'added %{name} to the site' site_metafields: updated: 'updated %{label}' + role: + created: 'created the %{name} role' + updated: 'updated the %{name} role' pagination: first: "«" diff --git a/spec/controllers/locomotive/roles_controller_spec.rb b/spec/controllers/locomotive/roles_controller_spec.rb new file mode 100644 index 0000000000..c75120a6ae --- /dev/null +++ b/spec/controllers/locomotive/roles_controller_spec.rb @@ -0,0 +1,5 @@ +describe Locomotive::RolesController do + + routes { Locomotive::Engine.routes } + +end \ No newline at end of file diff --git a/spec/factories/role.rb b/spec/factories/role.rb new file mode 100644 index 0000000000..faf3103196 --- /dev/null +++ b/spec/factories/role.rb @@ -0,0 +1,10 @@ +# encoding: utf-8 + +FactoryBot.define do + + ## Memberships ## + factory :role, class: Locomotive::Role do + site { build(:site) } + end + +end diff --git a/spec/factories/site.rb b/spec/factories/site.rb index c8feb2e68c..39c5eea6a0 100644 --- a/spec/factories/site.rb +++ b/spec/factories/site.rb @@ -15,6 +15,7 @@ after(:build) do |site_test| site_test.memberships.build account: Locomotive::Account.where(name: 'Admin').first || create('admin user'), role: 'admin' + end factory 'another site' do diff --git a/spec/models/locomotive/role_spec.rb b/spec/models/locomotive/role_spec.rb new file mode 100644 index 0000000000..687f2d1e80 --- /dev/null +++ b/spec/models/locomotive/role_spec.rb @@ -0,0 +1,12 @@ +# encoding: utf-8 + +describe Locomotive::Role do + + describe 'validation' do + it 'store pages in array' do + role = create(:role, role_pages_str: '1,2,3,4') + expect(role.role_pages).to eq(['1','2','3','4']) + end + end + +end \ No newline at end of file diff --git a/spec/services/locomotive/site_service_spec.rb b/spec/services/locomotive/site_service_spec.rb index f65dd1cda1..0751c039a7 100644 --- a/spec/services/locomotive/site_service_spec.rb +++ b/spec/services/locomotive/site_service_spec.rb @@ -28,7 +28,8 @@ it { expect { subject }.to change { Locomotive::Site.count }.by(1) } it { expect(subject.handle).not_to eq nil } it { expect(subject.created_by).not_to eq nil } - + it { expect(subject.roles.size).to eq(3) } + end describe '#create!' do