decidim 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of decidim might be problematic. Click here for more details.

Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.json +2 -5
  3. data/.mention-bot +2 -1
  4. data/.yardopts +8 -0
  5. data/Gemfile.lock +64 -62
  6. data/README.md +11 -9
  7. data/decidim-admin/app/assets/javascripts/decidim/admin/sort_steps.js.es6 +3 -3
  8. data/decidim-admin/app/assets/stylesheets/decidim/admin/_email_preview.scss +5 -0
  9. data/decidim-admin/app/assets/stylesheets/decidim/admin/application.scss +1 -0
  10. data/decidim-admin/app/commands/decidim/admin/create_newsletter.rb +30 -0
  11. data/decidim-admin/app/commands/decidim/admin/deliver_newsletter.rb +29 -0
  12. data/decidim-admin/app/commands/decidim/admin/destroy_participatory_process_step.rb +9 -0
  13. data/decidim-admin/app/commands/decidim/admin/update_newsletter.rb +33 -0
  14. data/decidim-admin/app/constraints/decidim/admin/organization_dashboard_constraint.rb +1 -1
  15. data/decidim-admin/app/controllers/decidim/admin/features_controller.rb +2 -2
  16. data/decidim-admin/app/controllers/decidim/admin/newsletters_controller.rb +115 -0
  17. data/decidim-admin/app/controllers/decidim/admin/participatory_processes_controller.rb +4 -4
  18. data/decidim-admin/app/forms/decidim/admin/newsletter_form.rb +15 -0
  19. data/decidim-admin/app/jobs/decidim/admin/newsletter_delivery_job.rb +18 -0
  20. data/decidim-admin/app/jobs/decidim/admin/newsletter_job.rb +32 -0
  21. data/decidim-admin/app/models/decidim/admin/abilities/admin_user.rb +4 -0
  22. data/decidim-admin/app/models/decidim/admin/abilities/base.rb +4 -0
  23. data/decidim-admin/app/models/decidim/admin/abilities/collaborator_user.rb +19 -0
  24. data/decidim-admin/app/models/decidim/admin/abilities/participatory_process_admin.rb +1 -1
  25. data/decidim-admin/app/queries/decidim/admin/manageable_participatory_processes_for_user.rb +1 -1
  26. data/decidim-admin/app/views/decidim/admin/newsletters/_form.html.erb +5 -0
  27. data/decidim-admin/app/views/decidim/admin/newsletters/edit.html.erb +11 -0
  28. data/decidim-admin/app/views/decidim/admin/newsletters/index.html.erb +47 -0
  29. data/decidim-admin/app/views/decidim/admin/newsletters/new.html.erb +11 -0
  30. data/decidim-admin/app/views/decidim/admin/newsletters/show.html.erb +14 -0
  31. data/decidim-admin/app/views/decidim/admin/participatory_process_steps/_form.html.erb +1 -1
  32. data/decidim-admin/app/views/decidim/admin/participatory_processes/_form.html.erb +1 -1
  33. data/decidim-admin/app/views/decidim/admin/static_pages/_form.html.erb +1 -1
  34. data/decidim-admin/app/views/layouts/decidim/admin/_sidebar.html.erb +1 -0
  35. data/decidim-admin/config/locales/ca.yml +40 -0
  36. data/decidim-admin/config/locales/en.yml +40 -0
  37. data/decidim-admin/config/locales/es.yml +40 -0
  38. data/decidim-admin/config/routes.rb +7 -0
  39. data/decidim-admin/decidim-admin.gemspec +0 -1
  40. data/decidim-admin/lib/decidim/admin/engine.rb +1 -0
  41. data/decidim-admin/lib/decidim/admin/features/base_controller.rb +5 -1
  42. data/decidim-admin/spec/commands/create_newsletter_spec.rb +62 -0
  43. data/decidim-admin/spec/commands/deliver_newsletter_spec.rb +45 -0
  44. data/decidim-admin/spec/commands/destroy_participatory_process_step_spec.rb +12 -0
  45. data/decidim-admin/spec/commands/update_newsletter_spec.rb +64 -0
  46. data/decidim-admin/spec/features/admin_manages_newsletters_spec.rb +147 -0
  47. data/decidim-admin/spec/features/admin_manages_participatory_processes_spec.rb +1 -5
  48. data/decidim-admin/spec/features/static_pages_spec.rb +16 -0
  49. data/decidim-admin/spec/forms/newsletter_form_spec.rb +47 -0
  50. data/decidim-admin/spec/jobs/newsletter_delivery_job_spec.rb +25 -0
  51. data/decidim-admin/spec/jobs/newsletter_job_spec.rb +29 -0
  52. data/decidim-admin/spec/models/abilities/collaborator_user_spec.rb +66 -0
  53. data/decidim-admin/spec/organization_dashboard_constraint_spec.rb +55 -0
  54. data/decidim-admin/spec/queries/manageable_participatory_processes_for_user_spec.rb +8 -0
  55. data/decidim-admin/spec/shared/manage_processes_examples.rb +1 -5
  56. data/decidim-api/lib/decidim/api.rb +2 -7
  57. data/decidim-api/lib/decidim/api/{types/mutation.rb → mutation_type.rb} +0 -0
  58. data/decidim-api/lib/decidim/api/{types/query.rb → query_type.rb} +0 -0
  59. data/decidim-budgets/app/assets/javascripts/decidim/budgets/projects.js.es6 +4 -4
  60. data/decidim-budgets/app/commands/decidim/budgets/add_line_item.rb +4 -2
  61. data/decidim-budgets/app/commands/decidim/budgets/checkout.rb +7 -3
  62. data/decidim-budgets/app/models/decidim/budgets/order.rb +16 -1
  63. data/decidim-budgets/app/models/decidim/budgets/project.rb +16 -0
  64. data/decidim-budgets/app/views/decidim/budgets/line_items/update_budget.js.erb +7 -7
  65. data/decidim-budgets/app/views/decidim/budgets/projects/_budget_confirm.html.erb +27 -25
  66. data/decidim-budgets/app/views/decidim/budgets/projects/_budget_summary.html.erb +4 -13
  67. data/decidim-budgets/app/views/decidim/budgets/projects/_order_progress.html.erb +23 -21
  68. data/decidim-budgets/app/views/decidim/budgets/projects/_order_selected_projects.html.erb +25 -23
  69. data/decidim-budgets/app/views/decidim/budgets/projects/_order_total_budget.html.erb +3 -1
  70. data/decidim-budgets/app/views/decidim/budgets/projects/_project.html.erb +32 -30
  71. data/decidim-budgets/app/views/decidim/budgets/projects/_projects.html.erb +1 -3
  72. data/decidim-budgets/app/views/decidim/budgets/projects/index.html.erb +0 -4
  73. data/decidim-budgets/app/views/decidim/budgets/projects/show.html.erb +5 -6
  74. data/decidim-budgets/config/i18n-tasks.yml +1 -0
  75. data/decidim-budgets/config/locales/ca.yml +7 -2
  76. data/decidim-budgets/config/locales/en.yml +17 -6
  77. data/decidim-budgets/config/locales/es.yml +7 -2
  78. data/decidim-budgets/lib/decidim/budgets/feature.rb +2 -2
  79. data/decidim-budgets/spec/commands/add_line_item_spec.rb +16 -3
  80. data/decidim-budgets/spec/commands/cancel_order_spec.rb +7 -1
  81. data/decidim-budgets/spec/commands/checkout_spec.rb +9 -2
  82. data/decidim-budgets/spec/commands/remove_line_item_spec.rb +16 -5
  83. data/decidim-budgets/spec/features/orders_spec.rb +8 -2
  84. data/decidim-budgets/spec/models/order_spec.rb +31 -2
  85. data/decidim-budgets/spec/shared/admin_shared_context.rb +1 -1
  86. data/decidim-comments/README.md +1 -1
  87. data/decidim-comments/app/assets/javascripts/decidim/comments/bundle.js +0 -0
  88. data/decidim-comments/app/frontend/comments/add_comment_form.component.jsx +156 -93
  89. data/decidim-comments/app/frontend/comments/add_comment_form.component.test.jsx +36 -23
  90. data/decidim-comments/app/frontend/comments/add_comment_form.mutation.graphql +5 -3
  91. data/decidim-comments/app/frontend/comments/add_comment_form_commentable.fragment.graphql +4 -0
  92. data/decidim-comments/app/frontend/comments/add_comment_form_session.fragment.graphql +6 -0
  93. data/decidim-comments/app/frontend/comments/comment.component.jsx +11 -13
  94. data/decidim-comments/app/frontend/comments/comment.component.test.jsx +22 -21
  95. data/decidim-comments/app/frontend/comments/comment.fragment.graphql +4 -4
  96. data/decidim-comments/app/frontend/comments/comment_data.fragment.graphql +4 -3
  97. data/decidim-comments/app/frontend/comments/comment_order_selector.component.jsx +1 -1
  98. data/decidim-comments/app/frontend/comments/comment_thread.component.jsx +3 -3
  99. data/decidim-comments/app/frontend/comments/comment_thread.component.test.jsx +3 -3
  100. data/decidim-comments/app/frontend/comments/comment_thread.fragment.graphql +1 -1
  101. data/decidim-comments/app/frontend/comments/comments.component.jsx +47 -39
  102. data/decidim-comments/app/frontend/comments/comments.component.test.jsx +51 -38
  103. data/decidim-comments/app/frontend/comments/comments.query.graphql +10 -4
  104. data/decidim-comments/app/frontend/comments/down_vote_button.component.jsx +6 -3
  105. data/decidim-comments/app/frontend/comments/up_vote_button.component.jsx +7 -4
  106. data/decidim-comments/app/frontend/support/generate_comments_data.js +4 -4
  107. data/decidim-comments/app/models/decidim/comments/comment.rb +7 -9
  108. data/decidim-comments/app/queries/decidim/comments/{comments_with_replies.rb → sorted_comments.rb} +3 -8
  109. data/decidim-comments/app/types/decidim/comments/commentable_interface.rb +44 -0
  110. data/decidim-comments/app/types/decidim/comments/commentable_mutation_type.rb +29 -0
  111. data/decidim-comments/app/types/decidim/comments/commentable_type.rb +14 -0
  112. data/decidim-comments/config/locales/ca.yml +3 -1
  113. data/decidim-comments/config/locales/en.yml +5 -2
  114. data/decidim-comments/config/locales/es.yml +3 -1
  115. data/decidim-comments/lib/decidim/comments.rb +4 -0
  116. data/decidim-comments/{app/types/decidim/comments → lib/decidim/comments/api}/add_comment_type.rb +0 -0
  117. data/decidim-comments/{app/types/decidim/comments → lib/decidim/comments/api}/comment_mutation_type.rb +0 -0
  118. data/decidim-comments/{app/types/decidim/comments → lib/decidim/comments/api}/comment_type.rb +11 -17
  119. data/decidim-comments/lib/decidim/comments/commentable.rb +45 -0
  120. data/decidim-comments/lib/decidim/comments/comments_helper.rb +15 -10
  121. data/decidim-comments/lib/decidim/comments/mutation_extensions.rb +8 -16
  122. data/decidim-comments/lib/decidim/comments/query_extensions.rb +5 -8
  123. data/decidim-comments/lib/decidim/comments/test/factories.rb +3 -3
  124. data/decidim-comments/spec/commands/vote_comment_spec.rb +3 -1
  125. data/decidim-comments/spec/features/comments_spec.rb +28 -14
  126. data/decidim-comments/spec/features/notifications_spec.rb +1 -1
  127. data/decidim-comments/spec/helpers/comments_helper_spec.rb +4 -27
  128. data/decidim-comments/spec/models/comment_spec.rb +7 -11
  129. data/decidim-comments/spec/models/comment_vote_spec.rb +3 -1
  130. data/decidim-comments/spec/models/seed_spec.rb +3 -4
  131. data/decidim-comments/spec/queries/{comments_with_replies_spec.rb → sorted_comments_spec.rb} +12 -29
  132. data/decidim-comments/spec/types/comment_type_spec.rb +23 -17
  133. data/decidim-comments/spec/types/commentable_mutation_type_spec.rb +34 -0
  134. data/decidim-comments/spec/types/commentable_type_spec.rb +48 -0
  135. data/decidim-comments/spec/types/mutation_type_spec.rb +5 -22
  136. data/decidim-comments/spec/types/query_type_spec.rb +0 -24
  137. data/decidim-dev/config/i18n-tasks.yml +4 -0
  138. data/decidim-dev/decidim-dev.gemspec +1 -1
  139. data/decidim-dev/lib/decidim/dev/test/rspec_support/action_mailer.rb +10 -4
  140. data/decidim-dev/lib/decidim/dev/test/rspec_support/feature.rb +6 -5
  141. data/decidim-dev/lib/decidim/dev/test/rspec_support/i18n.rb +0 -1
  142. data/decidim-meetings/app/assets/javascripts/decidim/meetings/map.js.es6.erb +8 -3
  143. data/decidim-meetings/app/assets/stylesheets/decidim/meetings/map.css +3 -1
  144. data/decidim-meetings/app/controllers/decidim/meetings/meetings_controller.rb +2 -2
  145. data/decidim-meetings/app/forms/decidim/meetings/admin/close_meeting_form.rb +2 -1
  146. data/decidim-meetings/app/helpers/decidim/meetings/map_helper.rb +1 -0
  147. data/decidim-meetings/app/services/decidim/meetings/meeting_search.rb +7 -3
  148. data/decidim-meetings/app/views/decidim/meetings/meetings/_datetime.html.erb +1 -1
  149. data/decidim-meetings/app/views/decidim/meetings/meetings/_filters.html.erb +5 -5
  150. data/decidim-meetings/app/views/decidim/meetings/meetings/_linked_meetings.html.erb +1 -1
  151. data/decidim-meetings/app/views/decidim/meetings/meetings/index.html.erb +1 -5
  152. data/decidim-meetings/app/views/decidim/meetings/meetings/show.html.erb +16 -9
  153. data/decidim-meetings/config/locales/ca.yml +2 -3
  154. data/decidim-meetings/config/locales/en.yml +2 -3
  155. data/decidim-meetings/config/locales/es.yml +2 -3
  156. data/decidim-meetings/spec/features/explore_meetings_spec.rb +75 -24
  157. data/decidim-meetings/spec/forms/close_meeting_form_spec.rb +1 -1
  158. data/decidim-meetings/spec/services/meeting_search_spec.rb +15 -12
  159. data/decidim-meetings/vendor/assets/javascripts/leaflet.markercluster.js +7 -0
  160. data/decidim-meetings/vendor/assets/stylesheets/MarkerCluster.Default.css +60 -0
  161. data/decidim-meetings/vendor/assets/stylesheets/MarkerCluster.css +14 -0
  162. data/decidim-pages/app/models/decidim/pages/page.rb +22 -0
  163. data/decidim-pages/app/views/decidim/pages/application/show.html.erb +2 -6
  164. data/decidim-pages/config/locales/ca.yml +2 -2
  165. data/decidim-pages/config/locales/en.yml +2 -2
  166. data/decidim-pages/config/locales/es.yml +2 -2
  167. data/decidim-pages/lib/decidim/pages/feature.rb +2 -2
  168. data/decidim-pages/spec/features/page_show_spec.rb +0 -33
  169. data/decidim-proposals/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +2 -0
  170. data/decidim-proposals/app/controllers/decidim/proposals/admin/proposals_controller.rb +2 -0
  171. data/decidim-proposals/app/models/decidim/proposals/abilities/admin_user.rb +45 -0
  172. data/decidim-proposals/app/models/decidim/proposals/abilities/process_admin_user.rb +57 -0
  173. data/decidim-proposals/app/models/decidim/proposals/proposal.rb +21 -0
  174. data/decidim-proposals/app/views/decidim/proposals/admin/proposals/_form.html.erb +1 -1
  175. data/decidim-proposals/app/views/decidim/proposals/admin/proposals/index.html.erb +16 -10
  176. data/decidim-proposals/app/views/decidim/proposals/proposal_votes/update_buttons_and_counters.js.erb +4 -4
  177. data/decidim-proposals/app/views/decidim/proposals/proposals/_filters.html.erb +7 -3
  178. data/decidim-proposals/app/views/decidim/proposals/proposals/_proposal.html.erb +2 -6
  179. data/decidim-proposals/app/views/decidim/proposals/proposals/_remaining_votes_count.html.erb +3 -1
  180. data/decidim-proposals/app/views/decidim/proposals/proposals/_tags.html.erb +1 -1
  181. data/decidim-proposals/app/views/decidim/proposals/proposals/_vote_button.html.erb +23 -21
  182. data/decidim-proposals/app/views/decidim/proposals/proposals/_votes_count.html.erb +9 -6
  183. data/decidim-proposals/app/views/decidim/proposals/proposals/_votes_limit.html.erb +1 -3
  184. data/decidim-proposals/app/views/decidim/proposals/proposals/index.html.erb +0 -4
  185. data/decidim-proposals/app/views/decidim/proposals/proposals/new.html.erb +1 -1
  186. data/decidim-proposals/app/views/decidim/proposals/proposals/show.html.erb +2 -6
  187. data/decidim-proposals/config/locales/ca.yml +8 -1
  188. data/decidim-proposals/config/locales/en.yml +8 -1
  189. data/decidim-proposals/config/locales/es.yml +8 -1
  190. data/decidim-proposals/lib/decidim/proposals/admin_engine.rb +7 -0
  191. data/decidim-proposals/lib/decidim/proposals/feature.rb +6 -2
  192. data/decidim-proposals/spec/features/proposals_spec.rb +226 -40
  193. data/decidim-proposals/spec/models/decidim/proposals/abilities/admin_user_spec.rb +62 -0
  194. data/decidim-proposals/spec/models/decidim/proposals/abilities/process_admin_user_spec.rb +63 -0
  195. data/decidim-proposals/spec/shared/manage_proposals_examples.rb +161 -50
  196. data/decidim-results/app/models/decidim/results/result.rb +21 -0
  197. data/decidim-results/app/views/decidim/results/results/index.html.erb +0 -4
  198. data/decidim-results/app/views/decidim/results/results/show.html.erb +2 -8
  199. data/decidim-results/config/i18n-tasks.yml +1 -0
  200. data/decidim-results/config/locales/ca.yml +5 -2
  201. data/decidim-results/config/locales/en.yml +5 -2
  202. data/decidim-results/config/locales/es.yml +5 -2
  203. data/decidim-results/lib/decidim/results/feature.rb +2 -2
  204. data/decidim-results/spec/services/result_stats_calculator_spec.rb +2 -2
  205. data/decidim-results/spec/shared/admin_shared_context.rb +1 -1
  206. data/decidim-system/config/i18n-tasks.yml +1 -1
  207. data/package.json +50 -51
  208. data/yarn.lock +198 -151
  209. metadata +69 -36
  210. data/decidim-admin/spec/features/admin_access_control.rb +0 -52
  211. data/decidim-api/lib/decidim/api/types/author_interface.rb +0 -13
  212. data/decidim-api/lib/decidim/api/types/localized_string.rb +0 -13
  213. data/decidim-api/lib/decidim/api/types/translated_field.rb +0 -44
  214. data/decidim-api/spec/types/localized_string_type_spec.rb +0 -31
  215. data/decidim-api/spec/types/translated_field_type_spec.rb +0 -69
  216. data/decidim-comments/app/frontend/comments/add_comment_form.fragment.graphql +0 -6
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Admin
4
+ # Delivers the newsletter to its recipients.
5
+ class DeliverNewsletter < Rectify::Command
6
+ # Initializes the command.
7
+ #
8
+ # newsletter - The newsletter to deliver.
9
+ def initialize(newsletter)
10
+ @newsletter = newsletter
11
+ end
12
+
13
+ def call
14
+ @newsletter.with_lock do
15
+ return broadcast(:invalid) if @newsletter.sent?
16
+ send_newsletter!
17
+ end
18
+
19
+ broadcast(:ok, @newsletter)
20
+ end
21
+
22
+ private
23
+
24
+ def send_newsletter!
25
+ NewsletterJob.perform_later(@newsletter)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -22,8 +22,17 @@ module Decidim
22
22
  return broadcast(:invalid, :active_step) if @step.active?
