decidim-core 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (194) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/decidim_core_manifest.js +1 -0
  3. data/app/assets/javascripts/decidim/notifications.js.es6 +33 -0
  4. data/app/assets/stylesheets/decidim/extras/_meeting-registrations.scss +11 -0
  5. data/app/assets/stylesheets/decidim/modules/_extra.scss +4 -0
  6. data/app/assets/stylesheets/decidim/modules/_process-nav.scss +14 -0
  7. data/app/commands/decidim/create_follow.rb +40 -0
  8. data/app/commands/decidim/create_omniauth_registration.rb +0 -2
  9. data/app/commands/decidim/create_registration.rb +1 -3
  10. data/app/commands/decidim/create_report.rb +7 -7
  11. data/app/commands/decidim/delete_follow.rb +37 -0
  12. data/app/commands/decidim/invite_user.rb +1 -3
  13. data/app/commands/decidim/update_notifications_settings.rb +1 -2
  14. data/app/constraints/decidim/current_feature.rb +3 -4
  15. data/app/controllers/concerns/decidim/settings.rb +1 -1
  16. data/app/controllers/decidim/features/base_controller.rb +5 -6
  17. data/app/controllers/decidim/follows_controller.rb +45 -0
  18. data/app/controllers/decidim/notifications_controller.rb +39 -0
  19. data/app/controllers/decidim/pages_controller.rb +2 -2
  20. data/app/controllers/decidim/scopes_controller.rb +2 -0
  21. data/app/controllers/decidim/widgets_controller.rb +3 -3
  22. data/app/forms/decidim/follow_form.rb +20 -0
  23. data/app/forms/decidim/invite_user_form.rb +1 -1
  24. data/app/forms/decidim/notifications_settings_form.rb +2 -4
  25. data/app/helpers/decidim/action_authorization_helper.rb +2 -1
  26. data/app/helpers/decidim/decidim_form_helper.rb +4 -0
  27. data/app/helpers/decidim/feature_path_helper.rb +2 -13
  28. data/app/helpers/decidim/icon_helper.rb +26 -8
  29. data/app/helpers/decidim/paginate_helper.rb +1 -1
  30. data/app/helpers/decidim/scopes_helper.rb +1 -1
  31. data/app/jobs/decidim/email_notification_generator_job.rb +12 -0
  32. data/app/jobs/decidim/export_job.rb +1 -3
  33. data/app/jobs/decidim/notification_generator_for_recipient_job.rb +14 -0
  34. data/app/jobs/decidim/notification_generator_job.rb +12 -0
  35. data/app/mailers/decidim/export_mailer.rb +6 -7
  36. data/app/mailers/decidim/notification_mailer.rb +20 -0
  37. data/app/mailers/decidim/reported_mailer.rb +3 -3
  38. data/app/models/decidim/abilities/admin_ability.rb +0 -1
  39. data/app/models/decidim/abilities/base_ability.rb +8 -0
  40. data/app/models/decidim/abilities/everyone_ability.rb +0 -2
  41. data/app/models/decidim/abilities/participatory_process_admin_ability.rb +1 -1
  42. data/app/models/decidim/abilities/participatory_process_collaborator_ability.rb +1 -1
  43. data/app/models/decidim/category.rb +4 -4
  44. data/app/models/decidim/feature.rb +29 -4
  45. data/app/models/decidim/follow.rb +10 -0
  46. data/app/models/decidim/moderation.rb +1 -1
  47. data/app/models/decidim/notification.rb +12 -0
  48. data/app/models/decidim/scope.rb +8 -8
  49. data/app/models/decidim/user.rb +11 -1
  50. data/app/presenters/decidim/home_stats_presenter.rb +1 -1
  51. data/app/presenters/decidim/resource_locator_presenter.rb +3 -18
  52. data/app/queries/decidim/participatory_processes_with_user_role.rb +1 -1
  53. data/app/services/decidim/email_notification_generator.rb +63 -0
  54. data/app/services/decidim/events_manager.rb +41 -0
  55. data/app/services/decidim/notification_generator.rb +51 -0
  56. data/app/services/decidim/notification_generator_for_recipient.rb +50 -0
  57. data/app/uploaders/decidim/attachment_uploader.rb +1 -1
  58. data/app/views/decidim/authorizations/first_login.html.erb +2 -2
  59. data/app/views/decidim/follows/update_button.js.erb +3 -0
  60. data/app/views/decidim/notification_mailer/event_received.html.erb +9 -0
  61. data/app/views/decidim/notifications/_notification.html.erb +18 -0
  62. data/app/views/decidim/notifications/index.html.erb +32 -0
  63. data/app/views/decidim/notifications_settings/show.html.erb +3 -10
  64. data/app/views/decidim/shared/_follow_button.html.erb +19 -0
  65. data/app/views/layouts/decidim/_head.html.erb +1 -0
  66. data/app/views/layouts/decidim/_head_extra.html.erb +7 -0
  67. data/app/views/layouts/decidim/_user_menu.html.erb +1 -0
  68. data/app/views/layouts/decidim/_wrapper.html.erb +3 -0
  69. data/app/views/layouts/decidim/widget.html.erb +3 -3
  70. data/app/views/pages/home/_hero.html.erb +1 -1
  71. data/app/views/pages/home/_highlighted_processes.html.erb +3 -3
  72. data/config/locales/ca.yml +29 -74
  73. data/config/locales/en.yml +23 -71
  74. data/config/locales/es.yml +31 -78
  75. data/config/locales/eu.yml +61 -65
  76. data/config/locales/fi.yml +91 -51
  77. data/config/locales/fr.yml +40 -83
  78. data/config/locales/it.yml +129 -54
  79. data/config/locales/nl.yml +90 -51
  80. data/config/locales/pl.yml +319 -10
  81. data/config/locales/uk.yml +400 -0
  82. data/config/routes.rb +8 -14
  83. data/db/migrate/20170720120231_make_moderations_polymorphic.rb +29 -0
  84. data/db/migrate/20170726145242_make_categories_polymorphic.rb +27 -0
  85. data/db/migrate/20170807123535_create_decidim_follows.rb +20 -0
  86. data/db/migrate/20170808071019_create_decidim_notifications.rb +13 -0
  87. data/db/migrate/20170906091718_add_extra_to_notifications.rb +7 -0
  88. data/db/migrate/20170912082054_add_emails_on_notifications_flag_to_user.rb +7 -0
  89. data/db/migrate/20170913092351_add_header_snippets_to_organizations.rb +7 -0
  90. data/db/migrate/20170914075721_remove_followable_index_from_follows.rb +7 -0
  91. data/db/migrate/20170914092116_remove_comment_and_replies_notifications_from_users.rb +8 -0
  92. data/db/seeds.rb +2 -108
  93. data/lib/decidim/core.rb +46 -8
  94. data/lib/decidim/core/engine.rb +20 -6
  95. data/lib/decidim/core/test.rb +3 -0
  96. data/lib/decidim/core/test/factories.rb +37 -75
  97. data/lib/decidim/core/test/shared_examples/comments_examples.rb +0 -26
  98. data/lib/decidim/core/test/shared_examples/follows_examples.rb +37 -0
  99. data/lib/decidim/core/test/shared_examples/manage_moderations_examples.rb +1 -1
  100. data/lib/decidim/core/test/shared_examples/process_announcements_examples.rb +1 -1
  101. data/lib/decidim/core/test/shared_examples/reportable.rb +4 -4
  102. data/lib/decidim/core/test/shared_examples/reports_examples.rb +1 -1
  103. data/lib/decidim/core/test/shared_examples/scope_helper_examples.rb +41 -0
  104. data/lib/decidim/core/test/shared_examples/user_localised_email_examples.rb +25 -0
  105. data/lib/decidim/core/version.rb +1 -1
  106. data/lib/decidim/engine_router.rb +54 -0
  107. data/lib/decidim/events.rb +9 -0
  108. data/lib/decidim/events/base_event.rb +70 -0
  109. data/lib/decidim/events/email_event.rb +39 -0
  110. data/lib/decidim/events/notification_event.rb +32 -0
  111. data/lib/decidim/exporters.rb +7 -0
  112. data/lib/decidim/exporters/export_data.rb +14 -0
  113. data/lib/decidim/feature_manifest.rb +2 -2
  114. data/lib/decidim/features/namer.rb +1 -1
  115. data/lib/decidim/followable.rb +13 -0
  116. data/lib/decidim/form_builder.rb +1 -1
  117. data/lib/decidim/has_feature.rb +1 -1
  118. data/lib/decidim/has_settings.rb +19 -15
  119. data/lib/decidim/manifest_registry.rb +1 -3
  120. data/lib/decidim/participable.rb +80 -0
  121. data/lib/decidim/participatory_space_manifest.rb +40 -0
  122. data/lib/decidim/query_extensions.rb +1 -1
  123. data/lib/decidim/resource_manifest.rb +1 -1
  124. data/lib/decidim/settings_manifest.rb +0 -4
  125. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.uk.js +14 -0
  126. data/vendor/assets/javascripts/morphdom.js +14 -5
  127. metadata +70 -79
  128. data/app/assets/images/decidim/process.svg +0 -10
  129. data/app/constraints/decidim/current_participatory_process.rb +0 -35
  130. data/app/controllers/concerns/decidim/needs_participatory_process.rb +0 -46
  131. data/app/controllers/decidim/participatory_process_groups_controller.rb +0 -26
  132. data/app/controllers/decidim/participatory_process_steps_controller.rb +0 -18
  133. data/app/controllers/decidim/participatory_process_widgets_controller.rb +0 -19
  134. data/app/controllers/decidim/participatory_processes_controller.rb +0 -49
  135. data/app/helpers/decidim/participatory_process_helper.rb +0 -17
  136. data/app/helpers/decidim/participatory_process_steps_helper.rb +0 -18
  137. data/app/models/decidim/participatory_process.rb +0 -61
  138. data/app/models/decidim/participatory_process_group.rb +0 -15
  139. data/app/models/decidim/participatory_process_step.rb +0 -39
  140. data/app/presenters/decidim/participatory_process_stats_presenter.rb +0 -50
  141. data/app/queries/decidim/highlighted_participatory_processes.rb +0 -10
  142. data/app/queries/decidim/organization_participatory_process_groups.rb +0 -14
  143. data/app/queries/decidim/organization_participatory_processes.rb +0 -14
  144. data/app/queries/decidim/organization_prioritized_participatory_processes.rb +0 -18
  145. data/app/queries/decidim/organization_published_participatory_processes.rb +0 -17
  146. data/app/queries/decidim/prioritized_participatory_processes.rb +0 -11
  147. data/app/queries/decidim/promoted_participatory_processes.rb +0 -10
  148. data/app/queries/decidim/published_participatory_processes.rb +0 -10
  149. data/app/views/decidim/participatory_process_groups/_participatory_process_group.html.erb +0 -22
  150. data/app/views/decidim/participatory_process_groups/show.html.erb +0 -11
  151. data/app/views/decidim/participatory_process_steps/_participatory_process_step.html.erb +0 -16
  152. data/app/views/decidim/participatory_process_steps/_timeline.html.erb +0 -7
  153. data/app/views/decidim/participatory_process_steps/index.html.erb +0 -14
  154. data/app/views/decidim/participatory_process_widgets/show.html.erb +0 -17
  155. data/app/views/decidim/participatory_processes/_no_processes_yet.html.erb +0 -3
  156. data/app/views/decidim/participatory_processes/_order_by_processes.html.erb +0 -3
  157. data/app/views/decidim/participatory_processes/_participatory_process.html.erb +0 -28
  158. data/app/views/decidim/participatory_processes/_promoted_process.html.erb +0 -32
  159. data/app/views/decidim/participatory_processes/_statistics.html.erb +0 -10
  160. data/app/views/decidim/participatory_processes/index.html.erb +0 -17
  161. data/app/views/decidim/participatory_processes/show.html.erb +0 -85
  162. data/app/views/layouts/decidim/_process_header.html.erb +0 -65
  163. data/app/views/layouts/decidim/_process_header_steps.html.erb +0 -27
  164. data/app/views/layouts/decidim/participatory_process.html.erb +0 -30
  165. data/config/i18n-tasks.yml +0 -138
  166. data/db/migrate/20161005130108_add_participatory_processes.rb +0 -19
  167. data/db/migrate/20161010102356_translate_processes.rb +0 -17
  168. data/db/migrate/20161011125616_add_hero_image_to_processes.rb +0 -7
  169. data/db/migrate/20161011141033_add_banner_image_to_processes.rb +0 -7
  170. data/db/migrate/20161013134732_add_promoted_flag_to_processes.rb +0 -7
  171. data/db/migrate/20161017085822_add_participatory_process_steps.rb +0 -18
  172. data/db/migrate/20161019072016_add_active_flag_to_step.rb +0 -13
  173. data/db/migrate/20161020080756_add_position_to_steps.rb +0 -9
  174. data/db/migrate/20161025125300_add_published_at_to_processes.rb +0 -7
  175. data/db/migrate/20161107152228_remove_not_null_on_step_position.rb +0 -7
  176. data/db/migrate/20161110092735_add_index_for_process_slug_organization.rb +0 -10
  177. data/db/migrate/20161116115156_create_attachments.rb +0 -18
  178. data/db/migrate/20170116135237_loosen_step_requirements.rb +0 -8
  179. data/db/migrate/20170123134023_make_attachments_polymorphic.rb +0 -20
  180. data/db/migrate/20170125135937_rename_attachable_to_attached_to.rb +0 -13
  181. data/db/migrate/20170126151123_add_extra_info_to_processes.rb +0 -10
  182. data/db/migrate/20170206083118_rename_extra_info_on_processes.rb +0 -14
  183. data/db/migrate/20170220110740_remove_steps_short_description.rb +0 -23
  184. data/db/migrate/20170221094835_add_scopes_to_processes.rb +0 -8
  185. data/db/migrate/20170228142440_add_participatory_process_groups.rb +0 -17
  186. data/db/migrate/20170404132616_change_steps_end_and_start_date_to_date.rb +0 -8
  187. data/db/migrate/20170725085104_add_show_statistics_to_participatory_processes.rb +0 -7
  188. data/db/migrate/20170804125402_attachment_description_nullable.rb +0 -7
  189. data/db/migrate/20170808080905_add_announcement_to_participatory_processes.rb +0 -7
  190. data/db/migrate/20170809084005_add_scopes_enabled_to_participatory_processes.rb +0 -7
  191. data/db/seeds/Exampledocument.pdf +0 -0
  192. data/db/seeds/city.jpeg +0 -0
  193. data/db/seeds/city2.jpeg +0 -0
  194. data/lib/decidim/notifiable.rb +0 -22
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ class Follow < ApplicationRecord
5
+ belongs_to :followable, foreign_key: "decidim_followable_id", foreign_type: "decidim_followable_type", polymorphic: true
6
+ belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User"
7
+
8
+ validates :user, uniqueness: { scope: [:followable] }
9
+ end
10
+ end
@@ -4,7 +4,7 @@ module Decidim
4
4
  # A moderation belongs to a reportable and includes many reports
