decidim-centers 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -8
  3. data/app/commands/concerns/decidim/centers/publish_center_update_event.rb +1 -0
  4. data/app/commands/decidim/centers/admin/create_role.rb +50 -0
  5. data/app/commands/decidim/centers/admin/destroy_role.rb +47 -0
  6. data/app/commands/decidim/centers/admin/update_role.rb +47 -0
  7. data/app/commands/decidim/centers/create_or_update_role_user.rb +44 -0
  8. data/app/controllers/decidim/centers/admin/roles_controller.rb +106 -0
  9. data/app/forms/concerns/decidim/centers/account_form_override.rb +7 -0
  10. data/app/forms/decidim/centers/admin/role_form.rb +18 -0
  11. data/app/forms/decidim/centers/verifications/center.rb +8 -0
  12. data/app/helpers/decidim/centers/application_helper.rb +6 -0
  13. data/app/jobs/decidim/centers/auto_verification_job.rb +1 -1
  14. data/app/jobs/decidim/centers/sync_role_user_job.rb +29 -0
  15. data/app/models/concerns/decidim/centers/user_override.rb +11 -0
  16. data/app/models/decidim/centers/role.rb +25 -0
  17. data/app/models/decidim/centers/role_user.rb +28 -0
  18. data/app/packs/src/decidim/centers/admin/resource_permissions_select2.js +4 -0
  19. data/app/permissions/decidim/centers/admin/permissions.rb +1 -1
  20. data/app/presenters/decidim/centers/admin_log/role_presenter.rb +29 -0
  21. data/app/views/decidim/centers/_profile_form.html.erb +4 -0
  22. data/app/views/decidim/centers/_registration_form.html.erb +4 -0
  23. data/app/views/decidim/centers/admin/centers/index.html.erb +4 -4
  24. data/app/views/decidim/centers/admin/roles/_form.html.erb +10 -0
  25. data/app/views/decidim/centers/admin/roles/edit.html.erb +8 -0
  26. data/app/views/decidim/centers/admin/roles/index.html.erb +45 -0
  27. data/app/views/decidim/centers/admin/roles/new.html.erb +8 -0
  28. data/config/locales/en.yml +41 -7
  29. data/db/migrate/20241204134913_create_decidim_centers_roles.rb +13 -0
  30. data/db/migrate/20241204135138_create_decidim_centers_role_users.rb +12 -0
  31. data/lib/decidim/centers/admin_engine.rb +7 -0
  32. data/lib/decidim/centers/engine.rb +2 -0
  33. data/lib/decidim/centers/test/factories.rb +15 -0
  34. data/lib/decidim/centers/test/shared_contexts.rb +10 -1
  35. data/lib/decidim/centers/verifications/center_action_authorizer.rb +17 -1
  36. data/lib/decidim/centers/version.rb +1 -1
  37. data/lib/decidim/centers.rb +5 -0
  38. metadata +18 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3d9404d74205212bd3baf112cadc15a1f876f9ed8a00ed882ca60924a330ca0
4
- data.tar.gz: 2ddef7bf7c73e61d52aee22a361569c84e4e8255a88ce5c8ec5341a48d08164a
3
+ metadata.gz: 322ae36dae0cc1f6450d0bdb4dee7cdc1125823329f0dded27ffe1406d98af1c
4
+ data.tar.gz: 94e3c59ce5f089248873859b0ae967bb8f12aebf9afdc082be358158b0d7cccf
5
5
  SHA512:
6
- metadata.gz: 313aed3486d72c4a012151c98873aa891ddf58ddda348d406f20bbd3377863364c032af93f29ccaf645e88a183152053f35d4d089d50318c169aef650e0b6257
7
- data.tar.gz: b861414678d79a6049c075f443cb964d6474d9c024eaea61b258a9f4d268e1f18658e1fd3e0e136ad07723f4528539037d5774ca49a19182ebe6b19e6798a48f
6
+ metadata.gz: ffe9e5780b724a8194f7e041b37a62203b8439c678730ec6fc8a1797d06006077a2fbcae7d060e7aeb4fc5de08345a0e7f386c82884933a8d6d3dad3c45eb84f
7
+ data.tar.gz: b3b96f0f4f7d912a5b6b325ec6ba2e5d116f68cbacf34ed17f9349887914347cf841fb8290d1b19d081f92934eb38bfda40a8138e136e636e29b43db9385cf70
data/README.md CHANGED
@@ -4,15 +4,16 @@
4
4
  [![[CI] Lint](https://github.com/Platoniq/decidim-module-centers/actions/workflows/lint.yml/badge.svg)](https://github.com/Platoniq/decidim-module-centers/actions/workflows/lint.yml)
5
5
  [![[CI] Test](https://github.com/Platoniq/decidim-module-centers/actions/workflows/test.yml/badge.svg)](https://github.com/Platoniq/decidim-module-centers/actions/workflows/test.yml)
6
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/6b1b656b229f9731a64b/maintainability)](https://codeclimate.com/github/Platoniq/decidim-module-centers/maintainability)
7
- [![codecov](https://codecov.io/gh/Platoniq/decidim-module-centers/branch/main/graph/badge.svg)](https://codecov.io/gh/Platoniq/decidim-module-centers)
7
+ [![Coverage Status](https://coveralls.io/repos/github/Platoniq/decidim-module-centers/badge.svg?branch=main)](https://coveralls.io/github/Platoniq/decidim-module-centers?branch=main)
8
8
 
9
- Manage your centers and scopes so the users can be authorized over them. As an admin you will be able
10
- to create centers and scopes (we use the model Decidim currently provides).
9
+ Manage your centers, roles and scopes so the users can be authorized over them. As an admin you will be able
10
+ to create centers, roles and scopes (we use the model Decidim currently provides).
11
11
 
12
12
  When a user signs up in the platform two new fields will appear in the registration form.
13
13
 
14
14
  - **Center**: Where the user works. For example: "University of Granada"
15
15
  - **Scope**: The work scope in which the user works. For example: "Computer Science"
16
+ - **Role**: The role of the user in the center. For example: "Professor"
16
17
 
17
18
  ![Registration form](examples/registration.png)
18
19
 
@@ -22,8 +23,8 @@ When they create the account or update their values, a `center` authorization wi
22
23
  with the value of the center and scope the user has selected.
23
24
 
24
25
  As an admin you will be able to configure the permissions of a component restricting the access to
25
- specific centers and scopes. When you select multiple centers or scopes they work as "or". When you specify
26
- both the center and the scope it will work as an "and" between them.
26
+ specific centers, roles and scopes. When you select multiple centers, roles or scopes they work as "or". When you specify
27
+ the center, the role and the scope it will work as an "and" between them.
27
28
 
28
29
  ![Permissions in the admin page](examples/permissions.png)
29
30
 
@@ -53,14 +54,16 @@ Depending on your Decidim version, choose the corresponding version to ensure co
53
54
  | Version | Compatible decidim versions |
54
55
  |---------|-----------------------------|
55
56
  | 0.1.x | v0.27.x |
57
+ | 0.2.x | v0.27.x |
56
58
 
57
59
  ## Configuration
58
60
 
59
61
  You can customize your installation using the environment variables below:
60
62
 
61
- | ENV | Description | Default | Example |
62
- |--------------------------------------|-----------------------------------------------------------|---------|--------------|
63
- | DECIDIM_CENTERS_SCOPES_ENABLED | Use scopes to categorize users too along with the centers | true | false |
63
+ | ENV | Description | Default | Example |
64
+ |--------------------------------|-----------------------------------------------------------|---------|--------------|
65
+ | DECIDIM_CENTERS_SCOPES_ENABLED | Use scopes to categorize users too along with the centers | true | false |
66
+ | DECIDIM_CENTERS_ROLES_ENABLED | Use roles to categorize users too along with the centers | true | false |
64
67
 
65
68
  > **IMPORTANT**: Remember to activate the verification method `center` in the
66
69
  > Decidim `/system` admin page for your organization.
@@ -12,6 +12,7 @@ module Decidim
12
12
  "decidim.centers.user.updated",
13
13
  user_id: @user.id,
14
14
  center_id: @form.center_id,
15
+ role_id: @form.role_id,
15
16
  scope_id: @form.scope_id
16
17
  )
17
18
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Centers
5
+ module Admin
6
+ # This command is executed when the user creates a Role from the admin
7
+ # panel.
8
+ class CreateRole < Decidim::Command
9
+ # Initializes a CreateRole Command.
10
+ #
11
+ # form - The form from which to get the data.
12
+ # current_user - The user who performs the action.
13
+ def initialize(form, current_user)
14
+ @form = form
15
+ @current_user = current_user
16
+ end
17
+
18
+ # Creates the role if valid.
19
+ #
20
+ # Broadcasts :ok if successful, :invalid otherwise.
21
+ def call
22
+ return broadcast(:invalid) if form.invalid?
23
+
24
+ transaction do
25
+ create_role!
26
+ end
27
+
28
+ broadcast(:ok, @role)
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :form, :current_user
34
+
35
+ def create_role!
36
+ attributes = {
37
+ organization: form.current_organization,
38
+ title: form.title
39
+ }
40
+
41
+ @role = Decidim.traceability.create!(
42
+ Role,
43
+ current_user,
44
+ attributes
45
+ )
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Centers
5
+ module Admin
6
+ # This command is executed when the user destroys a Role from the admin
7
+ # panel.
8
+ class DestroyRole < Decidim::Command
9
+ # Initializes a DestroyRole Command.
10
+ #
11
+ # role - The current instance of the role to be destroyed.
12
+ # current_user - The user who performs the action.
13
+ def initialize(role, current_user)
14
+ @role = role
15
+ @current_user = current_user
16
+ end
17
+
18
+ # Destroys the role if valid.
19
+ #
20
+ # Broadcasts :ok if successful, :invalid otherwise.
21
+ def call
22
+ transaction do
23
+ destroy_role!
24
+ end
25
+
26
+ broadcast(:ok, role)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :role, :current_user
32
+
33
+ def destroy_role!
34
+ attributes = {
35
+ deleted_at: Time.current
36
+ }
37
+
38
+ Decidim.traceability.update!(
39
+ role,
40
+ current_user,
41
+ attributes
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Centers
5
+ module Admin
6
+ # This command is executed when the user changes a Role from the admin
7
+ # panel.
8
+ class UpdateRole < Decidim::Command
9
+ # Initializes a UpdateRole Command.
10
+ #
11
+ # form - The form from which to get the data.
12
+ # role - The current instance of the role to be updated.
13
+ # current_user - The user who performs the action.
14
+ def initialize(form, role, current_user)
15
+ @form = form
16
+ @role = role
17
+ @current_user = current_user
18
+ end
19
+
20
+ # Updates the role if valid.
21
+ #
22
+ # Broadcasts :ok if successful, :invalid otherwise.
23
+ def call
24
+ return broadcast(:invalid) if form.invalid?
25
+
26
+ transaction do
27
+ update_role!
28
+ end
29
+
30
+ broadcast(:ok, role)
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :form, :role, :current_user
36
+
37
+ def update_role!
38
+ Decidim.traceability.update!(
39
+ role,
40
+ current_user,
41
+ title: form.title
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Centers
5
+ # This command is executed when a new relationship between user
6
+ # and role is created
7
+ class CreateOrUpdateRoleUser < Decidim::Command
8
+ # Initializes a CreateOrUpdateRoleUser Command.
9
+ #
10
+ # role - The role to be related with the user.
11
+ # user - The user to be related with the role.
12
+ def initialize(role, user)
13
+ @role = role
14
+ @user = user
15
+ end
16
+
17
+ # Creates or update the role user if valid.
18
+ #
19
+ # Broadcasts :ok if successful, :invalid otherwise.
20
+ def call
21
+ transaction do
22
+ delete_existing_role_user!
23
+ create_role_user!
24
+ rescue ActiveRecord::RecordInvalid
25
+ broadcast(:invalid)
26
+ end
27
+
28
+ broadcast(:ok, @role_user)
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :role, :user
34
+
35
+ def delete_existing_role_user!
36
+ RoleUser.where(user: user).destroy_all
37
+ end
38
+
39
+ def create_role_user!
40
+ @role_user = RoleUser.create!(role: role, user: user)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Centers
5
+ module Admin
6
+ # This controller allows the create or update a role.
7
+ class RolesController < ApplicationController
8
+ include TranslatableAttributes
9
+
10
+ helper_method :roles, :role
11
+
12
+ def index
13
+ enforce_permission_to :index, :role
14
+ respond_to do |format|
15
+ format.html
16
+ format.json do
17
+ render json: json_roles
18
+ end
19
+ end
20
+ end
21
+
22
+ def new
23
+ enforce_permission_to :create, :role
24
+ @form = form(RoleForm).instance
25
+ end
26
+
27
+ def create
28
+ enforce_permission_to :create, :role
29
+ @form = form(RoleForm).from_params(params)
30
+
31
+ CreateRole.call(@form, current_user) do
32
+ on(:ok) do
33
+ flash[:notice] = I18n.t("roles.create.success", scope: "decidim.centers.admin")
34
+ redirect_to roles_path
35
+ end
36
+
37
+ on(:invalid) do
38
+ flash.now[:alert] = I18n.t("roles.create.invalid", scope: "decidim.centers.admin")
39
+ render action: "new"
40
+ end
41
+ end
42
+ end
43
+
44
+ def edit
45
+ enforce_permission_to :update, :role, role: role
46
+ @form = form(RoleForm).from_model(role)
47
+ end
48
+
49
+ def update
50
+ enforce_permission_to :update, :role, role: role
51
+ @form = form(RoleForm).from_params(params)
52
+
53
+ UpdateRole.call(@form, role, current_user) do
54
+ on(:ok) do
55
+ flash[:notice] = I18n.t("roles.update.success", scope: "decidim.centers.admin")
56
+ redirect_to roles_path
57
+ end
58
+
59
+ on(:invalid) do
60
+ flash.now[:alert] = I18n.t("roles.update.invalid", scope: "decidim.centers.admin")
61
+ render action: "edit"
62
+ end
63
+ end
64
+ end
65
+
66
+ def destroy
67
+ enforce_permission_to :destroy, :role, role: role
68
+
69
+ DestroyRole.call(role, current_user) do
70
+ on(:ok) do
71
+ flash[:notice] = I18n.t("roles.destroy.success", scope: "decidim.centers.admin")
72
+ end
73
+ end
74
+
75
+ redirect_to roles_path
76
+ end
77
+
78
+ private
79
+
80
+ def json_roles
81
+ query = filtered_roles
82
+ query = query.where(id: params[:ids]) if params[:ids]
83
+ query = query.where("title->>? ilike ?", I18n.locale, "%#{params[:q]}%") if params[:q]
84
+ query.map do |item|
85
+ {
86
+ id: item.id,
87
+ text: translated_attribute(item.title)
88
+ }
89
+ end
90
+ end
91
+
92
+ def role
93
+ @role ||= filtered_roles.find(params[:id])
94
+ end
95
+
96
+ def roles
97
+ @roles ||= filtered_roles.page(params[:page]).per(15)
98
+ end
99
+
100
+ def filtered_roles
101
+ Role.where(organization: current_organization).not_deleted
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -13,20 +13,27 @@ module Decidim
13
13
  include Decidim::Centers::ApplicationHelper
14
14
 
15
15
  attribute :center_id, Integer
16
+ attribute :role_id, Integer
16
17
  attribute :scope_id, Integer
17
18
 
18
19
  validates :center_id, presence: true
20
+ validates :role_id, presence: true, if: :role_id?
19
21
  validates :scope_id, presence: true, if: :scope_id?
20
22
 
21
23
  def map_model(model)
22
24
  original_map_model(model)
23
25
 
24
26
  self.center_id = model.center.try(:id)
27
+ self.role_id = model.center_role.try(:id)
25
28
  self.scope_id = model.scope.try(:id)
26
29
  end
27
30
 
28
31
  private
29
32
 
33
+ def role_id?
34
+ Decidim::Centers.roles_enabled
35
+ end
36
+
30
37
  def scope_id?
31
38
  Decidim::Centers.scopes_enabled
32
39
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Centers
5
+ module Admin
6
+ # This class holds a Form to update roles from Decidim's admin panel.
7
+ class RoleForm < Decidim::Form
8
+ include TranslatableAttributes
9
+
10
+ translatable_attribute :title, String
11
+
12
+ validates :title, translatable_presence: true
13
+
14
+ alias organization current_organization
15
+ end
16
+ end
17
+ end
18
+ end
@@ -7,11 +7,13 @@ module Decidim
7
7
  module Verifications
8
8
  class Center < Decidim::AuthorizationHandler
9
9
  validate :center_present
10
+ validate :role_present
10
11
  validate :scope_present
11
12
 
12
13
  def metadata
13
14
  super.merge(
14
15
  centers: user.centers.pluck(:id),
16
+ roles: user.center_roles.pluck(:id),
15
17
  scopes: user.scopes.pluck(:id)
16
18
  )
17
19
  end
@@ -22,6 +24,12 @@ module Decidim
22
24
  errors.add(:user, I18n.t("decidim.centers.authorizations.new.error")) unless user.centers.any?
23
25
  end
24
26
 
27
+ def role_present
28
+ return unless Decidim::Centers.roles_enabled
29
+
30
+ errors.add(:user, I18n.t("decidim.centers.authorizations.new.error")) unless user.center_roles.any?
31
+ end
32
+
25
33
  def scope_present
26
34
  return unless Decidim::Centers.scopes_enabled
27
35
 
@@ -12,6 +12,12 @@ module Decidim
12
12
  [center.id, translated_attribute(center.title)]
13
13
  end
14
14
  end
15
+
16
+ def role_options_for_select
17
+ Decidim::Centers::Role.where(organization: current_organization).map do |role|
18
+ [role.id, translated_attribute(role.title)]
19
+ end
20
+ end
15
21
  end
16
22
  end
17
23
  end
@@ -7,7 +7,7 @@ module Decidim
7
7
 
8
8
  def perform(user_id)
9
9
  @user = Decidim::User.find(user_id)
10
- @user.centers.any? || @user.scopes.any? ? create_auth : remove_auth
10
+ @user.centers.any? || @user.scopes.any? || @user.center_roles.any? ? create_auth : remove_auth
11
11
  rescue ActiveRecord::RecordNotFound => _e
12
12
  Rails.logger.error "AutoVerificationJob: ERROR: user not found #{user_id}"
13
13
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Centers
5
+ class SyncRoleUserJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(data)
9
+ @user = Decidim::User.find(data[:user_id])
10
+ @role = Decidim::Centers::Role.find(data[:role_id])
11
+ create_or_update_role_user
12
+ end
13
+
14
+ private
15
+
16
+ def create_or_update_role_user
17
+ Decidim::Centers::CreateOrUpdateRoleUser.call(@role, @user) do
18
+ on(:ok) do
19
+ Rails.logger.info "SyncRoleUserJob: Success: updated for user #{@user.id}"
20
+ end
21
+
22
+ on(:invalid) do
23
+ Rails.logger.error "SyncRoleUserJob: ERROR: not updated for user #{@user.id}"
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -13,6 +13,13 @@ module Decidim
13
13
 
14
14
  has_many :centers, through: :center_users
15
15
 
16
+ has_many :role_users,
17
+ class_name: "Decidim::Centers::RoleUser",
18
+ foreign_key: "decidim_user_id",
19
+ dependent: :destroy
20
+
21
+ has_many :center_roles, through: :role_users, source: :role
22
+
16
23
  has_many :scope_users,
17
24
  class_name: "Decidim::Centers::ScopeUser",
18
25
  foreign_key: "decidim_user_id",
@@ -24,6 +31,10 @@ module Decidim
24
31
  centers.first
25
32
  end
26
33
 
34
+ def center_role
35
+ center_roles.first
36
+ end
37
+
27
38
  def scope
28
39
  scopes.first
29
40
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Centers
5
+ class Role < Centers::ApplicationRecord
6
+ include Decidim::TranslatableResource
7
+
8
+ translatable_fields :title
9
+
10
+ belongs_to :organization,
11
+ foreign_key: "decidim_organization_id",
12
+ class_name: "Decidim::Organization"
13
+
14
+ scope :not_deleted, -> { where(deleted_at: nil) }
15
+
16
+ def deleted?
17
+ deleted_at.present?
18
+ end
19
+
20
+ def self.log_presenter_class_for(_log)
21
+ Decidim::Centers::AdminLog::RolePresenter
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Centers
5
+ class RoleUser < Centers::ApplicationRecord
6
+ include UniqueByUser
7
+
8
+ belongs_to :role,
9
+ foreign_key: "decidim_centers_role_id",
10
+ class_name: "Decidim::Centers::Role"
11
+
12
+ belongs_to :user,
13
+ foreign_key: "decidim_user_id",
14
+ class_name: "Decidim::User"
15
+
16
+ validate :same_organization
17
+
18
+ private
19
+
20
+ def same_organization
21
+ return if role.try(:organization) == user.try(:organization)
22
+
23
+ errors.add(:role, :invalid)
24
+ errors.add(:user, :invalid)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -9,6 +9,10 @@ $(() => {
9
9
  url: "/admin/centers/centers",
10
10
  inputName: "[authorization_handlers_options][center][centers]"
11
11
  },
12
+ {
13
+ url: "/admin/centers/roles",
14
+ inputName: "[authorization_handlers_options][center][roles]"
15
+ },
12
16
  {
13
17
  url: "/admin/centers/scopes",
14
18
  inputName: "[authorization_handlers_options][center][scopes]"
@@ -8,7 +8,7 @@ module Decidim
8
8
  return permission_action unless user
9
9
  return permission_action unless user.admin?
10
10
  return permission_action unless permission_action.scope == :admin
11
- return permission_action unless permission_action.subject == :center
11
+ return permission_action unless [:center, :role].include?(permission_action.subject)
12
12
 
13
13
  allow!
14
14
  permission_action
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Centers
5
+ module AdminLog
6
+ # This class holds the logic to present a `Decidim::Centers::Role`
7
+ # for the `AdminLog` log.
8
+ #
9
+ # Usage should be automatic and you shouldn't need to call this class
10
+ # directly, but here's an example:
11
+ #
12
+ # action_log = Decidim::ActionLog.last
13
+ # view_helpers # => this comes from the views
14
+ # RolePresenter.new(action_log, view_helpers).present
15
+ class RolePresenter < Decidim::Log::BasePresenter
16
+ private
17
+
18
+ def action_string
19
+ case action
20
+ when "create", "delete", "update"
21
+ "decidim.centers.admin_log.role.#{action}"
22
+ else
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,5 +1,9 @@
1
1
  <%= f.collection_select :center_id, f.object.center_options_for_select, :first, :last %>
2
2
 
3
+ <% if Decidim::Centers.roles_enabled %>
4
+ <%= f.collection_select :role_id, f.object.role_options_for_select, :first, :last %>
5
+ <% end %>
6
+
3
7
  <% if Decidim::Centers.scopes_enabled %>
4
8
  <%= scopes_picker_field f, :scope_id, root: nil %>
5
9
  <% end %>
@@ -3,6 +3,10 @@
3
3
  <div class="field">
4
4
  <%= f.collection_select :center_id, f.object.center_options_for_select, :first, :last %>
5
5
 
6
+ <% if Decidim::Centers.roles_enabled %>
7
+ <%= f.collection_select :role_id, f.object.role_options_for_select, :first, :last %>
8
+ <% end %>
9
+
6
10
  <% if Decidim::Centers.scopes_enabled %>
7
11
  <%= scopes_picker_field f, :scope_id, root: nil %>
8
12
  <% end %>
@@ -3,7 +3,7 @@
3
3
  <div class="card-divider">
4
4
  <h2 class="card-title">
5
5
  <%= t(".title") %>
6
- <%= link_to t("actions.new", scope: "decidim.centers", name: t("models.center.name", scope: "decidim.centers.admin")), new_center_path, class: "button tiny button--title" if allowed_to? :create, :center %>
6
+ <%= link_to t("actions.new", scope: "decidim.centers.admin.centers", name: t("models.center.name", scope: "decidim.centers.admin")), new_center_path, class: "button tiny button--title" if allowed_to? :create, :center %>
7
7
  </h2>
8
8
  </div>
9
9
 
@@ -14,7 +14,7 @@
14
14
  <tr>
15
15
  <th><%= t("models.center.fields.title", scope: "decidim.centers") %></th>
16
16
  <th><%= t("models.center.fields.created_at", scope: "decidim.centers") %></th>
17
- <th class="actions"><%= t("actions.title", scope: "decidim.centers") %></th>
17
+ <th class="actions"><%= t("actions.title", scope: "decidim.centers.admin.centers") %></th>
18
18
  </tr>
19
19
  </thead>
20
20
  <tbody>
@@ -28,11 +28,11 @@
28
28
  </td>
29
29
  <td class="table-list__actions">
30
30
  <% if allowed_to? :update, :center, center: center %>
31
- <%= icon_link_to "pencil", edit_center_path(center), t("actions.edit", scope: "decidim.centers"), class: "action-icon--edit" %>
31
+ <%= icon_link_to "pencil", edit_center_path(center), t("actions.edit", scope: "decidim.centers.admin.centers"), class: "action-icon--edit" %>
32
32
  <% end %>
33
33
 
34
34
  <% if allowed_to? :destroy, :center, center: center %>
35
- <%= icon_link_to "circle-x", center_path(center), t("actions.destroy", scope: "decidim.centers"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.centers") } %>
35
+ <%= icon_link_to "circle-x", center_path(center), t("actions.destroy", scope: "decidim.centers.admin.centers"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.centers.admin.centers") } %>
36
36
  <% end %>
37
37
  </td>
38
38
  </tr>
@@ -0,0 +1,10 @@
1
+ <div class="card">
2
+ <div class="card-divider">
3
+ <h2 class="card-title"><%= title %></h2>
4
+ </div>
5
+ <div class="card-section">
6
+ <div class="row column">
7
+ <%= form.translated :text_field, :title, autofocus: true %>
8
+ </div>
9
+ </div>
10
+ </div>
@@ -0,0 +1,8 @@
1
+ <% add_decidim_page_title(t(".title")) %>
2
+ <%= decidim_form_for(@form, html: { class: "form edit_role" }) do |f| %>
3
+ <%= render partial: "form", object: f, locals: { title: t(".title") } %>
4
+
5
+ <div class="button--double form-general-submit">
6
+ <%= f.submit t(".save") %>
7
+ </div>
8
+ <% end %>
@@ -0,0 +1,45 @@
1
+ <% add_decidim_page_title(t(".title")) %>
2
+ <div class="card">
3
+ <div class="card-divider">
4
+ <h2 class="card-title">
5
+ <%= t(".title") %>
6
+ <%= link_to t("actions.new", scope: "decidim.centers.admin.roles", name: t("models.role.name", scope: "decidim.centers.admin")), new_role_path, class: "button tiny button--title" if allowed_to? :create, :role %>
7
+ </h2>
8
+ </div>
9
+
10
+ <div class="card-section">
11
+ <div class="table-scroll">
12
+ <table class="table-list roles">
13
+ <thead>
14
+ <tr>
15
+ <th><%= t("models.role.fields.title", scope: "decidim.centers") %></th>
16
+ <th><%= t("models.role.fields.created_at", scope: "decidim.centers") %></th>
17
+ <th class="actions"><%= t("actions.title", scope: "decidim.centers.admin.roles") %></th>
18
+ </tr>
19
+ </thead>
20
+ <tbody>
21
+ <% roles.each do |role| %>
22
+ <tr data-id="<%= role.id %>">
23
+ <td>
24
+ <%= translated_attribute(role.title) %><br>
25
+ </td>
26
+ <td>
27
+ <%= l role.created_at, format: "%d/%m/%Y - %H:%M" %>
28
+ </td>
29
+ <td class="table-list__actions">
30
+ <% if allowed_to? :update, :role, role: role %>
31
+ <%= icon_link_to "pencil", edit_role_path(role), t("actions.edit", scope: "decidim.centers.admin.roles"), class: "action-icon--edit" %>
32
+ <% end %>
33
+
34
+ <% if allowed_to? :destroy, :role, role: role %>
35
+ <%= icon_link_to "circle-x", role_path(role), t("actions.destroy", scope: "decidim.centers.admin.roles"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.centers.admin.roles") } %>
36
+ <% end %>
37
+ </td>
38
+ </tr>
39
+ <% end %>
40
+ </tbody>
41
+ </table>
42
+ <%= paginate roles, theme: "decidim" %>
43
+ </div>
44
+ </div>
45
+ </div>
@@ -0,0 +1,8 @@
1
+ <% add_decidim_page_title(t(".title")) %>
2
+ <%= decidim_form_for(@form, html: { class: "form new_role" }) do |f| %>
3
+ <%= render partial: "form", object: f, locals: { title: t(".title") } %>
4
+
5
+ <div class="button--double form-general-submit">
6
+ <%= f.submit t(".create") %>
7
+ </div>
8
+ <% end %>
@@ -6,17 +6,18 @@ en:
6
6
  explanation: Get verified with the center of the user
7
7
  fields:
8
8
  centers: Centers
9
+ roles: Roles
9
10
  scopes: Scopes
10
11
  name: Center
11
12
  centers:
12
- actions:
13
- confirm_destroy: Are you sure you want to delete this center?
14
- destroy: Delete
15
- edit: Edit
16
- new: New center
17
- title: Actions
18
13
  admin:
19
14
  centers:
15
+ actions:
16
+ confirm_destroy: Are you sure you want to delete this center?
17
+ destroy: Delete
18
+ edit: Edit
19
+ new: New center
20
+ title: Actions
20
21
  create:
21
22
  invalid: There was a problem creating this center
22
23
  success: Center successfully created
@@ -36,16 +37,49 @@ en:
36
37
  models:
37
38
  center:
38
39
  name: Center
40
+ role:
41
+ name: Role
42
+ roles:
43
+ actions:
44
+ confirm_destroy: Are you sure you want to delete this role?
45
+ destroy: Delete
46
+ edit: Edit
47
+ new: New role
48
+ title: Actions
49
+ create:
50
+ invalid: There was a problem creating this role
51
+ success: Role successfully created
52
+ destroy:
53
+ success: Role successfully deleted
54
+ edit:
55
+ save: Update
56
+ title: Edit role
57
+ index:
58
+ title: Roles
59
+ new:
60
+ create: Create
61
+ title: Create role
62
+ update:
63
+ invalid: There was a problem saving the role.
64
+ success: Role successfully saved
39
65
  admin_log:
40
66
  center:
41
67
  create: "%{user_name} created the %{resource_name} center"
42
68
  delete: "%{user_name} deleted the %{resource_name} center"
43
69
  update: "%{user_name} updated the %{resource_name} center"
70
+ role:
71
+ create: "%{user_name} created the %{resource_name} role"
72
+ delete: "%{user_name} deleted the %{resource_name} role"
73
+ update: "%{user_name} updated the %{resource_name} role"
44
74
  authorizations:
45
75
  new:
46
- error: The user has no center or scope configured
76
+ error: The user has no center, role or scope configured
47
77
  models:
48
78
  center:
49
79
  fields:
50
80
  created_at: Created at
51
81
  title: Title
82
+ role:
83
+ fields:
84
+ created_at: Created at
85
+ title: Title
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateDecidimCentersRoles < ActiveRecord::Migration[6.1]
4
+ def change
5
+ create_table :decidim_centers_roles do |t|
6
+ t.references :decidim_organization, foreign_key: true, index: true
7
+ t.jsonb :title, null: false
8
+ t.datetime :deleted_at
9
+
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateDecidimCentersRoleUsers < ActiveRecord::Migration[6.1]
4
+ def change
5
+ create_table :decidim_centers_role_users do |t|
6
+ t.references :decidim_centers_role, foreign_key: true, index: { name: "index_decidim_role_users_on_decidim_role_id" }
7
+ t.references :decidim_user, foreign_key: true, index: { name: "index_decidim_role_users_on_decidim_user_id" }
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -11,6 +11,7 @@ module Decidim
11
11
 
12
12
  routes do
13
13
  resources :centers
14
+ resources :roles
14
15
  resources :scopes, only: :index
15
16
  root to: "centers#index"
16
17
  end
@@ -29,6 +30,12 @@ module Decidim
29
30
  icon_name: "home",
30
31
  position: 15,
31
32
  active: :inclusive
33
+ menu.add_item :roles,
34
+ I18n.t("menu.roles", scope: "decidim.admin", default: "Roles"),
35
+ decidim_admin_centers.roles_path,
36
+ icon_name: "members",
37
+ position: 16,
38
+ active: :inclusive
32
39
  end
33
40
  end
34
41
 
@@ -54,6 +54,7 @@ module Decidim
54
54
  initializer "decidim_centers.sync" do
55
55
  ActiveSupport::Notifications.subscribe "decidim.centers.user.updated" do |_name, data|
56
56
  Decidim::Centers::SyncCenterUserJob.perform_now(data)
57
+ Decidim::Centers::SyncRoleUserJob.perform_now(data) if Decidim::Centers.roles_enabled
57
58
  Decidim::Centers::SyncScopeUserJob.perform_now(data) if Decidim::Centers.scopes_enabled
58
59
  Decidim::Centers::AutoVerificationJob.perform_later(data[:user_id])
59
60
  end
@@ -66,6 +67,7 @@ module Decidim
66
67
 
67
68
  workflow.options do |options|
68
69
  options.attribute :centers, type: :string
70
+ options.attribute :roles, type: :string if Decidim::Centers.roles_enabled
69
71
  options.attribute :scopes, type: :string if Decidim::Centers.scopes_enabled
70
72
  end
71
73
  end
@@ -18,6 +18,21 @@ FactoryBot.define do
18
18
  center { create :center, organization: user.organization }
19
19
  end
20
20
 
21
+ factory :role, class: "Decidim::Centers::Role" do
22
+ organization { create :organization }
23
+ title { generate_localized_title }
24
+ deleted_at { nil }
25
+
26
+ trait :deleted do
27
+ deleted_at { Time.current }
28
+ end
29
+ end
30
+
31
+ factory :role_user, class: "Decidim::Centers::RoleUser" do
32
+ user { create :user }
33
+ role { create :role, organization: user.organization }
34
+ end
35
+
21
36
  factory :scope_user, class: "Decidim::Centers::ScopeUser" do
22
37
  user { create :user }
23
38
  scope { create :scope, organization: user.organization }
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- def check_center_authorization(authorization, user, center, scope = nil)
3
+ def check_center_authorization(authorization, user, center, scope: nil, role: nil)
4
4
  expect(authorization.name).to eq("center")
5
5
  expect(authorization.user).to eq(user)
6
6
  expect(authorization.metadata["centers"]).to include(center.id)
7
7
  expect(authorization.metadata["scopes"]).to include(scope.id) if scope
8
+ expect(authorization.metadata["roles"]).to include(role.id) if role
8
9
  end
9
10
 
10
11
  shared_examples_for "no authorization is created" do
@@ -20,3 +21,11 @@ shared_context "with scopes disabled" do
20
21
  allow(Decidim::Centers).to receive(:scopes_enabled).and_return(false)
21
22
  end
22
23
  end
24
+
25
+ shared_context "with roles disabled" do
26
+ let(:role) { nil }
27
+
28
+ before do
29
+ allow(Decidim::Centers).to receive(:roles_enabled).and_return(false)
30
+ end
31
+ end
@@ -9,7 +9,7 @@ module Decidim
9
9
 
10
10
  status_code = :unauthorized
11
11
  return [status_code, { fields: { centers: "..." } }] if authorization_centers.blank?
12
- return [:ok, {}] if belongs_to_center? && belongs_to_scope?
12
+ return [:ok, {}] if belongs_to_center? && belongs_to_scope? && belongs_to_role?
13
13
 
14
14
  [status_code, {}]
15
15
  end
@@ -20,6 +20,12 @@ module Decidim
20
20
  options["centers"]&.split(",") || []
21
21
  end
22
22
 
23
+ def options_roles
24
+ return [] unless Decidim::Centers.roles_enabled
25
+
26
+ options["roles"]&.split(",") || []
27
+ end
28
+
23
29
  def options_scopes
24
30
  return [] unless Decidim::Centers.scopes_enabled
25
31
 
@@ -30,6 +36,10 @@ module Decidim
30
36
  authorization.metadata["centers"] || []
31
37
  end
32
38
 
39
+ def authorization_roles
40
+ authorization.metadata["roles"] || []
41
+ end
42
+
33
43
  def authorization_scopes
34
44
  authorization.metadata["scopes"] || []
35
45
  end
@@ -38,6 +48,12 @@ module Decidim
38
48
  options_centers.empty? || options_centers.detect { |center| authorization_centers.include? center.to_i }
39
49
  end
40
50
 
51
+ def belongs_to_role?
52
+ return true unless Decidim::Centers.roles_enabled
53
+
54
+ options_roles.empty? || options_roles.detect { |center| authorization_roles.include? center.to_i }
55
+ end
56
+
41
57
  def belongs_to_scope?
42
58
  return true unless Decidim::Centers.scopes_enabled
43
59
 
@@ -3,7 +3,7 @@
3
3
  module Decidim
4
4
  # This holds the decidim-centers version.
5
5
  module Centers
6
- VERSION = "0.1.1"
6
+ VERSION = "0.2.0"
7
7
  DECIDIM_VERSION = "0.27.4"
8
8
  COMPAT_DECIDIM_VERSION = [">= 0.27.0", "< 0.28"].freeze
9
9
  end
@@ -15,5 +15,10 @@ module Decidim
15
15
  config_accessor :scopes_enabled do
16
16
  Decidim::Env.new("DECIDIM_CENTERS_SCOPES_ENABLED", true).default_or_present_if_exists
17
17
  end
18
+
19
+ # if false, it won't ask the user for the role
20
+ config_accessor :roles_enabled do
21
+ Decidim::Env.new("DECIDIM_CENTERS_ROLES_ENABLED", true).default_or_present_if_exists
22
+ end
18
23
  end
19
24
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decidim-centers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francisco Bolívar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-13 00:00:00.000000000 Z
11
+ date: 2024-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: decidim-core
@@ -59,9 +59,13 @@ files:
59
59
  - app/commands/concerns/decidim/centers/publish_center_update_event.rb
60
60
  - app/commands/concerns/decidim/centers/update_account_override.rb
61
61
  - app/commands/decidim/centers/admin/create_center.rb
62
+ - app/commands/decidim/centers/admin/create_role.rb
62
63
  - app/commands/decidim/centers/admin/destroy_center.rb
64
+ - app/commands/decidim/centers/admin/destroy_role.rb
63
65
  - app/commands/decidim/centers/admin/update_center.rb
66
+ - app/commands/decidim/centers/admin/update_role.rb
64
67
  - app/commands/decidim/centers/create_or_update_center_user.rb
68
+ - app/commands/decidim/centers/create_or_update_role_user.rb
65
69
  - app/commands/decidim/centers/create_or_update_scope_user.rb
66
70
  - app/controllers/concerns/decidim/centers/admin/needs_select2_snippets.rb
67
71
  - app/controllers/concerns/decidim/centers/devise/omniauth_registrations_controller_override.rb
@@ -69,19 +73,24 @@ files:
69
73
  - app/controllers/concerns/decidim/centers/with_scopes_helper.rb
70
74
  - app/controllers/decidim/centers/admin/application_controller.rb
71
75
  - app/controllers/decidim/centers/admin/centers_controller.rb
76
+ - app/controllers/decidim/centers/admin/roles_controller.rb
72
77
  - app/controllers/decidim/centers/admin/scopes_controller.rb
73
78
  - app/forms/concerns/decidim/centers/account_form_override.rb
74
79
  - app/forms/decidim/centers/admin/center_form.rb
80
+ - app/forms/decidim/centers/admin/role_form.rb
75
81
  - app/forms/decidim/centers/verifications/center.rb
76
82
  - app/helpers/decidim/centers/application_helper.rb
77
83
  - app/jobs/decidim/centers/auto_verification_job.rb
78
84
  - app/jobs/decidim/centers/sync_center_user_job.rb
85
+ - app/jobs/decidim/centers/sync_role_user_job.rb
79
86
  - app/jobs/decidim/centers/sync_scope_user_job.rb
80
87
  - app/models/concerns/decidim/centers/unique_by_user.rb
81
88
  - app/models/concerns/decidim/centers/user_override.rb
82
89
  - app/models/decidim/centers/application_record.rb
83
90
  - app/models/decidim/centers/center.rb
84
91
  - app/models/decidim/centers/center_user.rb
92
+ - app/models/decidim/centers/role.rb
93
+ - app/models/decidim/centers/role_user.rb
85
94
  - app/models/decidim/centers/scope_user.rb
86
95
  - app/overrides/decidim/account/show/centers.html.erb.deface
87
96
  - app/overrides/decidim/devise/omniauth_registrations/new/centers.html.erb.deface
@@ -91,18 +100,25 @@ files:
91
100
  - app/packs/stylesheets/vendor/select2_foundation_theme.scss
92
101
  - app/permissions/decidim/centers/admin/permissions.rb
93
102
  - app/presenters/decidim/centers/admin_log/center_presenter.rb
103
+ - app/presenters/decidim/centers/admin_log/role_presenter.rb
94
104
  - app/views/decidim/centers/_profile_form.html.erb
95
105
  - app/views/decidim/centers/_registration_form.html.erb
96
106
  - app/views/decidim/centers/admin/centers/_form.html.erb
97
107
  - app/views/decidim/centers/admin/centers/edit.html.erb
98
108
  - app/views/decidim/centers/admin/centers/index.html.erb
99
109
  - app/views/decidim/centers/admin/centers/new.html.erb
110
+ - app/views/decidim/centers/admin/roles/_form.html.erb
111
+ - app/views/decidim/centers/admin/roles/edit.html.erb
112
+ - app/views/decidim/centers/admin/roles/index.html.erb
113
+ - app/views/decidim/centers/admin/roles/new.html.erb
100
114
  - config/assets.rb
101
115
  - config/i18n-tasks.yml
102
116
  - config/locales/en.yml
103
117
  - db/migrate/20231129114029_create_decidim_centers_centers.rb
104
118
  - db/migrate/20231130125631_create_decidim_centers_center_users.rb
105
119
  - db/migrate/20231205153627_create_decidim_centers_scope_users.rb
120
+ - db/migrate/20241204134913_create_decidim_centers_roles.rb
121
+ - db/migrate/20241204135138_create_decidim_centers_role_users.rb
106
122
  - lib/decidim/centers.rb
107
123
  - lib/decidim/centers/admin.rb
108
124
  - lib/decidim/centers/admin_engine.rb