23
23
 
24
24
  @step.destroy!
25
+ reorder_steps
25
26
  broadcast(:ok)
26
27
  end
28
+
29
+ private
30
+
31
+ def reorder_steps
32
+ ReorderParticipatoryProcessSteps
33
+ .new(@participatory_process.steps, @participatory_process.steps.map(&:id))
34
+ .call
35
+ end
27
36
  end
28
37
  end
29
38
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Admin
4
+ # Updates the newsletter given form data.
5
+ class UpdateNewsletter < Rectify::Command
6
+ # Initializes the command.
7
+ #
8
+ # newsletter - The Newsletter to update.
9
+ # form - The form object containing the data to update.
10
+ # user - The user that updates the newsletter.
11
+ def initialize(newsletter, form, user)
12
+ @newsletter = newsletter
13
+ @form = form
14
+ @user = user
15
+ @organization = user.organization
16
+ end
17
+
18
+ def call
19
+ return broadcast(:invalid) unless @form.valid?
20
+ return broadcast(:invalid) if @newsletter.sent?
21
+ return broadcast(:invalid) unless @organization == @newsletter.organization
22
+
23
+ @newsletter.update_attributes!(
24
+ subject: @form.subject,
25
+ body: @form.body,
26
+ author: @user
27
+ )
28
+
29
+ broadcast(:ok, @newsletter)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -15,7 +15,7 @@ module Decidim
15
15
  #