5
5
  class Moderation < ApplicationRecord
6
6
  belongs_to :reportable, foreign_key: "decidim_reportable_id", foreign_type: "decidim_reportable_type", polymorphic: true
7
- belongs_to :participatory_process, foreign_key: "decidim_participatory_process_id", class_name: "Decidim::ParticipatoryProcess"
7
+ belongs_to :participatory_space, foreign_key: "decidim_participatory_space_id", foreign_type: "decidim_participatory_space_type", polymorphic: true
8
8
  has_many :reports, foreign_key: "decidim_moderation_id", class_name: "Decidim::Report"
9
9
 
10
10
  delegate :feature, :organization, to: :reportable
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ class Notification < ApplicationRecord
5
+ belongs_to :resource, foreign_key: "decidim_resource_id", foreign_type: "decidim_resource_type", polymorphic: true
6
+ belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User"
7
+
8
+ def event_class_instance
9
+ @event_class_instance ||= event_class.constantize.new(resource: resource, event_name: event_name, user: user, extra: extra)
10
+ end
11
+ end
12
+ end
@@ -46,11 +46,11 @@ module Decidim
46
46
  organization.scopes.where("? = ANY(decidim_scopes.part_of)", id)
47
47
  end
48
48
 
49
- # Gets the scopes from the part_of list
49
+ # Gets the scopes from the part_of list in descending order (first the top level scope, last itself)
50
50
  #
