decidim-assemblies 0.11.2 → 0.12.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/admin/decidim_assemblies_manifest.js +1 -0
  3. data/app/assets/javascripts/decidim/assemblies/admin/assembly_members.js.es6 +37 -0
  4. data/app/cells/decidim/assemblies/assembly_cell.rb +19 -0
  5. data/app/cells/decidim/assemblies/assembly_m/footer.erb +15 -0
  6. data/app/cells/decidim/assemblies/assembly_m/tags.erb +1 -0
  7. data/app/cells/decidim/assemblies/assembly_m_cell.rb +50 -0
  8. data/app/cells/decidim/assemblies/assembly_member/show.erb +53 -0
  9. data/app/cells/decidim/assemblies/assembly_member_cell.rb +21 -0
  10. data/app/commands/decidim/assemblies/admin/create_assembly_member.rb +73 -0
  11. data/app/commands/decidim/assemblies/admin/destroy_assembly_member.rb +56 -0
  12. data/app/commands/decidim/assemblies/admin/update_assembly_member.rb +69 -0
  13. data/app/controllers/decidim/assemblies/admin/application_controller.rb +14 -0
  14. data/app/controllers/decidim/assemblies/admin/assemblies_controller.rb +8 -12
  15. data/app/controllers/decidim/assemblies/admin/assembly_attachment_collections_controller.rb +1 -1
  16. data/app/controllers/decidim/assemblies/admin/assembly_attachments_controller.rb +1 -1
  17. data/app/controllers/decidim/assemblies/admin/assembly_copies_controller.rb +3 -3
  18. data/app/controllers/decidim/assemblies/admin/assembly_members_controller.rb +86 -0
  19. data/app/controllers/decidim/assemblies/admin/assembly_publications_controller.rb +3 -3
  20. data/app/controllers/decidim/assemblies/admin/assembly_user_roles_controller.rb +7 -7
  21. data/app/controllers/decidim/assemblies/admin/concerns/assembly_admin.rb +20 -8
  22. data/app/controllers/decidim/assemblies/admin/participatory_space_private_users_controller.rb +1 -1
  23. data/app/controllers/decidim/assemblies/application_controller.rb +32 -0
  24. data/app/controllers/decidim/assemblies/assemblies_controller.rb +2 -2
  25. data/app/controllers/decidim/assemblies/assembly_members_controller.rb +35 -0
  26. data/app/forms/decidim/assemblies/admin/assembly_member_form.rb +51 -0
  27. data/app/models/decidim/assembly.rb +12 -1
  28. data/app/models/decidim/assembly_member.rb +24 -0
  29. data/app/permissions/decidim/assemblies/permissions.rb +230 -0
  30. data/app/presenters/decidim/admin/assembly_member_presenter.rb +24 -0
  31. data/app/presenters/decidim/assemblies/admin_log/assembly_member_presenter.rb +52 -0
  32. data/app/presenters/decidim/assemblies/admin_log/value_types/member_position_presenter.rb +21 -0
  33. data/app/presenters/decidim/assembly_member_presenter.rb +46 -0
  34. data/app/queries/decidim/assemblies/admin/assembly_members.rb +55 -0
  35. data/app/views/decidim/assemblies/_assembly.html.erb +1 -22
  36. data/app/views/decidim/assemblies/admin/assemblies/_form.html.erb +2 -0
  37. data/app/views/decidim/assemblies/admin/assemblies/edit.html.erb +2 -2
  38. data/app/views/decidim/assemblies/admin/assemblies/index.html.erb +8 -8
  39. data/app/views/decidim/assemblies/admin/assembly_members/_form.html.erb +66 -0
  40. data/app/views/decidim/assemblies/admin/assembly_members/edit.html.erb +7 -0
  41. data/app/views/decidim/assemblies/admin/assembly_members/index.html.erb +91 -0
  42. data/app/views/decidim/assemblies/admin/assembly_members/new.html.erb +7 -0
  43. data/app/views/decidim/assemblies/admin/assembly_user_roles/index.html.erb +4 -4
  44. data/app/views/decidim/assemblies/assemblies/show.html.erb +1 -2
  45. data/app/views/decidim/assemblies/assembly_members/index.html.erb +10 -0
  46. data/app/views/decidim/assemblies/pages/user_profile/_member_of.html.erb +12 -0
  47. data/app/views/decidim/assembly_members/_assembly_member.html.erb +1 -0
  48. data/app/views/layouts/decidim/_assembly_header.html.erb +10 -1
  49. data/app/views/layouts/decidim/admin/assemblies.html.erb +7 -10
  50. data/app/views/layouts/decidim/admin/assembly.html.erb +19 -11
  51. data/config/locales/ca.yml +84 -0
  52. data/config/locales/en.yml +84 -0
  53. data/config/locales/es.yml +84 -0
  54. data/config/locales/eu.yml +84 -0
  55. data/config/locales/fi.yml +84 -0
  56. data/config/locales/fr.yml +84 -0
  57. data/config/locales/gl.yml +84 -0
  58. data/config/locales/it.yml +84 -0
  59. data/config/locales/nl.yml +84 -0
  60. data/config/locales/pl.yml +90 -0
  61. data/config/locales/pt-BR.yml +84 -0
  62. data/config/locales/pt.yml +84 -0
  63. data/config/locales/ru.yml +96 -11
  64. data/config/locales/sv.yml +84 -0
  65. data/config/locales/uk.yml +91 -1
  66. data/db/migrate/20180314143822_add_assembly_members.rb +21 -0
  67. data/db/migrate/20180426162405_assembly_member_belongs_to_user.rb +7 -0
  68. data/db/migrate/20180515073049_update_assembly_members_index.rb +8 -0
  69. data/lib/decidim/assemblies/admin_engine.rb +2 -12
  70. data/lib/decidim/assemblies/engine.rb +21 -10
  71. data/lib/decidim/assemblies/participatory_space.rb +58 -2
  72. data/lib/decidim/assemblies/test/factories.rb +35 -0
  73. data/lib/decidim/assemblies/version.rb +1 -1
  74. metadata +41 -22
  75. data/app/controllers/decidim/assemblies/categories_controller.rb +0 -13
  76. data/app/models/decidim/assemblies/abilities/admin/admin_ability.rb +0 -20
  77. data/app/models/decidim/assemblies/abilities/admin/assembly_admin_ability.rb +0 -58
  78. data/app/models/decidim/assemblies/abilities/admin/assembly_collaborator_ability.rb +0 -25
  79. data/app/models/decidim/assemblies/abilities/admin/assembly_moderator_ability.rb +0 -33
  80. data/app/models/decidim/assemblies/abilities/admin/assembly_role_ability.rb +0 -65
  81. data/app/models/decidim/assemblies/abilities/admin_ability.rb +0 -16
  82. data/app/models/decidim/assemblies/abilities/assembly_admin_ability.rb +0 -15
  83. data/app/models/decidim/assemblies/abilities/assembly_collaborator_ability.rb +0 -15
  84. data/app/models/decidim/assemblies/abilities/assembly_moderator_ability.rb +0 -15
  85. data/app/models/decidim/assemblies/abilities/assembly_role_ability.rb +0 -51
  86. data/app/models/decidim/assemblies/abilities/everyone_ability.rb +0 -17