16
16
  # Returns boolean.
17
17
  def matches?
18
- user.organization == organization && ability.can?(:read, :admin_dashboard)
18
+ user && user.organization == organization && ability.can?(:read, :admin_dashboard)
19
19
  end
20
20
 
21
21
  private
@@ -95,7 +95,7 @@ module Decidim
95
95
  @feature.update_attribute(:published_at, Time.current)
96
96
 
97
97
  flash[:notice] = I18n.t("features.publish.success", scope: "decidim.admin")
98
- redirect_to :back
98
+ redirect_to action: :index
99
99
  end
100
100
 
101
101
  def unpublish
@@ -105,7 +105,7 @@ module Decidim
105
105
  @feature.update_attribute(:published_at, nil)
106
106
 
107
107
  flash[:notice] = I18n.t("features.unpublish.success", scope: "decidim.admin")
108
- redirect_to :back
108
+ redirect_to action: :index
109
109
  end
110
110
 
111
111
  private
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # Controller that allows managing user groups at the admin panel.
6
+ #
7
+ class NewslettersController < ApplicationController
8
+ def index
9
+ authorize! :index, Newsletter
10
+ @newsletters = collection.order(Newsletter.arel_table[:created_at].desc)
11
+ end
12
+
13
+ def new
14
+ authorize! :create, Newsletter
15
+ @form = form(NewsletterForm).instance
16
+ end
17
+
18
+ def show
19
+ @newsletter = collection.find(params[:id])
20
+ @email = NewsletterMailer.newsletter(current_user, @newsletter)
21
+ authorize! :read, @newsletter
22
+ end
23
+
24
+ def preview
25
+ @newsletter = collection.find(params[:id])
26
+ authorize! :read, @newsletter
27
+
28
+ email = NewsletterMailer.newsletter(current_user, @newsletter)
29
+ Premailer::Rails::Hook.perform(email)
30
+
31
+ render text: email.html_part.body.decoded
32
+ end
33
+
34
+ def create
35
+ authorize! :create, Newsletter
36
+ @form = form(NewsletterForm).from_params(params)
37
+
38
+ CreateNewsletter.call(@form, current_user) do
39
+ on(:ok) do |newsletter|
40
+ flash.now[:notice] = I18n.t("newsletters.create.success", scope: "decidim.admin")
41
+ redirect_to action: :show, id: newsletter.id
42
+ end
43
+
44
+ on(:invalid) do |newsletter|
45
+ @newsletter = newsletter
46
+ flash.now[:error] = I18n.t("newsletters.create.error", scope: "decidim.admin")
47
+ render action: :new
48
+ end
49
+ end
50
+ end
51
+
52
+ def edit
53
+ @newsletter = collection.find(params[:id])
54
+ authorize! :update, @newsletter
55
+ @form = form(NewsletterForm).from_model(@newsletter)
56
+ end
57
+
58
+ def update
59
+ @newsletter = collection.find(params[:id])
60
+ authorize! :update, Newsletter
61
+ @form = form(NewsletterForm).from_params(params)
62
+
63
+ UpdateNewsletter.call(@newsletter, @form, current_user) do
64
+ on(:ok) do |newsletter|
65
+ flash.now[:notice] = I18n.t("newsletters.update.success", scope: "decidim.admin")
66
+ redirect_to action: :show, id: newsletter.id
67
+ end
68
+
69
+ on(:invalid) do |newsletter|
70
+ @newsletter = newsletter
71
+ flash.now[:error] = I18n.t("newsletters.update.error", scope: "decidim.admin")
72
+ render action: :edit
73
+ end
74
+ end
75
+ end
76
+
77
+ def destroy
78
+ @newsletter = collection.find(params[:id])
79
+ authorize! :destroy, @newsletter
80
+
81
+ if @newsletter.sent?
82
+ flash.now[:error] = I18n.t("newsletters.destroy.error_already_sent", scope: "decidim.admin")
83
+ redirect_to :back
84
+ else
85
+ @newsletter.destroy!
86
+ flash[:notice] = I18n.t("newsletters.destroy.success", scope: "decidim.admin")
87
+ redirect_to action: :index
88
+ end
89
+ end
90
+
91
+ def deliver
92
+ @newsletter = collection.find(params[:id])
93
+ authorize! :update, @newsletter
94
+
95
+ DeliverNewsletter.call(@newsletter) do
96
+ on(:ok) do
97
+ flash[:notice] = I18n.t("newsletters.deliver.success", scope: "decidim.admin")
98
+ redirect_to action: :index
99
+ end
100
+
101
+ on(:invalid) do
102
+ flash[:error] = I18n.t("newsletters.deliver.error", scope: "decidim.admin")
103
+ redirect_to action: :show
104
+ end
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def collection
111
+ Newsletter.where(organization: current_organization)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -24,9 +24,9 @@ module Decidim
24
24
  @form = form(ParticipatoryProcessForm).from_params(params)