51
- # Returns an ActiveRecord::Relation.
51
+ # Returns an array of Scope objects
52
52
  def part_of_scopes
53
- organization.scopes.where(id: part_of)
53
+ organization.scopes.where(id: part_of).sort { |s1, s2| part_of.index(s2.id) <=> part_of.index(s1.id) }
54
54
  end
55
55
 
56
56
  private
@@ -60,19 +60,19 @@ module Decidim
60
60
  end
61
61
 
62
62
  def create_part_of
63
- self[:part_of] = calculate_part_of
63
+ build_part_of
64
64
  save if changed?
65
65
  end
66
66
 
67
67
  def update_part_of
68
- self[:part_of] = calculate_part_of
68
+ build_part_of
69
69
  end
70
70
 
71
- def calculate_part_of
71
+ def build_part_of
72
72
  if parent
73
- [id] + parent.part_of
73
+ part_of.clear.append(id).concat(parent.reload.part_of)
74
74
  else
75
- [id]
75
+ part_of.clear.append(id)
76
76
  end
77
77
  end
78
78
  end
@@ -18,9 +18,11 @@ module Decidim
18
18
  has_many :identities, foreign_key: "decidim_user_id", class_name: "Decidim::Identity"
19
19
  has_many :memberships, class_name: "Decidim::UserGroupMembership", foreign_key: :decidim_user_id
20
20
  has_many :user_groups, through: :memberships, class_name: "Decidim::UserGroup", foreign_key: :decidim_user_group_id
21
+ has_many :follows, foreign_key: "decidim_user_id", class_name: "Decidim::Follow", dependent: :destroy
22
+ has_many :notifications, foreign_key: "decidim_user_id", class_name: "Decidim::Notification", dependent: :destroy
21
23
 
22
24
  validates :name, presence: true, unless: -> { deleted? }
23
- validates :locale, inclusion: { in: Decidim.available_locales.map(&:to_s) }, allow_blank: true
25
+ validates :locale, inclusion: { in: :available_locales }, allow_blank: true
24
26
  validates :tos_agreement, acceptance: true, allow_nil: false, on: :create
25
27
  validates :avatar, file_size: { less_than_or_equal_to: MAXIMUM_AVATAR_FILE_SIZE }
26
28
  validates :email, uniqueness: { scope: :organization }, unless: -> { deleted? || managed? }
@@ -62,6 +64,10 @@ module Decidim
62
64
  deleted_at.present?
63
65
  end
64
66
 
67
+ def follows?(followable)
68
+ Decidim::Follow.where(user: self, followable: followable).any?
69
+ end
70
+
65
71
  # Check if the user exists with the given email and the current organization