@@ -49,6 +49,11 @@ module Decidim
49
49
  dependent: :destroy,
50
50
  as: :participatory_space
51
51
 
52
+ has_many :members,
53
+ foreign_key: "decidim_assembly_id",
54
+ class_name: "Decidim::AssemblyMember",
55
+ dependent: :destroy
56
+
52
57
  has_many :components, as: :participatory_space, dependent: :destroy
53
58
 
54
59
  has_many :children, foreign_key: "parent_id", class_name: "Decidim::Assembly", inverse_of: :parent, dependent: :destroy
@@ -89,7 +94,7 @@ module Decidim
89
94
  end
90
95
 
91
96
  def self_and_ancestors
92
- self.class.where("#{self.class.table_name}.parents_path @> ?", parents_path).order("string_to_array(#{self.class.table_name}.parents_path::text, '.')")
97
+ self.class.where("#{self.class.table_name}.parents_path @> ?", parents_path).order(Arel.sql("string_to_array(#{self.class.table_name}.parents_path::text, '.')"))
93
98
  end
94
99
 
95
100
  def ancestors
@@ -104,6 +109,12 @@ module Decidim
104
109
  super.where(private_space: false).or(Decidim::Assembly.where(private_space: true).where(is_transparent: true))
105
110
  end
106
111
 
112
+ def can_participate?(user)
113
+ return true unless private_space?
114
+ return true if private_space? && users.include?(user)
115
+ return false if private_space? && is_transparent?
116
+ end
117
+
107
118
  private
108
119
 