25
25
 
26
26
  CreateParticipatoryProcess.call(@form) do
27
- on(:ok) do
27
+ on(:ok) do |participatory_process|
28
28
  flash[:notice] = I18n.t("participatory_processes.create.success", scope: "decidim.admin")
29
- redirect_to participatory_processes_path
29
+ redirect_to participatory_process_path(participatory_process)
30
30
  end
31
31
 
32
32
  on(:invalid) do
@@ -48,9 +48,9 @@ module Decidim
48
48
  @form = form(ParticipatoryProcessForm).from_params(params)
49
49
 
50
50
  UpdateParticipatoryProcess.call(@participatory_process, @form) do
51
- on(:ok) do
51
+ on(:ok) do |participatory_process|
52
52
  flash[:notice] = I18n.t("participatory_processes.update.success", scope: "decidim.admin")
53
- redirect_to participatory_processes_path
53
+ redirect_to participatory_process_path(participatory_process)
54
54
  end
55
55
 
56
56
  on(:invalid) do
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Admin
4
+ # The form that validates the data to construct a valid Newsletter.
5
+ class NewsletterForm < Decidim::Form
6
+ mimic :newsletter
7
+
8
+ include TranslatableAttributes
9
+
10
+ translatable_attribute :subject, String
11
+ translatable_attribute :body, String
12
+ validates :subject, :body, translatable_presence: true
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Admin
4
+ # Custom ApplicationJob scoped to the admin panel.
5
+ #
6
+ class NewsletterDeliveryJob < ApplicationJob
7
+ queue_as :newsletter
8
+
9
+ def perform(user, newsletter)
10
+ NewsletterMailer.newsletter(user, newsletter).deliver_now
11
+
12
+ newsletter.with_lock do
13
+ newsletter.increment!(:total_deliveries)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Admin
4
+ # Custom ApplicationJob scoped to the admin panel.
5
+ #
6
+ class NewsletterJob < ApplicationJob
7
+ queue_as :newsletter
8
+
9
+ def perform(newsletter)
10
+ newsletter.with_lock do
11
+ raise "Newsletter already sent" if newsletter.sent?
12
+
13
+ newsletter.update_attributes!(
14
+ sent_at: Time.current,
15
+ total_recipients: recipients.count,
16
+ total_deliveries: 0
17
+ )
18
+ end
19
+
20
+ recipients.find_each do |user|
21
+ NewsletterDeliveryJob.perform_later(user, newsletter)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def recipients
28
+ @recipients ||= User.where(newsletter_notifications: true).where.not(email: nil)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -15,9 +15,11 @@ module Decidim
15
15
  can :manage, Category
16
16
  can :manage, ParticipatoryProcessUserRole
17
17
  can [:create, :update, :index, :new, :read], StaticPage
18
+
18
19
  can [:update_slug, :destroy], [StaticPage, StaticPageForm] do |page|
19
20
  !StaticPage.default?(page.slug)
20
21
  end
22
+
21
23
  can [:read, :update], Decidim::Organization do |organization|
22
24
  organization == user.organization
23
25
  end
@@ -27,7 +29,9 @@ module Decidim
27
29
  can :manage, :admin_users
28
30
  can :manage, Attachment
29
31
  can :manage, Scope
32
+ can :manage, Newsletter
30
33
  can [:create, :index, :new, :read, :invite], User
34
+
31
35
  can [:destroy], [User] do |user_to_destroy|
32
36
  user != user_to_destroy
33
37
  end
@@ -12,6 +12,10 @@ module Decidim
12
12
  def initialize(user, context)
13
13
  merge ::Decidim::Ability.new(user, context)
14
14
  merge ParticipatoryProcessAdmin.new(user, context)
15
+
16
+ Decidim.admin_abilities.each do |ability|
17
+ merge ability.constantize.new(user, context)
18
+ end
15
19
  end
16
20
  end
17
21
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Admin
4
+ module Abilities
5
+ # Defines the abilities for a collaborator user in the admin
6
+ # section. Intended to be used with `cancancan`.
7
+ class CollaboratorUser
8
+ include CanCan::Ability
9
+
10
+ def initialize(user, _context)
11
+ return unless user && user.role?(:collaborator)
12
+
13
+ can :manage, :admin_dashboard
14
+ can :preview, ParticipatoryProcess
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -14,7 +14,7 @@ module Decidim
14
14
  def initialize(user, _context)
15
15
  @user = user
16
16
 
17
- return unless user && !user.role?(:admin)
17
+ return unless user && !user.role?(:admin) && !user.role?(:collaborator)
18
18
 
19
19
  can :read, :admin_dashboard do
20
20
  participatory_processes.any?
@@ -22,7 +22,7 @@ module Decidim
22
22
  #
23
23
  # Returns an ActiveRecord::Relation.
24
24
  def query
25
- return user.organization.participatory_processes if user.role?(:admin)
25
+ return user.organization.participatory_processes if user.role?(:admin) || user.role?(:collaborator)
26
26
 
27
27
  ParticipatoryProcess.where(id: process_ids)
28
28
  end
@@ -0,0 +1,5 @@
1
+ <div class="field">
2
+ <%= form.translated :text_field, :subject %>
3
+ <%= form.translated :editor, :body %>
4
+ <%= t('.interpolations_hint') %>
5
+ </div>
@@ -0,0 +1,11 @@
1
+ <% provide :title do %>
2
+ <h2><%= t ".title" %></h2>
3
+ <% end %>
4
+
5
+ <%= decidim_form_for(@form) do |f| %>
6
+ <%= render partial: 'form', object: f %>
7
+
8
+ <div class="actions">
9
+ <%= f.submit t(".save_and_preview") %>
10
+ </div>
11
+ <% end %>
@@ -0,0 +1,47 @@
1
+ <div id="newsletters">
2
+ <% provide :title do %>
3
+ <h2><%= t ".title" %></h2>
4
+ <% end %>
5
+
6
+ <div class="actions title">
7
+ <%= link_to t("actions.new", scope: "decidim.admin", name: t("models.newsletter.name", scope: "decidim.admin").downcase), ['new', 'newsletter'], class: 'new' %>
8
+ </div>
9
+
10
+
11
+ <table class="stack newsletters">
12
+ <thead>
13
+ <tr>
14
+ <th><%= t("models.newsletter.fields.subject", scope: "decidim.admin") %></th>
15
+ <th><%= t("models.newsletter.fields.created_at", scope: "decidim.admin") %></th>
16
+ <th><%= t("models.newsletter.fields.sent_at", scope: "decidim.admin") %></th>
17
+ <th><%= t("models.newsletter.fields.progress", scope: "decidim.admin") %></th>
18
+ <th class="actions"><%= t("actions.title", scope: "decidim.admin") %></th>
19
+ </tr>
20
+ </thead>
21
+ <tbody>
22
+ <% @newsletters.each do |newsletter| %>
23
+ <tr data-newsletter-id="<%= newsletter.id %>">
24
+ <td><%= translated_attribute newsletter.subject %></td>
25
+ <td><%= l newsletter.created_at, format: :long if newsletter.created_at %></td>
26
+ <td><%= l newsletter.sent_at, format: :long if newsletter.sent_at %></td>
27
+ <td>
28
+ <% if newsletter.sent? %>
29
+ <%= "#{newsletter.total_deliveries} / #{newsletter.total_recipients}" %>
30
+ <% end %>
31
+ </td>
32
+ <td class="actions">
33
+ <%= link_to t("actions.preview", scope: "decidim.admin"), decidim_admin.newsletter_path(newsletter) %>
34
+
35
+ <% if can?(:update, newsletter) && !newsletter.sent? %>
36
+ <%= link_to t("actions.edit", scope: "decidim.admin"), decidim_admin.edit_newsletter_path(newsletter) %>
37
+ <% end %>
38
+
39
+ <% if can?(:destroy, newsletter) && !newsletter.sent? %>
40
+ <%= link_to t("actions.destroy", scope: "decidim.admin"), decidim_admin.newsletter_path(newsletter), method: :delete, class: "button", data: { confirm: t(".confirm_delete") } %>
41
+ <% end %>
42
+ </td>
43
+ </tr>
44
+ <% end %>
45
+ </tbody>
46
+ </table>
47
+ </div>