decidim-initiatives 0.30.1 → 0.31.0.rc1

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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +46 -9
  3. data/app/cells/decidim/initiatives/content_blocks/highlighted_initiatives_settings_form/show.erb +7 -2
  4. data/app/cells/decidim/initiatives/initiative_g_cell.rb +5 -1
  5. data/app/commands/decidim/initiatives/admin/publish_initiative.rb +1 -5
  6. data/app/commands/decidim/initiatives/admin/update_initiative.rb +1 -2
  7. data/app/commands/decidim/initiatives/create_initiative.rb +0 -1
  8. data/app/commands/decidim/initiatives/update_initiative.rb +1 -3
  9. data/app/commands/decidim/initiatives/vote_initiative.rb +1 -11
  10. data/app/controllers/concerns/decidim/initiatives/has_signature_workflow.rb +36 -0
  11. data/app/controllers/concerns/decidim/initiatives/needs_initiative.rb +1 -12
  12. data/app/controllers/decidim/initiatives/admin/initiatives_controller.rb +2 -2
  13. data/app/controllers/decidim/initiatives/admin/initiatives_settings_controller.rb +1 -1
  14. data/app/controllers/decidim/initiatives/admin/initiatives_type_scopes_controller.rb +2 -2
  15. data/app/controllers/decidim/initiatives/admin/initiatives_types_controller.rb +2 -2
  16. data/app/controllers/decidim/initiatives/committee_requests_controller.rb +10 -2
  17. data/app/controllers/decidim/initiatives/create_initiative_controller.rb +84 -18
  18. data/app/controllers/decidim/initiatives/initiative_signatures_controller.rb +133 -42
  19. data/app/controllers/decidim/initiatives/initiative_votes_controller.rb +3 -2
  20. data/app/controllers/decidim/initiatives/initiatives_controller.rb +21 -2
  21. data/app/forms/decidim/initiatives/admin/initiative_form.rb +0 -1
  22. data/app/forms/decidim/initiatives/initiative_form.rb +0 -3
  23. data/app/helpers/decidim/initiatives/application_helper.rb +2 -0
  24. data/app/helpers/decidim/initiatives/initiatives_helper.rb +0 -1
  25. data/app/models/decidim/initiative.rb +7 -31
  26. data/app/models/decidim/initiatives_committee_member.rb +1 -1
  27. data/app/models/decidim/initiatives_type.rb +5 -2
  28. data/app/models/decidim/initiatives_vote.rb +2 -2
  29. data/app/packs/entrypoints/decidim_initiatives.js +1 -1
  30. data/app/packs/entrypoints/decidim_initiatives_admin.scss +1 -1
  31. data/app/packs/src/decidim/initiatives/admin/initiatives_types.js +2 -11
  32. data/app/packs/src/decidim/initiatives/admin/invite_users.js +1 -1
  33. data/app/packs/src/decidim/initiatives/application.js +1 -1
  34. data/app/packs/src/decidim/initiatives/check_code.js +114 -0
  35. data/app/packs/src/decidim/initiatives/initiative_creation_wizard.js +16 -0
  36. data/app/packs/src/decidim/initiatives/scoped_type.js +1 -1
  37. data/app/packs/stylesheets/initiatives.scss +16 -2
  38. data/app/permissions/decidim/initiatives/admin/permissions.rb +4 -7
  39. data/app/permissions/decidim/initiatives/permissions.rb +26 -16
  40. data/app/presenters/decidim/initiative_presenter.rb +12 -6
  41. data/app/presenters/decidim/initiatives/admin_log/initiative_presenter.rb +1 -2
  42. data/app/queries/decidim/initiatives/initiatives_stats_followers_count.rb +14 -0
  43. data/app/queries/decidim/initiatives/initiatives_stats_participants_count.rb +14 -0
  44. data/app/serializers/decidim/initiatives/open_data_initiative_serializer.rb +0 -1
  45. data/app/services/decidim/initiatives/data_encryptor.rb +1 -1
  46. data/app/services/decidim/initiatives/legacy_signature_handler.rb +25 -0
  47. data/app/services/decidim/initiatives/progress_notifier.rb +1 -7
  48. data/app/services/decidim/initiatives/signature_handler.rb +248 -0
  49. data/app/services/decidim/initiatives/status_change_notifier.rb +1 -7
  50. data/app/views/decidim/initiatives/admin/committee_requests/index.html.erb +29 -11
  51. data/app/views/decidim/initiatives/admin/exports/_dropdown.html.erb +17 -20
  52. data/app/views/decidim/initiatives/admin/initiatives/_form.html.erb +7 -13
  53. data/app/views/decidim/initiatives/admin/initiatives/_initiative_attachments.erb +2 -2
  54. data/app/views/decidim/initiatives/admin/initiatives/index.html.erb +76 -47
  55. data/app/views/decidim/initiatives/admin/initiatives_types/_form.html.erb +13 -21
  56. data/app/views/decidim/initiatives/admin/initiatives_types/_initiative_type_scopes.html.erb +28 -12
  57. data/app/views/decidim/initiatives/admin/initiatives_types/index.html.erb +33 -15
  58. data/app/views/decidim/initiatives/create_initiative/_committee_member.html.erb +27 -0
  59. data/app/views/decidim/initiatives/create_initiative/_return_to_initiatives_button.html.erb +3 -0
  60. data/app/views/decidim/initiatives/create_initiative/_send_to_technical_validation_button.html.erb +10 -0
  61. data/app/views/decidim/initiatives/create_initiative/_share_committee_link.html.erb +5 -1
  62. data/app/views/decidim/initiatives/create_initiative/fill_data.html.erb +7 -11
  63. data/app/views/decidim/initiatives/create_initiative/finish.html.erb +16 -13
  64. data/app/views/decidim/initiatives/create_initiative/promotal_committee.html.erb +33 -6
  65. data/app/views/decidim/initiatives/create_initiative/select_initiative_type.html.erb +40 -26
  66. data/app/views/decidim/initiatives/initiative_signatures/_sms_code_form.html.erb +22 -0
  67. data/app/views/decidim/initiatives/initiative_signatures/_sms_phone_number_form.html.erb +13 -0
  68. data/app/views/decidim/initiatives/initiative_signatures/fill_personal_data.html.erb +23 -22
  69. data/app/views/decidim/initiatives/initiative_signatures/finish.html.erb +17 -5
  70. data/app/views/decidim/initiatives/initiative_signatures/sms_code.html.erb +6 -8
  71. data/app/views/decidim/initiatives/initiative_signatures/sms_phone_number.html.erb +3 -8
  72. data/app/views/decidim/initiatives/initiative_signatures/update_buttons_and_counters.js.erb +3 -14
  73. data/app/views/decidim/initiatives/initiative_votes/update_buttons_and_counters.js.erb +3 -14
  74. data/app/views/decidim/initiatives/initiatives/_committee_members.html.erb +1 -1
  75. data/app/views/decidim/initiatives/initiatives/_form.html.erb +1 -3
  76. data/app/views/decidim/initiatives/initiatives/_new_initiative_button.html.erb +10 -3
  77. data/app/views/decidim/initiatives/initiatives/_pending_initiatives.html.erb +5 -0
  78. data/app/views/decidim/initiatives/initiatives/index.html.erb +8 -0
  79. data/app/views/decidim/initiatives/initiatives/show.html.erb +2 -2
  80. data/app/views/layouts/decidim/_initiative_signature_creation_header.html.erb +20 -2
  81. data/app/views/layouts/decidim/admin/_manage_initiatives.html.erb +1 -1
  82. data/app/views/layouts/decidim/initiative_signature_creation.html.erb +3 -1
  83. data/config/assets.rb +2 -2
  84. data/config/locales/ar.yml +0 -45
  85. data/config/locales/bg.yml +0 -54
  86. data/config/locales/ca-IT.yml +99 -51
  87. data/config/locales/ca.yml +99 -51
  88. data/config/locales/cs.yml +93 -54
  89. data/config/locales/de.yml +100 -52
  90. data/config/locales/el.yml +0 -45
  91. data/config/locales/en.yml +99 -51
  92. data/config/locales/es-MX.yml +99 -51
  93. data/config/locales/es-PY.yml +99 -51
  94. data/config/locales/es.yml +99 -51
  95. data/config/locales/eu.yml +120 -72
  96. data/config/locales/fi-plain.yml +99 -51
  97. data/config/locales/fi.yml +99 -51
  98. data/config/locales/fr-CA.yml +44 -51
  99. data/config/locales/fr.yml +44 -51
  100. data/config/locales/ga-IE.yml +0 -17
  101. data/config/locales/gl.yml +0 -41
  102. data/config/locales/hu.yml +0 -54
  103. data/config/locales/id-ID.yml +0 -40
  104. data/config/locales/is-IS.yml +0 -22
  105. data/config/locales/it.yml +0 -53
  106. data/config/locales/ja.yml +98 -49
  107. data/config/locales/lb.yml +0 -50
  108. data/config/locales/lt.yml +0 -56
  109. data/config/locales/lv.yml +0 -46
  110. data/config/locales/nl.yml +0 -47
  111. data/config/locales/no.yml +0 -53
  112. data/config/locales/pl.yml +0 -56
  113. data/config/locales/pt-BR.yml +0 -53
  114. data/config/locales/pt.yml +0 -53
  115. data/config/locales/ro-RO.yml +92 -50
  116. data/config/locales/ru.yml +0 -25
  117. data/config/locales/sk.yml +0 -43
  118. data/config/locales/sl.yml +0 -1
  119. data/config/locales/sv.yml +10 -53
  120. data/config/locales/tr-TR.yml +0 -53
  121. data/config/locales/uk.yml +0 -25
  122. data/config/locales/zh-CN.yml +0 -45
  123. data/config/locales/zh-TW.yml +0 -53
  124. data/db/migrate/20250605104500_remove_hashtag_column_initiatives.rb +7 -0
  125. data/lib/decidim/api/initiative_api_type.rb +3 -0
  126. data/lib/decidim/api/initiative_type.rb +23 -4
  127. data/lib/decidim/exporters/initiative_votes_pdf.rb +1 -1
  128. data/lib/decidim/initiatives/default_signature_authorizer.rb +17 -0
  129. data/lib/decidim/initiatives/engine.rb +17 -14
  130. data/lib/decidim/initiatives/menu.rb +1 -1
  131. data/lib/decidim/initiatives/participatory_space.rb +15 -1
  132. data/lib/decidim/initiatives/seeds.rb +1 -2
  133. data/lib/decidim/initiatives/signature_workflow_manifest.rb +176 -0
  134. data/lib/decidim/initiatives/signatures.rb +12 -0
  135. data/lib/decidim/initiatives/test/factories.rb +7 -7
  136. data/lib/decidim/initiatives/test/initiatives_signatures_test_helpers.rb +19 -0
  137. data/lib/decidim/initiatives/validatable_authorizations.rb +83 -0
  138. data/lib/decidim/initiatives/version.rb +1 -1
  139. data/lib/decidim/initiatives.rb +23 -12
  140. metadata +33 -21
  141. data/app/events/decidim/initiatives/endorse_initiative_event.rb +0 -13
  142. data/app/forms/decidim/initiatives/vote_form.rb +0 -208
  143. data/app/packs/src/decidim/initiatives/identity_selector_dialog.js +0 -14
  144. data/app/services/decidim/initiatives/pdf_signature_example.rb +0 -110
  145. data/app/views/decidim/initiatives/initiative_signatures/_wizard_steps.html.erb +0 -15
  146. data/app/views/decidim/initiatives/initiatives/_interactions.html.erb +0 -10
  147. data/app/views/layouts/decidim/_initiative_header.html.erb +0 -27