66
72
  #
67
73
  # warden_conditions - A hash with the authentication conditions
@@ -102,5 +108,9 @@ module Decidim
102
108
  def all_roles_are_valid
103
109
  errors.add(:roles, :invalid) unless roles.all? { |role| ROLES.include?(role) }
104
110
  end
111
+
112
+ def available_locales
113
+ Decidim.available_locales.map(&:to_s)
114
+ end
105
115
  end
106
116
  end
@@ -75,7 +75,7 @@ module Decidim
75
75
  end
76
76
 
77
77
  def published_features
78
- @published_features ||= Feature.where(participatory_process: OrganizationPublishedParticipatoryProcesses.new(organization).query).published
78
+ @published_features ||= Feature.where(participatory_space: ParticipatoryProcesses::OrganizationPublishedParticipatoryProcesses.new(organization).query).published
79
79
  end
80
80
  end
81
81
  end
@@ -42,14 +42,14 @@ module Decidim
42
42
  #
43
43
  # Returns a String.
44
44
  def member_route(route_type, options)
45
- route_proxy.send("#{member_route_name}_#{route_type}", member_params.merge(options))
45
+ route_proxy.send("#{member_route_name}_#{route_type}", @resource, options)
46
46
  end
47
47
 
48
48
  # Private: Build the route to the associated collection of resources.
