decidim-initiatives 0.20.1 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/decidim/initiatives/admin/filterable.rb +37 -0
  3. data/app/controllers/decidim/initiatives/admin/initiatives_controller.rb +6 -12
  4. data/app/models/decidim/initiative.rb +18 -1
  5. data/app/permissions/decidim/initiatives/admin/permissions.rb +10 -6
  6. data/app/queries/decidim/initiatives/admin/manageable_initiatives.rb +7 -39
  7. data/app/types/decidim/initiatives/initiative_api_type.rb +26 -0
  8. data/app/types/decidim/initiatives/initiative_committee_member_type.rb +18 -0
  9. data/app/types/decidim/initiatives/initiative_type.rb +42 -0
  10. data/app/views/decidim/initiatives/admin/initiatives/index.html.erb +3 -43
  11. data/config/locales/ar.yml +12 -10
  12. data/config/locales/ca.yml +12 -10
  13. data/config/locales/cs.yml +14 -12
  14. data/config/locales/de.yml +10 -10
  15. data/config/locales/el.yml +1 -0
  16. data/config/locales/en.yml +12 -10
  17. data/config/locales/es-MX.yml +12 -10
  18. data/config/locales/es-PY.yml +12 -10
  19. data/config/locales/es.yml +12 -10
  20. data/config/locales/eu.yml +10 -10
  21. data/config/locales/fi-plain.yml +12 -10
  22. data/config/locales/fi.yml +12 -10
  23. data/config/locales/fr.yml +10 -10
  24. data/config/locales/gl.yml +10 -10
  25. data/config/locales/hu.yml +12 -10
  26. data/config/locales/id-ID.yml +10 -10
  27. data/config/locales/is-IS.yml +10 -10
  28. data/config/locales/it.yml +12 -10
  29. data/config/locales/nl.yml +10 -10
  30. data/config/locales/no.yml +12 -10
  31. data/config/locales/pl.yml +10 -10
  32. data/config/locales/pt-BR.yml +10 -10
  33. data/config/locales/pt.yml +10 -10
  34. data/config/locales/ru.yml +10 -10
  35. data/config/locales/sv.yml +10 -10
  36. data/config/locales/tr-TR.yml +10 -10
  37. data/config/locales/uk.yml +10 -10
  38. data/lib/decidim/api/initiative_type_interface.rb +13 -0
  39. data/lib/decidim/initiatives/api.rb +7 -0
  40. data/lib/decidim/initiatives/engine.rb +8 -0
  41. data/lib/decidim/initiatives/participatory_space.rb +2 -0
  42. data/lib/decidim/initiatives/query_extensions.rb +40 -0
  43. data/lib/decidim/initiatives/version.rb +1 -1
  44. metadata +20 -13
  45. data/app/views/decidim/initiatives/initiative_widgets/show.html.erb +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f700127f2cd4317eebf36ebc437cd3bf9f8db1b887afcf7c42e18d7735f71c1
4
- data.tar.gz: aa39b6b6f75f465c331be6578b36d57a80dc486ed515d809889454ddcf2ad641
3
+ metadata.gz: bb3ddfb0265d59d3529b4855a3b5d195ae04616050f26d2baf9c621bfa165eb6
4
+ data.tar.gz: 4d16e5c1ae4cffa02b3077074bfc8f90ecf31b6a0a1dc6efac25549042dcc919
5
5
  SHA512:
6
- metadata.gz: 5b4bb552d1c3d8aa05eab47f94905f45adf32864d98e7ffbd725b75702fbce0eed8876cc3f9adc8096cb08d66c23d45c58aa8918d63b0ef032f11d67cbcf02ca
7
- data.tar.gz: 5d1c86ec5c714bd81a7186663756d501741fbfe88d54ff454aa11a44510d9f0ccb5f23dcaa0ac18f06d2ae5fcf6d4b14d070f11e47535d9b8c7a485dcfbc19ab
6
+ metadata.gz: 684b21bc414dec69621ae6291c9bb07012495a45c24bc035a0348c08b3478eb70fe587a9cb5f4c68c04c91d24e483850a324ef292d8c2303f10e0f0bb4558075
7
+ data.tar.gz: 7fb407da4a8752b56720500ba69f8d9c691d99821b0a13bfe273d2dda935ad6c68d44cee9439251743dde22b9ba940428dd62564aa086830d7d6d05030b240c6
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Initiatives
7
+ module Admin
8
+ module Filterable
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include Decidim::Admin::Filterable
13
+
14
+ private
15
+
16
+ def base_query
17
+ collection
18
+ end
19
+
20
+ def search_field_predicate
21
+ :title_or_description_cont
22
+ end
23
+
24
+ def filters
25
+ [:state_eq]
26
+ end
27
+
28
+ def filters_with_values
29
+ {
30
+ state_eq: Initiative.states.keys
31
+ }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -9,6 +9,7 @@ module Decidim
9
9
  class InitiativesController < Decidim::Initiatives::Admin::ApplicationController
10
10
  include Decidim::Initiatives::NeedsInitiative
11
11
  include Decidim::Initiatives::TypeSelectorOptions
12
+ include Decidim::Initiatives::Admin::Filterable
12
13
 
13
14
  helper Decidim::Initiatives::InitiativeHelper
14
15
  helper Decidim::Initiatives::CreateInitiativeHelper
@@ -16,18 +17,7 @@ module Decidim
16
17
  # GET /admin/initiatives
17
18
  def index
18
19
  enforce_permission_to :list, :initiative
19
-
20
- @query = params[:q]
21
- @state = params[:state]
22
- @initiatives = ManageableInitiatives
23
- .for(
24
- current_organization,
25
- current_user,
26
- @query,
27
- @state
28
- )
29
- .page(params[:page])
30
- .per(15)
20
+ @initiatives = filtered_collection
31
21
  end
32
22
 
33
23
  # GET /admin/initiatives/:id
@@ -165,6 +155,10 @@ module Decidim
165
155
 
166
156
  private
167
157
 
158
+ def collection
159
+ @collection ||= ManageableInitiatives.for(current_user)
160
+ end
161
+
168
162
  def pdf_signature_service
169
163
  @pdf_signature_service ||= Decidim.pdf_signature_service.to_s.safe_constantize
170
164
  end
@@ -68,7 +68,7 @@ module Decidim
68
68
 
69
69
  scope :open, lambda {
70
70
  published
71
- .where.not(state: [:discarded, :rejected, :accepted])
71
+ .where.not(state: [:discarded, :rejected, :accepted, :created])
72
72
  .where("signature_start_date <= ?", Date.current)
73
73
  .where("signature_end_date >= ?", Date.current)
74
74
  }
@@ -332,6 +332,13 @@ module Decidim
332
332
  organization.available_authorizations.include?("sms") && type.validate_sms_code_on_votes?
333
333
  end
334
334
 
335
+ # Public: Returns an empty object. This method should be implemented by
336
+ # `ParticipatorySpaceResourceable`, but for some reason this model does not
337
+ # implement this interface.
338
+ def user_role_config_for(_user, _role_name)
339
+ Decidim::ParticipatorySpaceRoleConfig::Base.new(:empty_role_name)
340
+ end
341
+
335
342
  private
336
343
 
337
344
  def signature_type_allowed
@@ -351,5 +358,15 @@ module Decidim
351
358
  notifier = Decidim::Initiatives::StatusChangeNotifier.new(initiative: self)
352
359
  notifier.notify
353
360
  end
361
+
362
+ # Allow ransacker to search for a key in a hstore column (`title`.`en`)
363
+ [:title, :description].each do |column|
364
+ ransacker column do |parent|
365
+ Arel::Nodes::InfixOperation.new("->>", parent.table[column], Arel::Nodes.build_quoted(I18n.locale.to_s))
366
+ end
367
+ end
368
+
369
+ # Allow ransacker to search on an Enum Field
370
+ ransacker :state, formatter: proc { |int| states[int] }
354
371
  end