@@ -7,9 +7,7 @@ zh-TW:
7
7
  answer_url: 回答 URL
8
8
  area_id: 區域
9
9
  decidim_scope_id: 範圍
10
- decidim_user_group_id: 作者
11
10
  description: 說明
12
- hashtag: 主題標籤
13
11
  offline_votes: 親筆簽名
14
12
  offline_votes_for_scope: '%{scope_name} 的親筆簽名'
15
13
  scope_id: 範圍
@@ -36,7 +34,6 @@ zh-TW:
36
34
  comments_enabled: 啟用評論
37
35
  custom_signature_end_date_enabled: 允許作者選擇結束簽名收集期限
38
36
  description: 說明
39
- document_number_authorization_handler: 授權核准文件編號以驗證簽名
40
37
  extra_fields_legal_information: 關於個人資料蒐集的法律資訊
41
38
  minimum_committee_members: 委員會成員最少人數
42
39
  online_signature_enabled: 啟用線上簽名
@@ -86,14 +83,11 @@ zh-TW:
86
83
  label: 狀態
87
84
  values:
88
85
  accepted: 足夠的簽署數
89
- created: 已建立
90
86
  discarded: 已捨棄
91
87
  rejected: 不足夠的簽署數
92
88
  validating: 技術驗證
93
89
  type_id_eq:
94
90
  label: 類別
95
- search_placeholder:
96
- title_or_description_or_id_string_or_author_name_or_author_nickname_cont: 透過標題、描述、ID 或作者姓名搜尋 %{collection}
97
91
  initiatives_settings:
98
92
  update:
99
93
  success: 倡議設定已成功更新.
@@ -279,7 +273,6 @@ zh-TW:
279
273
  edit:
280
274
  update: 更新
281
275
  form:
282
- authorizations: 權限設置
283
276
  options: 選項
284
277
  initiative_type_scopes:
285
278
  title: 倡議類型範圍
@@ -302,7 +295,6 @@ zh-TW:
302
295
  update: "%{user_name} 更新了 %{resource_name} 倡議類型"
303
296
  admin_states:
304
297
  accepted: 足夠的簽署數
305
- created: 已建立
306
298
  discarded: 已捨棄
307
299
  rejected: 不足夠的簽署數
308
300
  validating: 技術驗證
@@ -331,37 +323,20 @@ zh-TW:
331
323
  name: 重點倡議
332
324
  create_initiative:
333
325
  fill_data:
334
- back: 返回
335
326
  continue: 繼續
336
327
  fill_data_help: "<ul> <li>檢閱您倡議的內容。標題是否易於理解?倡議的目標是否清晰明確?</li> <li>您需要選擇簽名的類型。是現場簽名、線上簽名還是兩者的組合?</li> <li>倡議的地理範圍是什麼?</li> </ul>"
337
- more_information: "(更多資訊)"
338
328
  select_area: 選擇一個地區
339
329
  select_scope: 選擇一個類型
340
330
  finish:
341
331
  back: 返回
342
- back_to_initiatives: 回到倡議
343
- confirm: 您即將發送倡議給管理員進行審核和發佈。一旦發佈,您將無法編輯它。您確定要繼續嗎?"
344
- edit_my_initiative: 編輯我的倡議
345
- go_to_my_initiatives: 前往我的倡議
346
- more_information: "(更多資訊)"
347
- send_my_initiative: 將我的倡議送交技術審核
348
- finish_help:
349
- publish_helper_text: 請記住,要使您的倡議能夠被發佈,您必須填寫必要的資訊並將其<strong>送交技術審核</strong>以供管理員審查。
350
332
  previous_form:
351
333
  back: 返回
352
- continue: 繼續
353
- promotal_committee:
354
- individual_help_text: 這種倡議需要至少 %{committee_size} 人(證明者)組成推廣委員會。您必須與參與該倡議的其他人分享以下鏈接。當您的聯繫人收到此鏈接後,他們將需要按照指示進行相應步驟。
355
- more_information: "(更多資訊)"
356
334
  select_initiative_type:
357
335
  back: 返回