49
49
  #
50
50
  # Returns a String.
51
51
  def collection_route(route_type, options)
52
- route_proxy.send("#{collection_route_name}_#{route_type}", collection_params.merge(options))
52
+ route_proxy.send("#{collection_route_name}_#{route_type}", options)
53
53
  end
54
54
 
55
55
  def manifest
@@ -60,21 +60,6 @@ module Decidim
60
60
  @resource.feature
61
61
  end
62
62
 
63
- def engine
64
- manifest.feature_manifest.engine
65
- end
66
-
67
- def member_params
68
- collection_params.merge(id: @resource.id)
69
- end
70
-
71
- def collection_params
72
- {
73
- feature_id: feature.id,
74
- participatory_process_id: feature.participatory_process.id
75
- }
76
- end
77
-
78
63
  def member_route_name
79
64
  manifest.route_name
80
65
  end
@@ -84,7 +69,7 @@ module Decidim
84
69
  end
85
70
 
86
71
  def route_proxy
87
- @route_proxy ||= engine.routes.url_helpers
72
+ @route_proxy ||= EngineRouter.main_proxy(feature)
88
73
  end
89
74
  end
90
75
  end
@@ -28,7 +28,7 @@ module Decidim
28
28
  # Returns an ActiveRecord::Relation.
29
29
  def query
30
30
  # Admin users have all role privileges for all organization processes
31
- return OrganizationParticipatoryProcesses.new(user.organization).query if user.admin?
31
+ return ParticipatoryProcesses::OrganizationParticipatoryProcesses.new(user.organization).query if user.admin?
32
32
 
33
33
  ParticipatoryProcess.where(id: process_ids)
