decidim-initiatives 0.20.1 → 0.23.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -0
  3. data/app/assets/images/decidim/gamification/badges/initiatives.svg +1 -87
  4. data/app/assets/images/decidim/initiatives/icon.svg +1 -3
  5. data/app/assets/javascripts/decidim/initiatives/admin/initiatives_types.js.es6 +18 -0
  6. data/app/assets/stylesheet/decidim/initiatives/initiatives-votes.css.scss +0 -1
  7. data/app/assets/stylesheet/decidim/initiatives/initiatives.scss +6 -8
  8. data/app/assets/stylesheet/decidim/initiatives/popularity_item.css.scss +0 -1
  9. data/app/assets/stylesheet/decidim/initiatives/print-initiative.css.scss +0 -3
  10. data/app/cells/decidim/initiatives/content_blocks/highlighted_initiatives/show.erb +4 -3
  11. data/app/cells/decidim/initiatives/initiative_m_cell.rb +25 -0
  12. data/app/cells/decidim/initiatives_votes/vote/show.erb +12 -9
  13. data/app/cells/decidim/initiatives_votes/vote_cell.rb +7 -0
  14. data/app/commands/decidim/initiatives/admin/create_initiative_type.rb +6 -1
  15. data/app/commands/decidim/initiatives/admin/send_initiative_to_technical_validation.rb +17 -0
  16. data/app/commands/decidim/initiatives/admin/update_initiative.rb +29 -10
  17. data/app/commands/decidim/initiatives/admin/update_initiative_type.rb +6 -1
  18. data/app/commands/decidim/initiatives/attachment_methods.rb +37 -0
  19. data/app/commands/decidim/initiatives/create_initiative.rb +25 -3
  20. data/app/commands/decidim/initiatives/unvote_initiative.rb +4 -10
  21. data/app/commands/decidim/initiatives/vote_initiative.rb +47 -31
  22. data/app/controllers/concerns/decidim/initiatives/admin/filterable.rb +51 -0
  23. data/app/controllers/concerns/decidim/initiatives/orderable.rb +3 -1
  24. data/app/controllers/concerns/decidim/initiatives/single_initiative_type.rb +26 -0
  25. data/app/controllers/decidim/initiatives/admin/answers_controller.rb +2 -3
  26. data/app/controllers/decidim/initiatives/admin/initiatives_controller.rb +28 -15
  27. data/app/controllers/decidim/initiatives/create_initiative_controller.rb +36 -6
  28. data/app/controllers/decidim/initiatives/initiative_signatures_controller.rb +19 -23
  29. data/app/controllers/decidim/initiatives/initiative_votes_controller.rb +11 -5
  30. data/app/controllers/decidim/initiatives/initiatives_controller.rb +28 -13
  31. data/app/controllers/decidim/initiatives/initiatives_type_scopes_controller.rb +9 -1
  32. data/app/controllers/decidim/initiatives/versions_controller.rb +20 -0
  33. data/app/controllers/decidim/initiatives/{initiative_widgets_controller.rb → widgets_controller.rb} +2 -2
  34. data/app/events/decidim/initiatives/admin/initiative_sent_to_technical_validation_event.rb +21 -0
  35. data/app/events/decidim/initiatives/admin/support_threshold_reached_event.rb +13 -0
  36. data/app/forms/decidim/initiatives/admin/initiative_form.rb +49 -7
  37. data/app/forms/decidim/initiatives/admin/initiative_type_form.rb +8 -1
  38. data/app/forms/decidim/initiatives/initiative_form.rb +56 -1
  39. data/app/forms/decidim/initiatives/vote_form.rb +133 -76
  40. data/app/helpers/decidim/initiatives/application_helper.rb +104 -0
  41. data/app/helpers/decidim/initiatives/initiative_helper.rb +13 -0
  42. data/app/helpers/decidim/initiatives/initiatives_helper.rb +10 -0
  43. data/app/jobs/decidim/initiatives/export_initiatives_job.rb +25 -0
  44. data/app/mailers/decidim/initiatives/initiatives_mailer.rb +0 -21
  45. data/app/models/concerns/decidim/initiatives/has_area.rb +30 -0
  46. data/app/models/decidim/initiative.rb +184 -44
  47. data/app/models/decidim/initiatives_type.rb +5 -2
  48. data/app/models/decidim/initiatives_type_scope.rb +5 -1
  49. data/app/models/decidim/initiatives_vote.rb +19 -23
  50. data/app/permissions/decidim/initiatives/admin/permissions.rb +19 -8
  51. data/app/permissions/decidim/initiatives/permissions.rb +37 -14
  52. data/app/presenters/decidim/initiatives/initiative_stats_presenter.rb +1 -5
  53. data/app/queries/decidim/initiatives/admin/manageable_initiatives.rb +7 -39
  54. data/app/serializers/decidim/initiatives/initiative_serializer.rb +32 -0
  55. data/app/services/decidim/initiatives/diff_renderer.rb +18 -0
  56. data/app/services/decidim/initiatives/initiative_search.rb +59 -15
  57. data/app/services/decidim/initiatives/status_change_notifier.rb +4 -5
  58. data/app/types/decidim/initiatives/initiative_api_type.rb +26 -0
  59. data/app/types/decidim/initiatives/initiative_committee_member_type.rb +18 -0
  60. data/app/types/decidim/initiatives/initiative_type.rb +42 -0
  61. data/app/views/decidim/initiatives/admin/answers/_info_initiative.html.erb +1 -1
  62. data/app/views/decidim/initiatives/admin/exports/_dropdown.html.erb +8 -0
  63. data/app/views/decidim/initiatives/admin/initiatives/_form.html.erb +59 -14
  64. data/app/views/decidim/initiatives/admin/initiatives/_initiative_attachments.erb +43 -0
  65. data/app/views/decidim/initiatives/admin/initiatives/export_pdf_signatures.pdf.erb +12 -9
  66. data/app/views/decidim/initiatives/admin/initiatives/index.html.erb +10 -45
  67. data/app/views/decidim/initiatives/admin/initiatives_types/_form.html.erb +47 -10
  68. data/app/views/decidim/initiatives/admin/initiatives_types/_initiative_type_scopes.html.erb +28 -25
  69. data/app/views/decidim/initiatives/create_initiative/fill_data.html.erb +46 -10
  70. data/app/views/decidim/initiatives/create_initiative/finish.html.erb +17 -10
  71. data/app/views/decidim/initiatives/create_initiative/previous_form.html.erb +2 -1
  72. data/app/views/decidim/initiatives/create_initiative/promotal_committee.html.erb +1 -1
  73. data/app/views/decidim/initiatives/create_initiative/select_initiative_type.html.erb +1 -2
  74. data/app/views/decidim/initiatives/create_initiative/show_similar_initiatives.html.erb +1 -1
  75. data/app/views/decidim/initiatives/initiative_signatures/fill_personal_data.html.erb +1 -1
  76. data/app/views/decidim/initiatives/initiative_signatures/update_buttons_and_counters.js.erb +1 -1
  77. data/app/views/decidim/initiatives/initiative_votes/update_buttons_and_counters.js.erb +1 -1
  78. data/app/views/decidim/initiatives/initiatives/_author.html.erb +1 -1
  79. data/app/views/decidim/initiatives/initiatives/_filters.html.erb +16 -28
  80. data/app/views/decidim/initiatives/initiatives/_index_header.html.erb +39 -5
  81. data/app/views/decidim/initiatives/initiatives/_initiatives.html.erb +11 -1
  82. data/app/views/decidim/initiatives/initiatives/_interactions.html.erb +2 -3
  83. data/app/views/decidim/initiatives/initiatives/_progress_bar.html.erb +24 -9
  84. data/app/views/decidim/initiatives/initiatives/_tags.html.erb +3 -0
  85. data/app/views/decidim/initiatives/initiatives/_vote_cabin.html.erb +1 -13
  86. data/app/views/decidim/initiatives/initiatives/index.html.erb +1 -1
  87. data/app/views/decidim/initiatives/initiatives/show.html.erb +2 -3
  88. data/app/views/decidim/initiatives/versions/index.html.erb +8 -0
  89. data/app/views/decidim/initiatives/versions/show.html.erb +10 -0
  90. data/app/views/layouts/decidim/_initiative_creation_header.html.erb +2 -1
  91. data/app/views/layouts/decidim/_initiative_header.html.erb +2 -1
  92. data/app/views/layouts/decidim/_initiative_signature_creation_header.html.erb +1 -1
  93. data/app/views/layouts/decidim/initiative.html.erb +1 -0
  94. data/app/views/layouts/decidim/initiative_creation.html.erb +1 -2
  95. data/app/views/layouts/decidim/initiative_signature_creation.html.erb +2 -2
  96. data/config/locales/am-ET.yml +1 -0
  97. data/config/locales/ar.yml +12 -21
  98. data/config/locales/bg-BG.yml +13 -0
  99. data/config/locales/bg.yml +13 -0
  100. data/config/locales/ca.yml +99 -21
  101. data/config/locales/cs.yml +110 -32
  102. data/config/locales/da-DK.yml +1 -0
  103. data/config/locales/da.yml +1 -0
  104. data/config/locales/de.yml +75 -21
  105. data/config/locales/el.yml +525 -0
  106. data/config/locales/en.yml +102 -24
  107. data/config/locales/eo.yml +1 -0
  108. data/config/locales/es-MX.yml +99 -21
  109. data/config/locales/es-PY.yml +99 -21
  110. data/config/locales/es.yml +103 -25
  111. data/config/locales/et-EE.yml +1 -0
  112. data/config/locales/et.yml +1 -0
  113. data/config/locales/eu.yml +4 -21
  114. data/config/locales/fi-plain.yml +99 -21
  115. data/config/locales/fi.yml +117 -39
  116. data/config/locales/fr-CA.yml +557 -0
  117. data/config/locales/fr.yml +101 -23
  118. data/config/locales/ga-IE.yml +1 -0
  119. data/config/locales/gl.yml +4 -21
  120. data/config/locales/hr-HR.yml +1 -0
  121. data/config/locales/hr.yml +1 -0
  122. data/config/locales/hu.yml +27 -21
  123. data/config/locales/id-ID.yml +4 -21
  124. data/config/locales/is-IS.yml +4 -17
  125. data/config/locales/is.yml +251 -0
  126. data/config/locales/it.yml +116 -61
  127. data/config/locales/ja-JP.yml +529 -0
  128. data/config/locales/ja.yml +549 -0
  129. data/config/locales/ko-KR.yml +1 -0
  130. data/config/locales/ko.yml +1 -0
  131. data/config/locales/lt-LT.yml +1 -0
  132. data/config/locales/lt.yml +1 -0
  133. data/config/locales/lv.yml +525 -0
  134. data/config/locales/mt-MT.yml +1 -0
  135. data/config/locales/mt.yml +1 -0
  136. data/config/locales/nl.yml +76 -21
  137. data/config/locales/no.yml +62 -31
  138. data/config/locales/om-ET.yml +1 -0
  139. data/config/locales/pl.yml +260 -189
  140. data/config/locales/pt-BR.yml +5 -22
  141. data/config/locales/pt.yml +231 -179
  142. data/config/locales/ro-RO.yml +533 -0
  143. data/config/locales/ru.yml +4 -21
  144. data/config/locales/sk-SK.yml +468 -0
  145. data/config/locales/sk.yml +458 -0
  146. data/config/locales/sl.yml +24 -0
  147. data/config/locales/so-SO.yml +1 -0
  148. data/config/locales/sr-CS.yml +8 -0
  149. data/config/locales/sv.yml +102 -31
  150. data/config/locales/ti-ER.yml +1 -0
  151. data/config/locales/tr-TR.yml +4 -21
  152. data/config/locales/uk.yml +4 -21
  153. data/config/locales/vi-VN.yml +1 -0
  154. data/config/locales/vi.yml +1 -0
  155. data/config/locales/zh-CN.yml +549 -0
  156. data/config/locales/zh-TW.yml +1 -0
  157. data/db/migrate/20191106144259_add_settings_to_initiatives_types.rb +8 -0
  158. data/db/migrate/20191107134847_add_scopes_to_initiatives_votes.rb +28 -0
  159. data/db/migrate/20191116170841_allow_multiple_initiative_votes_counter_caches.rb +32 -0
  160. data/db/migrate/20191118105634_allow_multiple_offline_votes.rb +34 -0
  161. data/db/migrate/20200320105920_index_foreign_keys_in_decidim_initiatives.rb +8 -0
  162. data/db/migrate/20200320105921_index_foreign_keys_in_decidim_initiatives_votes.rb +8 -0
  163. data/db/migrate/20200417120551_add_custom_signature_end_time_option.rb +7 -0
  164. data/db/migrate/20200424110930_add_attachments_enabled_option.rb +7 -0
  165. data/db/migrate/20200514085422_add_area_to_initiatives.rb +7 -0
  166. data/db/migrate/20200514102631_add_area_enabled_option_to_initiatives.rb +7 -0
  167. data/db/migrate/20200528151456_remove_user_groups_from_initiative_votes.rb +7 -0
  168. data/db/migrate/20200827154214_add_commentable_counter_cache_to_initiatives.rb +9 -0
  169. data/db/seeds/city.jpeg +0 -0
  170. data/db/seeds/city2.jpeg +0 -0
  171. data/lib/decidim/api/initiative_type_interface.rb +13 -0
  172. data/lib/decidim/initiatives/admin_engine.rb +5 -0
  173. data/lib/decidim/initiatives/api.rb +7 -0
  174. data/lib/decidim/initiatives/engine.rb +11 -2
  175. data/lib/decidim/initiatives/participatory_space.rb +18 -1
  176. data/lib/decidim/initiatives/query_extensions.rb +40 -0
  177. data/lib/decidim/initiatives/test/factories.rb +51 -4
  178. data/lib/decidim/initiatives/version.rb +1 -1
  179. data/lib/tasks/decidim_initiatives.rake +1 -3
  180. metadata +92 -21
  181. data/app/views/decidim/initiatives/initiative_widgets/show.html.erb +0 -4
  182. data/app/views/decidim/initiatives/initiatives/_supports.html.erb +0 -22
  183. data/app/views/decidim/initiatives/initiatives/signature_identities.html.erb +0 -42
  184. data/app/views/decidim/initiatives/initiatives_mailer/notify_validating_request.html.erb +0 -3