355
372
  end
@@ -151,6 +151,8 @@ module Decidim
151
151
  initiative.signature_end_date < Date.current &&
152
152
  initiative.percentage < 100
153
153
  toggle_allow(allowed)
154
+ when :send_to_technical_validation
155
+ toggle_allow(allowed_to_send_to_technical_validation?)
154
156
  else
155
157
  allow!
156
158
  end
@@ -180,18 +182,20 @@ module Decidim
180
182
  when :update
181
183
  toggle_allow(initiative.created?)
182
184
  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)
185
+ toggle_allow(allowed_to_send_to_technical_validation?)
189
186
  when :manage_membership
190
187
  toggle_allow(initiative.promoting_committee_enabled?)
191
188
  else
192
189
  disallow!
193
190
  end
194
191
  end
192
+
193
+ def allowed_to_send_to_technical_validation?
194
+ initiative.created? && (
195
+ !initiative.created_by_individual? ||
196
+ initiative.enough_committee_members?
197
+ )
198
+ end
195
199
  end
196
200
  end
197
201
  end
@@ -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,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ InitiativeApiType = GraphQL::ObjectType.define do
6
+ name "InitiativeType"
7
+ description "An initiative type"
8
+
9
+ field :id, !types.ID, "The internal ID for this initiative type"
10
+ field :title, Decidim::Core::TranslatedFieldType, "Initiative type name"
11
+ field :description, Decidim::Core::TranslatedFieldType, "This is the initiative type description"
12
+ field :createdAt, Decidim::Core::DateTimeType, "The date this initiative type was created", property: :created_at
13
+ field :updatedAt, Decidim::Core::DateTimeType, "The date this initiative type was updated", property: :updated_at
14
+ field :bannerImage, types.String, "Banner image", property: :banner_image
15
+ field :collectUserExtraFields, types.Boolean, "Collect participant personal data on signature", property: :collect_user_extra_fields
16
+ field :extraFieldsLegalInformation, types.String, "Legal information about the collection of personal data", property: :extra_fields_legal_information
17
+ field :minimumCommitteeMembers, types.Int, "Minimum of committee members", property: :minimum_committee_members
18
+ field :validateSmsCodeOnVotes, types.Boolean, "Add SMS code validation step to signature process", property: :validate_sms_code_on_votes
19
+ field :undoOnlineSignaturesEnabled, types.Boolean, "Enable participants to undo their online signatures", property: :undo_online_signatures_enabled
20
+ field :promotingComitteeEnabled, types.Boolean, "If promoting committee is enabled", property: :promoting_committee_enabled
21
+ field :signatureType, types.String, "Signature type of the initiative", property: :signature_type
22
+
23
+ field :initiatives, !types[Decidim::Initiatives::InitiativeType], "The initiatives that have this type"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ # This type represents a initiative committee member.
6
+ InitiativeCommitteeMemberType = GraphQL::ObjectType.define do
7
+ name "InitiativeCommitteeMemberType"
8
+ description "A initiative committee member"
9
+
10
+ field :id, !types.ID, "Internal ID for this member of the committee"
11
+ field :user, Decidim::Core::UserType, "The decidim user for this initiative committee member"
12
+
13
+ field :state, types.Int, "Type of the committee member"
14
+ field :createdAt, Decidim::Core::DateTimeType, "The date this initiative committee member was created", property: :created_at
15
+ field :updatedAt, Decidim::Core::DateTimeType, "The date this initiative committee member was updated", property: :updated_at
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ # This type represents a Initiative.
6
+ InitiativeType = GraphQL::ObjectType.define do
7
+ interfaces [
8
+ -> { Decidim::Core::ParticipatorySpaceInterface },
9
+ -> { Decidim::Core::ScopableInterface },
10
+ -> { Decidim::Core::AttachableInterface },
11
+ -> { Decidim::Core::AuthorInterface },
12
+ -> { Decidim::Initiatives::InitiativeTypeInterface }
13
+ ]
14
+
15
+ name "Initiative"
16
+ description "A initiative"
17
+
18
+ field :description, Decidim::Core::TranslatedFieldType, "The description of this initiative."
19
+ field :slug, !types.String
20
+ field :hashtag, types.String, "The hashtag for this initiative"
21
+ field :createdAt, !Decidim::Core::DateTimeType, "The time this initiative was created", property: :created_at
22
+ field :updatedAt, !Decidim::Core::DateTimeType, "The time this initiative was updated", property: :updated_at
23
+ field :publishedAt, !Decidim::Core::DateTimeType, "The time this initiative was published", property: :published_at
24
+ field :reference, !types.String, "Reference prefix for this initiative"
25
+ field :state, types.String, "Current status of the initiative"
26
+ field :signatureType, types.String, "Signature type of the initiative", property: :signature_type
27
+ field :signatureStartDate, !Decidim::Core::DateType, "The signature start date", property: :signature_start_date
28
+ field :signatureEndDate, !Decidim::Core::DateType, "The signature end date", property: :signature_end_date
29
+ field :offlineVotes, types.Int, "The number of offline votes in this initiative", property: :offline_votes
30
+ field :initiativeVotesCount, types.Int, "The number of votes in this initiative", property: :initiative_votes_count
31
+ field :initiativeSupportsCount, types.Int, "The number of supports in this initiative", property: :initiative_supports_count
32
+
33
+ field :author, !Decidim::Core::AuthorInterface, "The initiative author" do
34
+ resolve lambda { |obj, _args, _ctx|
35
+ obj.user_group || obj.author
36
+ }
37
+ end
38
+
39
+ field :committeeMembers, types[Decidim::Initiatives::InitiativeCommitteeMemberType], property: :committee_members
40
+ end
41
+ end
42
+ end
@@ -1,50 +1,10 @@
1
- <div class="filters row">
2
- <div class="column medium-3">
3
- <span class="dropdown-menu-inverted_label"><%= t(".filter_by") %> :</span>
4
- <ul class="dropdown menu dropdown-inverted" data-dropdown-menu data-close-on-click-inside="false">
5
- <li class="is-dropdown-submenu-parent">
6
- <a href="#">
7
- <% if @state.present? %>
8
- <%= t(".filter.#{@state}") %>
9
- <% else %>
10
- <%= t(".filter.all") %>
11
- <% end %>
12
- </a>
13
- <ul class="menu is-dropdown-submenu">
14
- <li><%= link_to t(".filter.created"), url_for(state: "created", q: @query) %></li>
15
- <li><%= link_to t(".filter.validating"), url_for(state: "validating", q: @query) %></li>
16
- <li><%= link_to t(".filter.discarded"), url_for(state: "discarded", q: @query) %></li>
17
- <li><%= link_to t(".filter.published"), url_for(state: "published", q: @query) %></li>
18
- <li><%= link_to t(".filter.rejected"), url_for(state: "rejected", q: @query) %></li>
19
- <li><%= link_to t(".filter.accepted"), url_for(state: "accepted", q: @query) %></li>
20
- <li><%= link_to t(".filter.all"), url_for(q: @query) %></li>
21
- </ul>
22
- </li>
23
- </ul>
24
- </div>
25
- <div class="column medium-4">
26
- <%= form_tag "", method: :get do %>
27
- <div class="filters__search">
28
- <div class="input-group">
29
- <%= search_field_tag :q, @query,label: false, class: "input-group-field", placeholder: t(".search") %>
30
- <%= hidden_field_tag :state, @state %>
31
- <div class="input-group-button">
32
- <button type="submit" class="button button--muted">
33
- <%= icon "magnifying-glass", aria_label: t(".search") %>
34
- </button>
35
- </div>
36
- </div>
37
- </div>
38
- <% end %>
39
- </div>
40
- </div>
41
-
42
- <div class="card" id="initiatives">
1
+ <div class="card with-overflow" id="initiatives">
43
2
  <div class="card-divider">