358
336
  choose_html: 我想要創建一個<strong>%{title}</strong>
359
337
  more_information: (更多資訊)
360
338
  select: 我想推動這個倡議
361
- select_initiative_type_help: 倡議是參與者可以通過其來干預,以便組織可以為捍衛公共利益而采取行動的手段。您希望發起哪個倡議?
362
339
  verification_required: 驗證您的帳戶以推動這個倡議
363
- share_committee_link:
364
- invite_to_committee_help: 邀請成為推動委員會成員的人的連結
365
340
  edit:
366
341
  accept: 接受倡議
367
342
  back: 返回
@@ -383,11 +358,6 @@ zh-TW:
383
358
  email_outro: 您收到此通知,是因為您正在關注 %{author_nickname}。您可以使用上面的連結停止接收通知。
384
359
  email_subject: 由 %{author_nickname} 提出的新倡議
385
360
  notification_title: 倡議 <a href="%{resource_path}">%{resource_title}</a> 由 <a href="%{author_path}">%{author_name} %{author_nickname}</a> 提出。
386
- endorse_initiative_event:
387
- email_intro: "\"%{author_name} %{author_nickname} 正在追蹤,並已連署以下倡議,或許你想參與對話:"
388
- email_outro: 您收到此通知,是因為您正在關注 %{author_nickname}。您可以使用上面的連結停止接收通知。
389
- email_subject: 倡議得到 %{author_nickname} 的連署
390
- notification_title: <a href="%{resource_path}">%{resource_title}</a> 倡議已獲 %{author_name} %{author_nickname} 連署。
391
361
  revoke_membership_request:
392
362
  email_intro: "%{author_nickname} 拒絕了您加入 %{resource_title} 倡議發起委員會的申請。"
393
363
  email_outro: '這是您收到的通知,因為您申請參與這個倡議:%{resource_title}。'
@@ -402,23 +372,14 @@ zh-TW:
402
372
  uninitialized: 尚未由管理員設置倡議。
403
373
  initiative_signatures:
404
374
  fill_personal_data:
405
- continue: 繼續
406
375
  help: 以下請填寫您的個人資料以簽署此倡議。
407
376
  finish:
408
377
  back_to_initiative: 回到倡議
409
- sms_code:
410
- continue: 檢查代碼並繼續
411
- help: 請檢查您手機收到的SMS簡訊
412
- sms_phone_number:
413
- continue: 發送簡訊給我
414
- help: 請填寫您驗證過的手機號碼以申請驗證碼.
415
378
  initiative_votes:
416
379
  create:
417
380
  error: 發起倡議的簽署遇到問題。
418
381
  invalid: 提供的資料以簽署此倡議為無效.
419
382
  success_html: 恭喜!<strong> %{title} </strong> 倡議已成功簽署。
420
- personal_data:
421
- invalid: 個人資料與授權提供的資料不一致。
422
383
  sms_code:
423
384
  invalid: 您的驗證碼與我們的不符。請仔細檢查我們發送給您的SMS簡訊。
424
385
  sms_phone:
@@ -449,10 +410,6 @@ zh-TW:
449
410
  initiatives:
450
411
  closed_initiatives_warning: 目前沒有開放中的倡議,但您可以在這裡找到所有已結束的倡議清單。
451
412
  no_initiatives_warning: 沒有符合您的搜尋條件的倡議。
452
- interactions:
453
- comments_count:
454
- count:
455
- other: 評論
456
413
  orders:
457
414
  label: '按照以下方式對倡議進行排序:'
458
415
  most_commented: 最多評論
@@ -517,7 +474,6 @@ zh-TW:
517
474
  show:
518
475
  badge_name:
519
476
  accepted: 足夠的簽署數
520
- created: 已建立
521
477
  discarded: 已捨棄
522
478
  rejected: 簽署數不足
523
479
  validating: 技術驗證
@@ -536,20 +492,11 @@ zh-TW:
536
492
  initiatives_type:
537
493
  actions:
538
494
  title: 操作
539
- vote: 簽署
540
495
  layouts:
541
496
  decidim:
542
497
  initiative_creation_header:
543
498
  fill_data: 建立
544
- finish: 完成
545
- promotal_committee: 推動委員會
546
499
  select_initiative_type: 選擇
547
- initiative_signature_creation_header:
548
- fill_personal_data: 填寫您的資料
549
- finish: 完成
550
- sms_code: SMS短訊代碼驗證碼
551
- sms_phone_number: 行動電話號碼
552
- title: 簽名 %{initiative_title}
553
500
  initiatives:
554
501
  no_initiatives_yet:
555
502
  no_initiatives_yet: 尚無倡議!
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RemoveHashtagColumnInitiatives < ActiveRecord::Migration[7.1]
4
+ def change
5
+ remove_column :decidim_initiatives, :hashtag, :string, if_exists: true
6
+ end
7
+ end
@@ -8,8 +8,11 @@ module Decidim
8
8
  graphql_name "InitiativeType"
9
9
  description "An initiative type"
10
10
 
11
+ field :attachments_enabled, GraphQL::Types::Boolean, "Enable attachments on initiative types", null: true
11
12
  field :banner_image, GraphQL::Types::String, "Banner image", null: true
12
13
  field :collect_user_extra_fields, GraphQL::Types::Boolean, "Collect participant personal data on signature", null: true
14
+ field :comments_enabled, GraphQL::Types::Boolean, "Enable comments on initiative types", null: true
15
+ field :custom_signature_end_date_enabled, GraphQL::Types::Boolean, "Enable participants to set custom signature end date", null: true
13
16
  field :description, Decidim::Core::TranslatedFieldType, "This is the initiative type description", null: true
14
17
  field :extra_fields_legal_information, GraphQL::Types::String, "Legal information about the collection of personal data", null: true
15
18
  field :id, GraphQL::Types::ID, "The internal ID for this initiative type", null: false
@@ -9,13 +9,19 @@ module Decidim
9
9
  implements Decidim::Core::AttachableInterface
10
10
  implements Decidim::Initiatives::InitiativeTypeInterface
11
11
  implements Decidim::Core::TimestampsInterface
12
+ implements Decidim::Core::FollowableInterface
13
+ implements Decidim::Core::ReferableInterface
14
+ implements Decidim::Comments::CommentableInterface
12
15
 
13
16
  description "A initiative"
14
17
 
18
+ field :answer, Decidim::Core::TranslatedFieldType, "The answer of the initiative", null: true
19
+ field :answer_url, GraphQL::Types::String, "The answer url of the initiative", null: true
20
+ field :answered_at, Decidim::Core::DateTimeType, "The date this initiative was answered", null: true
15
21
  field :author, Decidim::Core::AuthorInterface, "The initiative author", null: false
16
22
  field :committee_members, [Decidim::Initiatives::InitiativeCommitteeMemberType, { null: true }], "The committee members list", null: true
17
23
  field :description, Decidim::Core::TranslatedFieldType, "The description of this initiative.", null: true
18
- field :hashtag, GraphQL::Types::String, "The hashtag for this initiative", null: true
24
+ field :first_progress_notification_at, Decidim::Core::DateTimeType, "The date when the first progress notification was sent ", null: true
19
25
  field :initiative_supports_count, GraphQL::Types::Int,
20
26
  description: "The number of supports in this initiative",
21
27
  method: :online_votes_count,
@@ -28,15 +34,28 @@ module Decidim
28
34
  field :offline_votes, GraphQL::Types::Int, "The number of offline votes in this initiative", method: :offline_votes_count, null: true
29
35
  field :online_votes, GraphQL::Types::Int, "The number of online votes in this initiative", method: :online_votes_count, null: true
30
36
  field :published_at, Decidim::Core::DateTimeType, "The time this initiative was published", null: false
31
- field :reference, GraphQL::Types::String, "Reference prefix for this initiative", null: false
37
+ field :second_progress_notification_at, Decidim::Core::DateTimeType, "The date when the second progress notification was sent", null: true
32
38
  field :signature_end_date, Decidim::Core::DateType, "The signature end date", null: false
33
39
  field :signature_start_date, Decidim::Core::DateType, "The signature start date", null: false
34
40
  field :signature_type, GraphQL::Types::String, "Signature type of the initiative", null: true
35
41
  field :slug, GraphQL::Types::String, "The slug of the initiative", null: false
36
42
  field :state, GraphQL::Types::String, "Current status of the initiative", null: true
43
+ field :url, GraphQL::Types::String, "The URL of this initiative", null: true
37
44
 
38
- def author
39
- object.user_group || object.author
45
+ def url
46
+ Decidim::EngineRouter.main_proxy(object).initiative_url(object)
47
+ end
48
+
49
+ def answer
50
+ return unless object.answered?
51
+
52
+ object.answer
53
+ end
54
+
55
+ def answer_url
56
+ return unless object.answered?
57
+
58
+ object.answer_url
40
59
  end
41
60
  end
42
61
  end
@@ -148,7 +148,7 @@ module Decidim
148
148
  end
149
149
 
150
150
  def encryptor
151
- @encryptor ||= Decidim::Initiatives::DataEncryptor.new(secret: "personal user metadata")
151
+ @encryptor ||= Decidim::Initiatives::DataEncryptor.new(secret: Decidim::Initiatives.signature_handler_encryption_secret)
152
152
  end
153
153
 
154
154
  def truncate(text, length = 50)
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ class DefaultSignatureAuthorizer < Decidim::Verifications::DefaultActionAuthorizer
6
+ #
7
+ # Initializes the DefaultActionAuthorizer class.
8
+ #
9
+ # authorization - The existing authorization record to be evaluated. Can be nil.
10
+ # options - A hash with options related only to the current authorization process.
11
+ #
12
+ def initialize(authorization, options = {})
13
+ super(authorization, options, nil, nil)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -22,6 +22,7 @@ module Decidim
22
22
 
23
23
  resources :create_initiative do
24
24
  collection do
25
+ get :load_initiative_draft
25
26
  get :select_initiative_type
26
27
  put :select_initiative_type, to: "create_initiative#store_initiative_type"
27
28
  get :fill_data
@@ -60,6 +61,7 @@ module Decidim
60
61
  get :authorization_create_modal, to: "authorization_create_modals#show"
61
62
  get :print, to: "initiatives#print", as: "print"
62
63
  get :send_to_technical_validation, to: "initiatives#send_to_technical_validation"
64
+ delete :discard, to: "initiatives#discard"
63
65
  end
64
66
 
65
67
  resource :initiative_vote, only: [:create, :destroy]
@@ -99,6 +101,15 @@ module Decidim
99
101
  Decidim.icons.register(name: "forbid-line", icon: "forbid-line", category: "system", description: "", engine: :initiatives)
100
102
  end
101
103
 
104
+ initializer "decidim_initiatives.stats" do
105
+ Decidim.stats.register :initiatives_count,
106
+ priority: StatsRegistry::HIGH_PRIORITY,
107
+ icon_name: "lightbulb-flash-line",
108
+ tooltip_key: "initiatives_count_tooltip" do |organization, _start_at, _end_at|
109
+ Decidim::Initiative.where(organization:).public_spaces.count
110
+ end
111
+ end
112
+
102
113
  initializer "decidim_initiatives.content_blocks" do
103
114
  Decidim::Initiatives::ContentBlocks::RegistryManager.register!
104
115
  end
@@ -118,20 +129,12 @@ module Decidim
118
129
  Decidim::Gamification.register_badge(:initiatives) do |badge|
119
130
  badge.levels = [1, 5, 15, 30, 50]
120
131
 
121
- badge.valid_for = [:user, :user_group]
132
+ badge.valid_for = [:user]
122
133
 
123
134
  badge.reset = lambda { |model|
124
- case model
125
- when User
126
- Decidim::Initiative.where(
127
- author: model,
128
- user_group: nil
129
- ).published.count
130
- when UserGroup
131
- Decidim::Initiative.where(
132
- user_group: model
133
- ).published.count
134
- end
135
+ Decidim::Initiative.where(
136
+ author: model
137
+ ).published.count
135
138
  }
136
139
  end
137
140
  end
@@ -140,7 +143,7 @@ module Decidim
140
143
  Decidim::Api::QueryType.include QueryExtensions
141
144
  end
142
145
 
143
- initializer "decidim_initiatives.webpacker.assets_path" do
146
+ initializer "decidim_initiatives.shakapacker.assets_path" do
144
147
  Decidim.register_assets_path File.expand_path("app/packs", root)
145
148
  end
146
149
 
@@ -149,7 +152,7 @@ module Decidim
149
152
  # We need to make sure we call `Preview.all` before requiring our
150
153
  # previews, otherwise any previews the app attempts to add need to be
151
154
  # manually required.
152
- if Rails.env.development? || Rails.env.test?
155
+ if Rails.env.local?
153
156
  ActionMailer::Preview.all
154
157
 
155
158
  Dir[root.join("spec/mailers/previews/**/*_preview.rb")].each do |file|
@@ -113,7 +113,7 @@ module Decidim
113
113
  decidim_admin_initiatives.initiative_share_tokens_path(current_participatory_space),
114
114
  active: is_active_link?(decidim_admin_initiatives.initiative_share_tokens_path(current_participatory_space)),
115
115
  icon_name: "share-line",
116
- if: allowed_to?(:read, :share_tokens, current_participatory_space:)
116
+ if: allowed_to?(:read, :share_token, current_participatory_space:)
117
117
  end
118
118
  end
119
119
 
@@ -32,7 +32,21 @@ Decidim.register_participatory_space(:initiatives) do |participatory_space|
32
32
 
33
33
  participatory_space.register_resource(:initiatives_type) do |resource|
34
34
  resource.model_class_name = "Decidim::InitiativesType"
35
- resource.actions = %w(vote create)
35
+ resource.actions = %w(create)
36
+ end
37
+
38
+ participatory_space.register_stat :followers_count,
39
+ priority: Decidim::StatsRegistry::MEDIUM_PRIORITY,
40
+ icon_name: "user-follow-line",
41
+ tooltip_key: "followers_count_tooltip" do
42
+ Decidim::Initiatives::InitiativesStatsFollowersCount.for(participatory_space)
43
+ end
44
+
45
+ participatory_space.register_stat :participants_count,
46
+ priority: Decidim::StatsRegistry::MEDIUM_PRIORITY,
47
+ icon_name: "user-line",
48
+ tooltip_key: "participants_count_tooltip" do
49
+ Decidim::Initiatives::InitiativesStatsParticipantsCount.for(participatory_space)
36
50
  end
37
51
 
38
52
  participatory_space.model_class_name = "Decidim::Initiative"
@@ -9,7 +9,7 @@ module Decidim
9
9
  def call
10
10
  create_content_block!
11
11
 
12
- 3.times do |_n|
12
+ number_of_records.times do |_n|
13
13
  type = create_initiative_type!
14
14
 
15
15
  organization.top_scopes.each do |scope|
@@ -19,7 +19,6 @@ module Decidim
19
19
 
20
20
  Decidim::Initiative.states.keys.each do |state|
21
21
  Decidim::Initiative.skip_callback(:save, :after, :notify_state_change, raise: false)
22
- Decidim::Initiative.skip_callback(:create, :after, :notify_creation, raise: false)
23
22
 
24
23
  initiative = create_initiative!(state:)