@@ -4,6 +4,9 @@ module Decidim
4
4
  # Initiative type.
5
5
  class InitiativesType < ApplicationRecord
6
6
  include Decidim::HasResourcePermission
7
+ include Decidim::TranslatableResource
8
+
9
+ translatable_fields :title, :description, :extra_fields_legal_information
7
10
 
8
11
  belongs_to :organization,
9
12
  foreign_key: "decidim_organization_id",
@@ -21,8 +24,8 @@ module Decidim
21
24
 
22
25
  enum signature_type: [:online, :offline, :any], _suffix: true
23
26
 
24
- validates :signature_type, presence: true
25
- validates :title, :description, presence: true
27
+ validates :title, :description, :signature_type, presence: true
28
+ validates :document_number_authorization_handler, presence: true, if: ->(form) { form.collect_user_extra_fields? }
26
29
 
27
30
  mount_uploader :banner_image, Decidim::BannerImageUploader
28
31
 
@@ -25,8 +25,12 @@ module Decidim
25
25
  greater_than: 0
26
26
  }
27
27
 
28
+ def global_scope?
29
+ decidim_scopes_id.nil?
30
+ end
31
+
28
32
  def scope_name
29
- return { I18n.locale.to_s => I18n.t("decidim.scopes.global") } if decidim_scopes_id.nil?
33
+ return { I18n.locale.to_s => I18n.t("decidim.scopes.global") } if global_scope?
30
34
 
31
35
  scope&.name.presence || { I18n.locale.to_s => I18n.t("decidim.initiatives.unavailable_scope") }
32
36
  end
@@ -11,37 +11,43 @@ module Decidim
11
11
  foreign_key: "decidim_author_id",
12
12
  class_name: "Decidim::User"
13
13
 
14
- belongs_to :user_group,
15
- foreign_key: "decidim_user_group_id",
16
- class_name: "Decidim::UserGroup",
17
- optional: true
18
-
19
14
  belongs_to :initiative,
20
15
  foreign_key: "decidim_initiative_id",
21
16
  class_name: "Decidim::Initiative",
22
17
  inverse_of: :votes
23
18
 
24
- validates :initiative, uniqueness: { scope: [:author, :user_group] }
19
+ belongs_to :scope,
20
+ foreign_key: "decidim_scope_id",
21
+ class_name: "Decidim::Scope",
22
+ optional: true
23
+
24
+ validates :initiative, uniqueness: { scope: [:author, :scope] }
25
+ validates :initiative, uniqueness: { scope: [:hash_id, :scope] }
25
26
 
26
27
  after_commit :update_counter_cache, on: [:create, :destroy]
27
28
 
28
- scope :supports, -> { where.not(decidim_user_group_id: nil) }
29
- scope :votes, -> { where(decidim_user_group_id: nil) }
29
+ scope :for_scope, ->(scope) { where(scope: scope) }
30
30
 
31
- # PUBLIC
31
+ # Public: Generates a hashed representation of the initiative support.
32
32
  #
33
- # Generates a hashed representation of the initiative support.
33
+ # Used when exporting the votes as CSV.
34
34
  def sha1
35
- return unless decidim_user_group_id.nil?
36
-
37
35
  title = translated_attribute(initiative.title)
38
36
  description = translated_attribute(initiative.description)
39
37
 
40
38
  Digest::SHA1.hexdigest "#{authorization_unique_id}#{title}#{description}"
41
39
  end
42
40
 
41
+ def decrypted_metadata
42
+ @decrypted_metadata ||= encrypted_metadata ? encryptor.decrypt(encrypted_metadata) : {}
43
+ end
44
+
43
45
  private
44
46
 
47
+ def encryptor
48
+ @encryptor ||= Decidim::Initiatives::DataEncryptor.new(secret: "personal user metadata")
49
+ end
50
+
45
51
  def authorization_unique_id
46
52
  first_authorization = Decidim::Initiatives::UserAuthorizations
47
53
  .for(author)
@@ -51,17 +57,7 @@ module Decidim
51
57
  end
52
58
 
53
59
  def update_counter_cache
54
- initiative.initiative_votes_count = Decidim::InitiativesVote
55
- .votes
56
- .where(decidim_initiative_id: initiative.id)
57
- .count
58
-
59
- initiative.initiative_supports_count = Decidim::InitiativesVote
60
- .supports
61
- .where(decidim_initiative_id: initiative.id)
62
- .count
63
-
64
- initiative.save
60
+ initiative.update_online_votes_counters
65
61
  end
66
62
  end
67
63
  end
@@ -35,6 +35,7 @@ module Decidim
35
35
  initiative_type_scope_action?
36
36
  initiative_committee_action?
37
37
  initiative_admin_user_action?
38
+ initiative_export_action?
38
39
  moderator_action?
39
40
  allow! if permission_action.subject == :attachment
40
41
 
@@ -69,6 +70,8 @@ module Decidim
69
70
  def attachment_action?
70
71
  return unless permission_action.subject == :attachment
71
72
 
73
+ disallow! && return unless initiative.attachments_enabled?
74
+
72
75
  attachment = context.fetch(:attachment, nil)
73
76
  attached = attachment&.attached_to
74
77
 
@@ -144,18 +147,24 @@ module Decidim
144
147
  when :accept
145
148
  allowed = initiative.published? &&
146
149
  initiative.signature_end_date < Date.current &&
147
- initiative.percentage >= 100
150
+ initiative.supports_goal_reached?
148
151
  toggle_allow(allowed)
149
152
  when :reject
150
153
  allowed = initiative.published? &&
151
154
  initiative.signature_end_date < Date.current &&
152
- initiative.percentage < 100
155
+ !initiative.supports_goal_reached?
153
156
  toggle_allow(allowed)
157
+ when :send_to_technical_validation
158
+ toggle_allow(allowed_to_send_to_technical_validation?)
154
159
  else
155
160
  allow!
156
161
  end
157
162
  end
158
163
 
164
+ def initiative_export_action?
165
+ allow! if permission_action.subject == :initiatives && permission_action.action == :export
166
+ end
167
+
159
168
  def moderator_action?
160
169
  return unless permission_action.subject == :moderation
161
170
 
@@ -180,18 +189,20 @@ module Decidim
180
189
  when :update
181
190
  toggle_allow(initiative.created?)
182
191
  when :send_to_technical_validation
183
- allowed = initiative.created? && (
184
- !initiative.created_by_individual? ||
185
- initiative.enough_committee_members?
186
- )
187
-
188
- toggle_allow(allowed)
192
+ toggle_allow(allowed_to_send_to_technical_validation?)
189
193
  when :manage_membership
190
194
  toggle_allow(initiative.promoting_committee_enabled?)
191
195
  else
192
196
  disallow!
193
197
  end
194
198
  end
199
+
200
+ def allowed_to_send_to_technical_validation?
201
+ initiative.created? && (
202
+ !initiative.created_by_individual? ||
203
+ initiative.enough_committee_members?
204
+ )
205
+ end
195
206
  end
196
207
  end
197
208
  end
@@ -18,16 +18,18 @@ module Decidim
18
18
  list_public_initiatives?
19
19
  read_public_initiative?
20
20
  search_initiative_types_and_scopes?
21
+ request_membership?
21
22
 
22
23
  return permission_action unless user
23
24
 
24
25
  create_initiative?
25
- request_membership?
26
26
 
27
27
  vote_initiative?
28
28
  sign_initiative?
29
29
  unvote_initiative?
30
30
 
31
+ initiative_attachment?
32
+
31
33
  permission_action
32
34
  end
33
35
 
@@ -37,6 +39,10 @@ module Decidim
37
39
  @initiative ||= context.fetch(:initiative, nil) || context.fetch(:current_participatory_space, nil)
38
40
  end
39
41
 
42
+ def initiative_type
43
+ @initiative_type ||= context[:initiative_type]
44
+ end
45
+
40
46
  def list_public_initiatives?
41
47
  allow! if permission_action.subject == :initiative &&
42
48
  permission_action.action == :list
@@ -78,16 +84,28 @@ module Decidim
78
84
  return unless permission_action.subject == :initiative &&
79
85
  permission_action.action == :request_membership
80
86
 
81
- can_request = !initiative.published? &&
82
- initiative.promoting_committee_enabled? &&
83
- !initiative.has_authorship?(user) &&
84
- (
85
- Decidim::Initiatives.do_not_require_authorization ||
86
- UserAuthorizations.for(user).any? ||
87
- Decidim::UserGroups::ManageableUserGroups.for(user).verified.any?
88
- )
87
+ toggle_allow(can_request_membership?)
88
+ end
89
+
90
+ def can_request_membership?
91
+ return access_request_without_user? if user.blank?
92
+
93
+ access_request_membership?
94
+ end
89
95
 
90
- toggle_allow(can_request)
96
+ def access_request_without_user?
97
+ !initiative.published? && initiative.promoting_committee_enabled? || Decidim::Initiatives.do_not_require_authorization
98
+ end
99
+
100
+ def access_request_membership?
101
+ !initiative.published? &&
102
+ initiative.promoting_committee_enabled? &&
103
+ !initiative.has_authorship?(user) &&
104
+ (
105
+ Decidim::Initiatives.do_not_require_authorization ||
106
+ UserAuthorizations.for(user).any? ||
107
+ Decidim::UserGroups::ManageableUserGroups.for(user).verified.any?
108
+ )
91
109
  end
92
110
 
93
111
  def has_initiatives?
@@ -124,13 +142,19 @@ module Decidim
124
142
 
125
143
  can_unvote = initiative.accepts_online_unvotes? &&
126
144
  initiative.organization&.id == user.organization&.id &&
127
- initiative.votes.where(decidim_author_id: user.id, decidim_user_group_id: decidim_user_group_id).any? &&
128
- (can_user_support?(initiative) || Decidim::UserGroups::ManageableUserGroups.for(user).verified.any?) &&
145
+ initiative.votes.where(author: user).any? &&
129
146
  authorized?(:vote, resource: initiative, permissions_holder: initiative.type)
130
147
 
131
148
  toggle_allow(can_unvote)
132
149
  end
133
150
 
151
+ def initiative_attachment?
152
+ return unless permission_action.action == :add_attachment &&
153
+ permission_action.subject == :initiative
154
+
155
+ toggle_allow(initiative_type.attachments_enabled?)
156
+ end
157
+
134
158
  def public_report_content_action?
135
159
  return unless permission_action.action == :create &&
136
160
  permission_action.subject == :moderation
@@ -155,8 +179,7 @@ module Decidim
155
179
  def can_vote?