109
120
  # When an assembly changes their parent, we need to update the parents_path attribute
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # It represents a member of the assembly (president, secretary, ...)
5
+ # Can be linked to an existent user in the platform
6
+ class AssemblyMember < ApplicationRecord
7
+ include Decidim::Traceable
8
+ include Decidim::Loggable
9
+
10
+ POSITIONS = %w(president vice_president secretary other).freeze
11
+
12
+ belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User", optional: true
13
+ belongs_to :assembly, foreign_key: "decidim_assembly_id", class_name: "Decidim::Assembly"
14
+ alias participatory_space assembly
15
+
16
+ default_scope { order(weight: :asc, created_at: :asc) }
17
+
18
+ scope :not_ceased, -> { where(ceased_date: nil) }
19
+
20
+ def self.log_presenter_class_for(_log)
21
+ Decidim::Assemblies::AdminLog::AssemblyMemberPresenter
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ class Permissions < Decidim::DefaultPermissions
6
+ def permissions
7
+ user_can_enter_space_area?
8
+
9
+ return permission_action if assembly && !assembly.is_a?(Decidim::Assembly)
10
+
11
+ if permission_action.scope == :public
12
+ public_list_assemblies_action?
13
+ public_read_assembly_action?
14
+ public_list_members_action?
15
+ public_report_content_action?
16
+ return permission_action
17
+ end
18
+
19
+ return permission_action unless user
20
+ if !has_manageable_assemblies? && !user.admin?
21
+ disallow!
22
+ return permission_action
23
+ end
24
+ return permission_action unless permission_action.scope == :admin
25
+
26
+ if read_admin_dashboard_action?
27
+ user_can_read_admin_dashboard?
28
+ return permission_action
29
+ end
30
+
31
+ user_can_read_assembly_list?
32
+ user_can_read_current_assembly?
33
+ user_can_create_assembly?
34
+ user_can_destroy_assembly?
35
+
36
+ # org admins and space admins can do everything in the admin section
37
+ org_admin_action?
38
+
39
+ return permission_action unless assembly
40
+
41
+ moderator_action?
42
+ collaborator_action?
43
+ assembly_admin_action?
44
+
45
+ permission_action
46
+ end
47
+
48
+ private
49
+
50
+ # It's an admin user if it's an organization admin or is a space admin
51
+ # for the current `assembly`.
52
+ def admin_user?
53
+ user.admin? || (assembly ? can_manage_assembly?(role: :admin) : has_manageable_assemblies?)
54
+ end
55
+
56
+ # Checks if it has any manageable assembly, with any possible role.
57
+ def has_manageable_assemblies?(role: :any)
58
+ return unless user
59
+ assemblies_with_role_privileges(role).any?
60
+ end
61
+
62
+ # Whether the user can manage the given assembly or not.
63
+ def can_manage_assembly?(role: :any)
64
+ return unless user
65
+ assemblies_with_role_privileges(role).include? assembly
66
+ end
67
+
68
+ # Returns a collection of assemblies where the given user has the
69
+ # specific role privilege.
70
+ def assemblies_with_role_privileges(role)
71
+ Decidim::Assemblies::AssembliesWithUserRole.for(user, role)
72
+ end
73
+
74
+ def public_list_assemblies_action?
75
+ return unless permission_action.action == :list &&
76
+ permission_action.subject == :assembly
77
+
78
+ allow!
79
+ end
80
+
81
+ def public_read_assembly_action?
82
+ return unless permission_action.action == :read &&
83
+ [:assembly, :participatory_space].include?(permission_action.subject) &&
84
+ assembly
85
+
86
+ return allow! if user&.admin?
87
+ return allow! if assembly.published?
88
+ toggle_allow(can_manage_assembly?)
89
+ end
90
+
91
+ def public_list_members_action?
92
+ return unless permission_action.action == :list &&
93
+ permission_action.subject == :members
94
+
95
+ allow!
96
+ end
97
+
98
+ def public_report_content_action?
99
+ return unless permission_action.action == :create &&
100
+ permission_action.subject == :moderation
101
+
102
+ allow!
103
+ end
104
+
105
+ # All users with a relation to a assembly and organization admins can enter
106
+ # the space area. The sapce area is considered to be the assemblies zone,
107
+ # not the assembly groups one.
108
+ def user_can_enter_space_area?
109
+ return unless permission_action.action == :enter &&
110
+ permission_action.scope == :admin &&
111
+ permission_action.subject == :space_area &&
112
+ context.fetch(:space_name, nil) == :assemblies
113
+
114
+ toggle_allow(user.admin? || has_manageable_assemblies?)
115
+ end
116
+
117
+ # Checks if the permission_action is to read in the admin or not.
118
+ def admin_read_permission_action?
119
+ permission_action.action == :read
120
+ end
121
+
122
+ def read_admin_dashboard_action?
123
+ permission_action.action == :read &&
124
+ permission_action.subject == :admin_dashboard
125
+ end
126
+
127
+ # Any user that can enter the space area can read the admin dashboard.
128
+ def user_can_read_admin_dashboard?
129
+ toggle_allow(user.admin? || has_manageable_assemblies?)
130
+ end
131
+
132
+ # Only organization admins can create a assembly
133
+ def user_can_create_assembly?
134
+ return unless permission_action.action == :create &&
135
+ permission_action.subject == :assembly
136
+
137
+ toggle_allow(user.admin?)
138
+ end
139
+
140
+ # Only organization admins can destroy a assembly
141
+ def user_can_destroy_assembly?
142
+ return unless permission_action.action == :destroy &&
143
+ permission_action.subject == :assembly
144
+
145
+ toggle_allow(user.admin?)
146
+ end
147
+
148
+ # Everyone can read the assembly list
149
+ def user_can_read_assembly_list?
150
+ return unless read_assembly_list_permission_action?
151
+ toggle_allow(user.admin? || has_manageable_assemblies?)
152
+ end
153
+
154
+ def user_can_read_current_assembly?
155
+ return unless read_assembly_list_permission_action?
156
+ return if permission_action.subject == :assembly_list
157
+ toggle_allow(user.admin? || can_manage_assembly?)
158
+ end
159
+
160
+ # A moderator needs to be able to read the assembly they are assigned to,
161
+ # and needs to perform all actions for the moderations of that assembly.
162
+ def moderator_action?
163
+ return unless can_manage_assembly?(role: :moderator)
164
+
165
+ allow! if permission_action.subject == :moderation
166
+ end
167
+
168
+ # Collaborators can read/preview everything inside their assembly.
169
+ def collaborator_action?
170
+ return unless can_manage_assembly?(role: :collaborator)
171
+
172
+ allow! if permission_action.action == :read || permission_action.action == :preview
173
+ end
174
+
175
+ # Process admins can eprform everything *inside* that assembly. They cannot
176
+ # create a assembly or perform actions on assembly groups or other
177
+ # assemblies. They cannot destroy their assembly either.
178
+ def assembly_admin_action?
179
+ return unless can_manage_assembly?(role: :admin)
180
+ return if user.admin?
181
+ return disallow! if permission_action.action == :create &&
182
+ permission_action.subject == :assembly
183
+ return disallow! if permission_action.action == :destroy &&
184
+ permission_action.subject == :assembly
185
+
186
+ is_allowed = [
187
+ :attachment,
188
+ :attachment_collection,
189
+ :category,
190
+ :component,
191
+ :component_data,
192
+ :moderation,
193
+ :assembly,
194
+ :assembly_user_role,
195
+ :assembly_member
196
+ ].include?(permission_action.subject)
197
+ allow! if is_allowed
198
+ end
199
+
200
+ def org_admin_action?
201
+ return unless user.admin?
202
+
203
+ is_allowed = [
204
+ :attachment,
205
+ :attachment_collection,
206
+ :category,
207
+ :component,
208
+ :component_data,
209
+ :moderation,
210
+ :assembly,
211
+ :assembly_user_role,
212
+ :assembly_member,
213
+ :space_private_user
214
+ ].include?(permission_action.subject)
215
+ allow! if is_allowed
216
+ end
217
+
218
+ # Checks if the permission_action is to read the admin assemblies list or
219
+ # not.
220
+ def read_assembly_list_permission_action?
221
+ permission_action.action == :read &&
222
+ [:assembly, :participatory_space, :assembly_list].include?(permission_action.subject)
223
+ end
224
+
225
+ def assembly
226
+ @assembly ||= context.fetch(:current_participatory_space, nil) || context.fetch(:assembly, nil)
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ #
6
+ # Decorator for assembly members
7
+ #
8
+ class AssemblyMemberPresenter < SimpleDelegator
9
+ def name
10
+ if user
11
+ "#{user.name} (#{Decidim::UserPresenter.new(user).nickname})"
12
+ else
13
+ full_name
14
+ end
15
+ end
16
+
17
+ def position
18
+ return position_other if __getobj__.position == "other"
19
+
20
+ I18n.t(__getobj__.position, scope: "decidim.admin.models.assembly_member.positions", default: "")
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module AdminLog
6
+ # This class holds the logic to present a `Decidim::AssemblyMember`
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
+ # AssemblyMemberPresenter.new(action_log, view_helpers).present
15
+ class AssemblyMemberPresenter < Decidim::Log::BasePresenter
16
+ private
17
+
18
+ def diff_fields_mapping
19
+ {
20
+ full_name: :string,
21
+ gender: :string,
22
+ birthday: :date,
23
+ birthplace: :string,
24
+ designation_date: :date,
25
+ designation_mode: :string,
26
+ position: "Decidim::Assemblies::AdminLog::ValueTypes::MemberPositionPresenter",
27
+ position_other: :string,
28
+ weight: :integer,
29
+ ceased_date: :date
30
+ }
31
+ end
32
+
33
+ def i18n_labels_scope
34
+ "activemodel.attributes.assembly_member"
35
+ end
36
+
37
+ def action_string
38
+ case action
39
+ when "create", "delete", "update"
40
+ "decidim.admin_log.assembly_member.#{action}"
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ def has_diff?
47
+ action == "delete" || super
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module AdminLog
6
+ module ValueTypes
7
+ # This class presents the given value as an assembly member position.
8
+ # Check the `DefaultPresenter` for more info on how value presenters work.
9
+ class MemberPositionPresenter < Decidim::Log::ValueTypes::DefaultPresenter
10
+ # Public: Presents the value as an assembly member position.
11
+ #
12
+ # Returns an HTML-safe String.
13
+ def present
14
+ return if value.blank?
15
+ h.t(value, scope: "decidim.admin.models.assembly_member.positions", default: value)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ #
5
+ # Decorator for assembly members
6
+ #
7
+ class AssemblyMemberPresenter < SimpleDelegator
8
+ def age
9
+ (Time.current.strftime("%Y%m%d").to_i - birthday.strftime("%Y%m%d").to_i) / 10_000 if birthday
10
+ end
11
+
12
+ delegate :profile_url, :avatar_url, to: :user, allow_nil: true
13
+
14
+ def name
15
+ user ? user.name : full_name
16
+ end
17
+
18
+ def nickname
19
+ user.nickname if user
20
+ end
21
+
22
+ def personal_information
23
+ [
24
+ gender.presence,
25
+ age,
26
+ birthplace.presence
27
+ ].compact.join(" / ")
28
+ end
29
+
30
+ def position
31
+ return position_other if __getobj__.position == "other"
32
+
33
+ I18n.t(__getobj__.position, scope: "decidim.admin.models.assembly_member.positions", default: "")
34
+ end
35
+
36
+ private
37
+
38
+ def user
39
+ @user ||= begin
40
+ if (user = __getobj__.user.presence)
41
+ Decidim::UserPresenter.new(user)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Admin
6
+ # A class used to find the AssemblyMembers's by their status status.
7
+ class AssemblyMembers < Rectify::Query
8
+ # Syntactic sugar to initialize the class and return the queried objects.
9
+ #
10
+ # assembly_members - the initial AssemblyMember relation that needs to be filtered.
11
+ # query - query to filter user group names
12
+ # status - ceased status to be used as a filter
13
+ def self.for(assembly_members, query = nil, status = nil)
14
+ new(assembly_members, query, status).query
15
+ end
16
+
17
+ # Initializes the class.
18
+ #
19
+ # assembly_members - the AssemblyMember relation that need to be filtered
20
+ # query - query to filter user group names
21
+ # status - ceased status to be used as a filter
22
+ def initialize(assembly_members, query = nil, status = nil)
23
+ @assembly_members = assembly_members
24
+ @query = query
25
+ @status = status
26
+ end
27
+
28
+ # List the assembly members by the different filters.
29
+ def query
30
+ @assembly_members = filter_by_search(@assembly_members)
31
+ @assembly_members = filter_by_status(@assembly_members)
32
+ @assembly_members
33
+ end
34
+
35
+ private
36
+
37
+ def filter_by_search(assembly_members)
38
+ return assembly_members if @query.blank?
39
+ assembly_members.where("LOWER(full_name) LIKE LOWER(?)", "%#{@query}%")
40
+ end
41
+
42
+ def filter_by_status(assembly_members)
43
+ case @status
44
+ when "ceased"
45
+ assembly_members.where.not(ceased_date: nil)
46
+ when "not_ceased"
47
+ assembly_members.where(ceased_date: nil)
48
+ else
49
+ assembly_members
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end