25
24
 
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require "decidim/settings_manifest"
4
+ require "decidim/initiatives/default_signature_authorizer"
5
+
6
+ module Decidim
7
+ module Initiatives
8
+ # This class serves as a DSL to declaratively specify a signature method.
9
+ #
10
+ # To define a direct signature method, you need to specify the `form`
11
+ # attribute as a `Decidim::Form` that will be valid if the signature
12
+ # process is valid. If no form is provided the application assumes that the
13
+ # form class is `Decidim::Initiatives::SignatureHandler`
14
+ #
15
+ # All the attributes except the name are optional. If no attributes are set
16
+ # there are default values for them and the signature flow will be direct
17
+ # without steps and metadata stored in the initiatives_vote
18
+ #
19
+ # Signature workflow attributes:
20
+ # - :form (String) (optional) The name of the form object class responsible
21
+ # for collecting and validating the data to create the initiative votes.
22
+ # This class should be inherited from
23
+ # Decidim::Initiatives::SignatureHandler and can define a set of
24
+ # attributes to collect user personal data. If extra attributes are
25
+ # defined a collect personal data step will be enabled. This class also
26
+ # can define an unique_id to avoid duplicated votes based on the
27
+ # personal data and a metadata hash that will be encrypted in the vote
28
+ # creation. The class can also collect a hash of metadata that will be
29
+ # passed to an authorization handler if defined in the workflow. The
30
+ # class is also responsible to define the scopes associated with the
31
+ # votes. The main class by default detects the scopes candidates and
32
+ # the inherited class can define a signature_scope_id based on the data
33
+ # provided by the user.
34
+ # (default: "Decidim::Initiatives::SignatureHandler")
35
+ # - :authorization_handler_form (String) (optional) If this authorization
36
+ # handler class name is set the previous form will create and validate
37
+ # an instance of it from the params defined in it (by default the
38
+ # collected metadata and the user). If the authorization is invalid the
39
+ # signature is blocked. (default: nil)
40
+ # - :save_authorizations (Boolean) (optional) This option allows the
41
+ # workflow to save or update if already exist the authorization
42
+ # associated to the previous authorization handler. Note that if this
43
+ # option is set to false the user will be required to have been
44
+ # previously authorized with the method associated to the authorization
45
+ # handler. The authorization is searched by the organization, the user
46
+ # and the authorization handler name. If this class is not defined,
47
+ # class Decidim::Initiatives::DefaultSignatureAuthorizer is used,
48
+ # which inherits from Decidim::Verifications::DefaultActionAuthorizer
49
+ # and checks the authorization status. (default: true)
50
+ # - :ephemeral (Boolean) (optional) This option enables the possibility for
51
+ # users to sign without prior registration through an ephemeral session.
52
+ # To allow ephemeral sessions to be recovered o transferred to regular
53
+ # users authorizations must be stored in the process so an
54
+ # authorization_handler_form must be defined and the save_authorizations
55
+ # option must not be set to false. If those settings are not properly
56
+ # configured this option will be ignored and the workflow will not
57
+ # allow ephemeral sessions. (default: false)
58
+ # - :promote_authorization_validation_errors (Boolean) (optional) If set
59
+ # to true, errors in the personal data passed to the authorization
60
+ # handler form will be displayed next to the corresponding fields
61
+ # in the collection of personal data. Note that this option may provide
62
+ # information about personal data to the user. (default: false)
63
+ # - :action_authorizer (String) This is the name of the action authorizer
64
+ # class responsible for checking the status of the authorization
65
+ # associated to the signature. If this class is not defined, the
66
+ # authorization status will be ignored. You can define a handler
67
+ # inherited from Decidim::Initiatives::DefaultSignatureAuthorizer which
68
+ # inherits from Decidim::Verifications::DefaultActionAuthorizer and
69
+ # checks the authorization status with an instance initialized only
70
+ # with the authorization (without a component or a resource).
71
+ # (default: nil)
72
+ # - :sms_verification (Boolean) (optional) This option enables an
73
+ # additional SMS verification step. It uses by default the sms
74
+ # verification flow defined in decidim_verifications and expects the
75
+ # user to have previously created an SMS authorization validating
76
+ # their phone number. This behaviour can be changed defining the
77
+ # following attributes. (default: false)
78
+ # - :sms_mobile_phone_form_class (String) (optional) The name of the class
79
+ # responsible for starting the users phone verification. It uses the
80
+ # default form defined in decidim_initiatives.
81
+ # (default: "Decidim::Verifications::Sms::MobilePhoneForm")
82
+ # - :sms_mobile_phone_validator (String) (optional) The name of the command
83
+ # class responsible for checking if the mobile phone provided by the
84
+ # user has an authorization associated to the sms_mobile_form_class
85
+ # and delivering the sms code
86
+ # (default: "Decidim::Initiatives::ValidateMobilePhone")
87
+ # - :sms_code_validator_class (String) (optional) The name of the command
88
+ # class responsible for checking if the SMS code provided by the user
89
+ # is valid. (default: "Decidim::Initiatives::ValidateSmsCode")
90
+ #
91
+ # An example of declaration of a workflow:
92
+ #
93
+ # Decidim::Initiatives::Signatures.register_workflow(:dummy_signature_handler) do |workflow|
94
+ # workflow.form = "DummySignatureHandler"
95
+ # workflow.authorization_handler_form = "DummyAuthorizationHandler"
96
+ # workflow.action_authorizer = "DummySignatureHandler::DummySignatureActionAuthorizer"
97
+ # workflow.promote_authorization_validation_errors = true
98
+ # workflow.sms_verification = true
99
+ # end
100
+ #
101
+ class SignatureWorkflowManifest
102
+ include ActiveModel::Model
103
+ include Decidim::AttributeObject::Model
104
+
105
+ attribute :name, String
106
+ attribute :form, String
107
+ attribute :authorization_handler_form, String, default: nil
108
+ attribute :action_authorizer, String
109
+ attribute :save_authorizations, Boolean, default: true
110
+ attribute :promote_authorization_validation_errors, Boolean, default: false
111
+ attribute :ephemeral, Boolean, default: false
112
+ attribute :sms_verification, Boolean, default: false
113
+ attribute :sms_mobile_phone_form, String, default: nil
114
+ attribute :sms_mobile_phone_validator, String, default: nil
115
+ attribute :sms_code_validator, String, default: nil
116
+
117
+ validates :name, presence: true
118
+ validate :ephemeral_configuration
119
+
120
+ alias key name
121
+
122
+ def fullname
123
+ I18n.t("#{key}.name", scope: "decidim.signature_workflows", default: name.humanize)
124
+ end
125
+
126
+ def signature_form_class
127
+ form&.safe_constantize || Decidim::Initiatives::SignatureHandler
128
+ end
129
+
130
+ def action_authorizer_class
131
+ return if action_authorizer.blank?
132
+
133
+ action_authorizer.safe_constantize || DefaultSignatureAuthorizer
134
+ end
135
+
136
+ def authorization_handler_form_class
137
+ authorization_handler_form&.safe_constantize
138
+ end
139
+
140
+ # If no authorization handler is set or the save_authorizations option
141
+ # is disabled the workflow will not be able to save the user verification
142
+ # attributes necessary to recover the user session or transfer their
143
+ # activities
144
+ def ephemeral?
145
+ return if authorization_handler_form_class.blank? || !save_authorizations
146
+
147
+ ephemeral
148
+ end
149
+
150
+ def sms_mobile_phone_form_class
151
+ return unless sms_verification
152
+
153
+ sms_mobile_phone_form&.safe_constantize || Decidim::Verifications::Sms::MobilePhoneForm
154
+ end
155
+
156
+ def sms_mobile_phone_validator_class
157
+ return unless sms_verification
158
+
159
+ sms_mobile_phone_validator&.safe_constantize || Decidim::Initiatives::ValidateMobilePhone
160
+ end
161
+
162
+ def sms_code_validator_class
163
+ return unless sms_verification
164
+
165
+ sms_code_validator&.safe_constantize || Decidim::Initiatives::ValidateSmsCode
166
+ end
167
+
168
+ def ephemeral_configuration
169
+ return unless Rails.env.development?
170
+ return unless ephemeral
171
+
172
+ raise StandardError, "Wrong configuration of ephemeral signature workflow #{fullname}" if !save_authorizations || authorization_handler_form.blank?
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ module Signatures
6
+ autoload :SignatureWorkflowManifest, "decidim/initiatives/signature_workflow_manifest"
7
+ include Decidim::HasWorkflows
8
+
9
+ def self.workflow_manifest_class = SignatureWorkflowManifest
10
+ end
11
+ end
12
+ end
@@ -84,13 +84,17 @@ FactoryBot.define do
84
84
  end
85
85
 
86
86
  trait :with_user_extra_fields_collection do
87
- collect_user_extra_fields { true }
88
87
  extra_fields_legal_information { generate_localized_description(:initiatives_type_extra_fields_legal_information, skip_injection:) }
89
- document_number_authorization_handler { "dummy_authorization_handler" }
88
+ document_number_authorization_handler { "dummy_signature_with_personal_data_handler" }
90
89
  end
91
90
 
92
91
  trait :with_sms_code_validation do
93
- validate_sms_code_on_votes { true }
92
+ document_number_authorization_handler { "dummy_signature_with_sms_handler" }
93
+ end
94
+
95
+ trait :with_sms_code_validation_and_user_extra_fields_collection do
96
+ extra_fields_legal_information { generate_localized_description(:initiatives_type_extra_fields_legal_information, skip_injection:) }
97
+ document_number_authorization_handler { "dummy_signature_handler" }
94
98
  end
95
99
 
96
100
  trait :child_scope_threshold_enabled do
@@ -268,10 +272,6 @@ FactoryBot.define do
268
272
  end
269
273
  initiative { create(:initiative, skip_injection:) }
270
274
  author { create(:user, :confirmed, organization: initiative.organization, skip_injection:) }
271
- decidim_user_group_id { create(:user_group, skip_injection:).id }
272
- after(:create) do |support, evaluator|
273
- create(:user_group_membership, user: support.author, user_group: Decidim::UserGroup.find(support.decidim_user_group_id), skip_injection: evaluator.skip_injection)
274
- end
275
275
  end
276
276
 
277
277
  factory :initiatives_committee_member, class: "Decidim::InitiativesCommitteeMember" do
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ module InitiativesSignaturesTestHelpers
6
+ def fill_signature_date(date)
7
+ [date.year, date.month, date.day].each_with_index do |value, i|
8
+ within "select[name='dummy_signature_handler[date_of_birth(#{i + 1}i)]']" do
9
+ find("option[value='#{value}']").select_option
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ RSpec.configure do |config|
18
+ config.include Decidim::Initiatives::InitiativesSignaturesTestHelpers, type: :system
19
+ end