156
180
  initiative.votes_enabled? &&
157
181
  initiative.organization&.id == user.organization&.id &&
158
- initiative.votes.where(decidim_author_id: user.id, decidim_user_group_id: decidim_user_group_id).empty? &&
159
- (can_user_support?(initiative) || Decidim::UserGroups::ManageableUserGroups.for(user).verified.any?) &&
182
+ initiative.votes.where(author: user).empty? &&
160
183
  authorized?(:vote, resource: initiative, permissions_holder: initiative.type)
161
184
  end
162
185
 
@@ -6,16 +6,12 @@ module Decidim
6
6
  class InitiativeStatsPresenter < Rectify::Presenter
7
7
  attribute :initiative, Decidim::Initiative
8
8
 
9
- def supports_count
10
- initiative.initiative_supports_count
11
- end
12
-
13
9
  def comments_count
14
10
  Rails.cache.fetch(
15
11
  "initiative/#{initiative.id}/comments_count",
16
12
  expires_in: Decidim::Initiatives.stats_cache_expiration_time
17
13
  ) do
18
- Decidim::Comments::Comment.where(root_commentable: initiative).count
14
+ initiative.comments_count
19
15
  end
20
16
  end
21
17
 
@@ -7,57 +7,25 @@ module Decidim
7
7
  # Regular users will get only their initiatives. Administrators will
8
8
  # retrieve all initiatives.
9
9
  class ManageableInitiatives < Rectify::Query
10
- attr_reader :organization, :user, :q, :state
11
-
12
10
  # Syntactic sugar to initialize the class and return the queried objects
13
11
  #
14
- # organization - Decidim::Organization
15
- # user - Decidim::User
16
- # query - String
17
- # state - String
18
- def self.for(organization, user, query, state)
19
- new(organization, user, query, state).query
12
+ # user - Decidim::User
13
+ def self.for(user)
14
+ new(user).query
20
15
  end
21
16
 
22
17
  # Initializes the class.
23
18
  #
24
- # organization - Decidim::Organization
25
- # user - Decidim::User
26
- # query - String
27
- # state - String
28
- def initialize(organization, user, query, state)
29
- @organization = organization
19
+ # user - Decidim::User
20
+ def initialize(user)
30
21
  @user = user
31
- @q = query
32
- @state = state
33
22
  end
34
23
 
35
24
  # Retrieves all initiatives / Initiatives created by the user.
36
25
  def query
37
- if user.admin?
38
- base = Initiative
39
- .where(organization: organization)
40
- .with_state(state)
41
- else
42
- ids = InitiativesCreated.by(user).with_state(state).pluck(:id)
43
- ids += InitiativesPromoted.by(user).with_state(state).pluck(:id)
44
- base = Initiative.where(id: ids)
45
- end
46
-
47
- return base if q.blank?
48
-
49
- organization.available_locales.each_with_index do |loc, index|
50
- base = if index.zero?
51
- base.where("title->>? ilike ?", loc, "%#{q}%")
52
- .or(Initiative.where("description->>? ilike ?", loc, "%#{q}%"))
53
- else
54
- base
55
- .or(Initiative.where("title->>? ilike ?", loc, "%#{q}%"))
56
- .or(Initiative.where("description->>? ilike ?", loc, "%#{q}%"))
57
- end
58
- end
26
+ return Initiative.where(organization: @user.organization) if @user.admin?
59
27
 
60
- base
28
+ Initiative.where(id: InitiativesCreated.by(@user) + InitiativesPromoted.by(@user))
61
29
  end
62
30
  end
63
31
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ class InitiativeSerializer < Decidim::Exporters::Serializer
6
+ # Serializes an inititative
7
+ def serialize
8
+ {
9
+ id: resource.id,
10
+ title: resource.title,
11
+ description: resource.description,
12
+ state: resource.state,
13
+ created_at: resource.created_at,
14
+ published_at: resource.published_at,
15
+ signature_end_date: resource.signature_end_date,
16
+ signature_type: resource.signature_type,
17
+ signatures: resource.supports_count,
18
+ scope: {
19
+ name: resource.scope&.name
20
+ },
21
+ type: {
22
+ title: resource.type&.title
23
+ },
24
+ authors: {
25
+ id: resource.author_users.map(&:id),
26
+ name: resource.author_users.map(&:name)
27
+ }
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ class DiffRenderer < BaseDiffRenderer
6
+ private
7
+
8
+ # Lists which attributes will be diffable and how they should be rendered.
9
+ def attribute_types
10
+ {
11
+ description: :i18n_html,
12
+ title: :i18n,
13
+ state: :string
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -16,7 +16,9 @@ module Decidim
16
16
  def base_query
17
17
  Decidim::Initiative
18
18
  .includes(scoped_type: [:scope])
19
+ .joins("JOIN decidim_users ON decidim_users.id = decidim_initiatives.decidim_author_id")
19
20
  .where(organization: options[:organization])
21
+ .published
20
22
  end
21
23
 
22
24
  # Handle the search_text filter
@@ -29,22 +31,38 @@ module Decidim
29
31
  "%#{search_text}%"
30
32
  )
31
33
  )
34
+ .or(
35
+ query.where(
36
+ "cast(decidim_initiatives.id as text) ILIKE ?", "%#{search_text}%"
37
+ )
38
+ )
39
+ .or(
40
+ query.where(
41
+ "decidim_users.name ILIKE ? OR decidim_users.nickname ILIKE ?", "%#{search_text}%", "%#{search_text}%"
42
+ )
43
+ )
32
44
  end
33
45
 
34
46
  # Handle the state filter
35
47
  def search_state
36
- case state
37
- when "closed"
38
- query.closed
39
- else # Assume open
40
- query.open
41
- end
48
+ accepted = state.member?("accepted") ? query.accepted : nil
49
+ rejected = state.member?("rejected") ? query.rejected : nil
50
+ answered = state.member?("answered") ? query.answered : nil
51
+ open = state.member?("open") ? query.open : nil
52
+ closed = state.member?("closed") ? query.closed : nil
53
+
54
+ query
55
+ .where(id: accepted)
56
+ .or(query.where(id: rejected))
57
+ .or(query.where(id: answered))
58
+ .or(query.where(id: open))
59
+ .or(query.where(id: closed))
42
60
  end
43
61
 
44
- def search_type
45
- return query if type == "all"
62
+ def search_type_id
63
+ return query if type_ids.include?("all")
46
64
 
47
- types = InitiativesTypeScope.where(decidim_initiatives_types_id: type).pluck(:id)
65
+ types = InitiativesTypeScope.where(decidim_initiatives_types_id: type_ids).pluck(:id)
48
66
 
49
67
  query.where(scoped_type: types)
50
68
  end
@@ -58,13 +76,39 @@ module Decidim
58
76
  end
59
77
 
60
78
  def search_scope_id
61
- return if scope_id.nil?
79
+ return query if scope_ids.include?("all")
62
80
 
63
- query
64
- .joins(:scoped_type)
65
- .where(
66
- "decidim_initiatives_type_scopes.decidim_scopes_id": scope_id
67
- )
81
+ clean_scope_ids = scope_ids
82
+
83
+ conditions = []
84
+ conditions << "decidim_initiatives_type_scopes.decidim_scopes_id IS NULL" if clean_scope_ids.delete("global")
85
+ conditions.concat(["? = ANY(decidim_scopes.part_of)"] * clean_scope_ids.count) if clean_scope_ids.any?
86
+
87
+ query.joins(:scoped_type).references(:decidim_scopes).where(conditions.join(" OR "), *clean_scope_ids.map(&:to_i))
88
+ end
89
+
90
+ def search_area_id
91
+ return query if area_ids.include?("all")
92
+
93
+ query.where(decidim_area_id: area_ids)
94
+ end
95
+
96
+ private
97
+
98
+ # Private: Returns an array with checked type ids.
99
+ def type_ids
100
+ [type_id].flatten
101
+ end
102
+
103
+ # Private: Returns an array with checked scope ids.
104
+ def scope_ids
105
+ [scope_id].flatten
106
+ end
107
+
108
+ # Private: Returns an array with checked area ids, handling area_types which are coded as its
109
+ # areas ids joined by _.
110
+ def area_ids
111
+ area_id.map { |id| id.split("_") }.flatten.uniq
68
112
  end
69
113
  end
70
114
  end