34
34
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This class handles system events affecting resources and generates a
5
+ # notification for each recipient by scheduling a new
6
+ # `Decidim::NotificationMailer` job for each of them. This way we can
7
+ # easily control which jobs fail and retry them, so that we don't have
8
+ # duplicated notifications.
9
+ class EmailNotificationGenerator
10
+ # Initializes the class.
11
+ #
12
+ # event - A String with the name of the event.
13
+ # event_class - A class that wraps the event.
14
+ # resource - an instance of a class implementing the `Decidim::Resource` concern.
15
+ # extra - a Hash with extra information to be included in the notification.
16
+ def initialize(event, event_class, resource, recipient_ids, extra)
17
+ @event = event
18
+ @event_class = event_class
19
+ @resource = resource
20
+ @recipient_ids = recipient_ids
21
+ @extra = extra
22
+ end
23
+
24
+ # Schedules a job for each recipient to send the email. Returns `nil`
25
+ # if the resource is not resource or if it is not present.
26
+ #
27
+ # Returns nothing.
28
+ def generate
29
+ return unless resource
30
+ return unless event_class.types.include?(:email)
31
+
32
+ recipient_ids.each do |recipient_id|
33
+ send_email_to(recipient_id)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :event, :event_class, :resource, :recipient_ids, :extra
40
+
41
+ # Private: sends the notification email to the user if they have the
42
+ # `email_on_notification` flag active.
43
+ #
44
+ # recipient_id - The ID of the user that will receive the email.
45
+ #
46
+ # Returns nothing.
47
+ def send_email_to(recipient_id)
48
+ recipient = Decidim::User.where(id: recipient_id).first
49
+ return unless recipient
50
+ return unless recipient.email_on_notification?
51
+
52
+ NotificationMailer
53
+ .event_received(
54
+ event,
55
+ event_class.name,
56
+ resource,
57
+ recipient,
58
+ extra
59
+ )
60
+ .deliver_later
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This class acts as a wrapper of `ActiveSupport::Notifications`, so that if
5
+ # we ever need to change the API we just have to change it in a single point.
6
+ class EventsManager
7
+ # Publishes a event through the events channel. It requires the name of an
8
+ # event, a class that handles the event and the resource that received the action.
9
+ #
10
+ # event - a String representing the event that has happened. Ideally, it should
11
+ # start with `decidim.events` and include the name of the engine that publishes
12
+ # it.
13
+ # event_class - The event class must be a class that wraps the event name and
14
+ # the resource and builds the needed information to publish the event to
15
+ # the different subscribers in the system.
16
+ # resource - an instance of a class that received the event.
17
+ # recipient_ids - an Array of IDs of the users that will receive the event
18
+ # extra - a Hash with extra information to be included in the notification.
19
+ #
20
+ # Returns nothing.
21
+ def self.publish(event:, event_class: Decidim::Events::BaseEvent, resource:, recipient_ids:, extra: {})
22
+ ActiveSupport::Notifications.publish(
23
+ event,
24
+ event_class: event_class.name,
25
+ resource: resource,
26
+ recipient_ids: recipient_ids,
27
+ extra: extra
28
+ )
29
+ end
30
+
31
+ # Subscribes to the given event, and runs the block every time that event
32
+ # is received.
33
+ #
34
+ # event - a String or a RegExp to match against event names.
35
+ #
36
+ # Returns nothing.
37
+ def self.subscribe(event, &block)
38
+ ActiveSupport::Notifications.subscribe(event, &block)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This class handles system events affecting resources and generates a
5
+ # notification for each recipient by scheduling a new
6
+ # `Decidim::NotificationGeneratorForRecipientJob` job for each of them. This way
7
+ # we can easily control which jobs fail and retry them, so that we don't have
8
+ # duplicated notifications.
9
+ class NotificationGenerator
10
+ # Initializes the class.
11
+ #
12
+ # event - A String with the name of the event.
13
+ # event_class - A class that wraps the event.
14
+ # resource - an instance of a class implementing the `Decidim::Resource` concern.
15
+ # extra - a Hash with extra information for the event.
16
+ def initialize(event, event_class, resource, recipient_ids, extra)
17
+ @event = event
18
+ @event_class = event_class
19
+ @resource = resource
20
+ @recipient_ids = recipient_ids
21
+ @extra = extra
22
+ end
23
+
24
+ # Schedules a job for each recipient to create the notification. Returns `nil`
25
+ # if the resource is not resource or if it is not present.
26
+ #
27
+ # Returns nothing.
28
+ def generate
29
+ return unless resource
30
+ return unless event_class.types.include?(:notification)
31
+
32
+ recipient_ids.each do |recipient_id|
33
+ generate_notification_for(recipient_id)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :event, :event_class, :resource, :recipient_ids, :extra
40
+
41
+ def generate_notification_for(recipient_id)
42
+ NotificationGeneratorForRecipientJob.perform_later(
43
+ event,
44
+ event_class.name,
45
+ resource,
46
+ recipient_id,
47
+ extra
48
+ )
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This class generates a notification based on the given event, for the given
5
+ # resource/recipient couple. It is intended to be used by the
6
+ # `Decidim::NotificationGenerator` class, which schedules a job for each recipient
7
+ # of the event, so that we can easily control which jobs fail.
8
+ class NotificationGeneratorForRecipient
9
+ # Initializes the class.
10
+ #
11
+ # event - A String with the name of the event.
12
+ # event_class - The class that wraps the event, in order to decorate it.
13
+ # resource - an instance of a class implementing the `Decidim::Resource` concern.
14
+ # recipient - the ID of the User that will receive the notification.
15
+ # extra - a Hash with extra information to be included in the notification.
16
+ def initialize(event, event_class, resource, recipient_id, extra)
17
+ @event = event
18
+ @event_class = event_class
19
+ @resource = resource
20
+ @recipient_id = recipient_id
21
+ @extra = extra
22
+ end
23
+
24
+ # Generates the notification. Returns `nil` if the resource is not resource
25
+ # or if the resource or the user are not present.
26
+ #
27
+ # Returns a Decidim::Notification.
28
+ def generate
29
+ return unless event_class
30
+ return unless resource
31
+ return unless recipient
32
+
33
+ Notification.create!(
34
+ user: recipient,
35
+ event_class: event_class,
36
+ resource: resource,
37
+ event_name: event,
38
+ extra: extra
39
+ )
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :event, :event_class, :resource, :recipient_id, :extra
45
+
46
+ def recipient
47
+ @recipient ||= User.where(id: recipient_id).first
48
+ end
49
+ end
50
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Decidim
4
- # This class deals with uploading attachments to a ParticipatoryProcess.
4
+ # This class deals with uploading attachments to a participatory space.
5
5
  class AttachmentUploader < ApplicationUploader
6
6
  include CarrierWave::MiniMagick
7
7
 
@@ -11,9 +11,9 @@
11
11
  <div class="card">
12
12
  <div class="card__content">
13
13
  <% handlers.each do |handler| %>
14
- <%= link_to t(handler.handler_name, scope: "decidim.authorizations.first_login.actions"), new_authorization_path(handler: handler.handler_name), class: "button expanded" %>
14
+ <%= link_to t("authorizations.first_login.actions.#{handler.handler_name}", scope: "decidim"), new_authorization_path(handler: handler.handler_name), class: "button expanded" %>
15
15
  <% end %>
16
- <p class="text-center skip"><%= t("decidim.authorizations.skip_verification", link: link_to(t("decidim.authorizations.current_participatory_processes"), participatory_processes_path).html_safe).html_safe %>.</p>
16
+ <p class="text-center skip"><%= t("decidim.authorizations.skip_verification", link: link_to(t("decidim.authorizations.current_participatory_processes"), decidim_participatory_processes.participatory_processes_path).html_safe).html_safe %>.</p>
17
17
  </div>
18
18
  </div>
19
19
  </div>
@@ -0,0 +1,3 @@
1
+ var $followResource = $("#follow-resource");
2
+
3
+ morphdom($followResource[0], "<%= j(render partial: "decidim/shared/follow_button", locals: { followable: resource } ).strip.html_safe %>");
@@ -0,0 +1,9 @@
1
+ <p><%= @event_instance.email_greeting %></p>
2
+
3
+ <p><%= @event_instance.email_intro %></p>
4
+
5
+ <p>
6
+ <%= link_to @event_instance.resource_url, @event_instance.resource_url %>
7
+ </p>
8
+
9
+ <p><%= @event_instance.email_outro %></p>
@@ -0,0 +1,18 @@
1
+ <div class="card--list__item">
2
+ <div class="card--list__text">
3
+ <%= link_to notification.event_class_instance.resource_path do %>
4
+ <%= resource_icon notification.resource, class: "icon--chat card--list__icon" %>
5
+ <% end %>
6
+ <div>
7
+ <h5 class="card--list__heading">
8
+ <%= notification.event_class_instance.notification_title %>
9
+ </h5>
10
+ <span class="text-small"><%= l notification.created_at.to_date, format: :long %></span>
11
+ </div>
12
+ </div>
13
+ <div class="card--list__data">
14
+ <%= link_to notification, class: "mark-as-read-button card--list__data__icon", remote: true, method: :delete do %>
15
+ <%= icon "check", class: "icon icon--chevron-right" %>
16
+ <% end %>
17
+ </div>
18
+ </div>
@@ -0,0 +1,32 @@
1
+ <main class="wrapper" id="notifications">
2
+ <div class="row">
3
+ <div class="columns">
4
+ <div class="title-action" >
5
+ <h1 class="heading1 title-action__title"><%= t("title", scope: "layouts.decidim.notifications_dashboard") %></h1>
6
+ <%= link_to(
7
+ t("mark_all_as_read", scope: "layouts.decidim.notifications_dashboard"),
8
+ decidim.read_all_notifications_path,
9
+ class: "button title-action__action hollow mark-all-as-read-button",
10
+ method: :delete,
11
+ data: { disable: true },
12
+ remote: true
13
+ ) %>
14
+ </div>
15
+ </div>
16
+ </div>
17
+ <div class="row">
18
+ <div class="columns mediumlarge-12 large-12">
19
+ <p class="empty-notifications hide"><%= t("no_notifications", scope: "layouts.decidim.notifications_dashboard") %></p>
20
+ <% if notifications.any? %>
21
+ <section class="section" id="notifications-list">
22
+ <div class="card card--list">
23
+ <%= render notifications %>
24
+ </div>
25
+ </section>
26
+ <%= decidim_paginate notifications %>
27
+ <% end %>
28
+ </div>
29
+ </div>
30
+ </main>
31
+
32
+ <%= javascript_include_tag "decidim/notifications" %>