44
3
  <h2 class="card-title">
45
4
  <%= t "decidim.admin.titles.initiatives" %>
46
5
  </h2>
47
6
  </div>
7
+ <%= admin_filter_selector %>
48
8
  <div class="card-section">
49
9
  <div class="table-scroll">
50
10
  <table class="table-list">
@@ -54,7 +14,7 @@
54
14
  <th><%= t("models.initiatives.fields.title", scope: "decidim.admin") %></th>
55
15
  <th><%= t("models.initiatives.fields.state", scope: "decidim.admin") %></th>
56
16
  <th><%= t("models.initiatives.fields.supports_count", scope: "decidim.admin") %></th>
57
- <th><%= t("models.initiatives.fields.created_at", scope: "decidim.admin") %></th>
17
+ <th><%= sort_link(query, :created_at, t("models.initiatives.fields.created_at", scope: "decidim.admin"), default_order: :desc) %></th>
58
18
  <th class="actions"><%= t ".actions_title" %></th>
59
19
  </tr>
60
20
  </thead>
@@ -73,6 +73,18 @@ ar:
73
73
  admin:
74
74
  actions:
75
75
  new_initiative_type: نوع المبادرة الجديدة
76
+ filters:
77
+ search_placeholder:
78
+ title_or_description_cont: البحث عن %{collection} بحسب العنوان أو الوصف.
79
+ state_eq:
80
+ label: الحالة
81
+ values:
82
+ accepted: وافقت
83
+ created: تمّ إنشاؤه
84
+ discarded: التخلص منها
85
+ published: نشرت
86
+ rejected: مرفوض
87
+ validating: المصادقة الفنية
76
88
  menu:
77
89
  initiatives: المبادرات
78
90
  initiatives_types: أنواع المبادرة
@@ -188,18 +200,8 @@ ar:
188
200
  title: معلومات عامة
189
201
  index:
190
202
  actions_title: عمل
191
- filter:
192
- accepted: وافقت
193
- all: الكل
194
- created: تمّ إنشاؤه
195
- discarded: التخلص منها
196
- published: نشرت
197
- rejected: مرفوض
198
- validating: المصادقة الفنية
199
- filter_by: مصنف بواسطة
200
203
  preview: معاينة
201
204
  print: طباعة
202
- search: بحث
203
205
  show:
204
206
  print: طباعة
205
207
  update:
@@ -61,6 +61,18 @@ ca:
61
61
  admin:
62
62
  actions:
63
63
  new_initiative_type: Nou tipus d'iniciativa
64
+ filters:
65
+ search_placeholder:
66
+ title_or_description_cont: Cerca %{collection} per títol o descripció.
67
+ state_eq:
68
+ label: Estat
69
+ values:
70
+ accepted: Acceptada
71
+ created: Creada
72
+ discarded: Rebutjada
73
+ published: Publicada
74
+ rejected: Rebutjada
75
+ validating: Validació tècnica
64
76
  menu:
65
77
  initiatives: Iniciatives
66
78
  initiatives_types: Tipus d'iniciatives
@@ -176,18 +188,8 @@ ca:
176
188
  title: Informació general
177
189
  index:
178
190
  actions_title: Acció
179
- filter:
180
- accepted: Acceptada
181
- all: Totes
182
- created: Creada
183
- discarded: Rebutjada
184
- published: Publicada
185
- rejected: Rebutjada
186
- validating: Validació tècnica
187
- filter_by: Filtra per
188
191
  preview: Vista prèvia
189
192
  print: Imprimir
190
- search: Cerca
191
193
  show:
192
194
  print: Imprimir
193
195
  update: