decidim-core 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/decidim/append_redirect_url_to_modals.js.es6 +1 -1
  3. data/app/assets/javascripts/decidim/data_picker.js.es6 +32 -28
  4. data/app/assets/javascripts/decidim/form_filter.component.js.es6 +2 -2
  5. data/app/assets/javascripts/decidim/foundation.js.es6 +1 -2
  6. data/app/assets/stylesheets/decidim/_decidim.scss +6 -1
  7. data/app/assets/stylesheets/decidim/modules/_address.scss +4 -0
  8. data/app/assets/stylesheets/decidim/modules/_block-banner.scss +39 -0
  9. data/app/assets/stylesheets/decidim/modules/_buttons.scss +76 -1
  10. data/app/assets/stylesheets/decidim/modules/_callout.scss +4 -0
  11. data/app/assets/stylesheets/decidim/modules/_cards.scss +117 -16
  12. data/app/assets/stylesheets/decidim/modules/_collapsible-list.scss +19 -0
  13. data/app/assets/stylesheets/decidim/modules/_data-picker.scss +0 -1
  14. data/app/assets/stylesheets/decidim/modules/_docs-manager.scss +16 -0
  15. data/app/assets/stylesheets/decidim/modules/_icons.scss +13 -4
  16. data/app/assets/stylesheets/decidim/modules/_margins.scss +39 -0
  17. data/app/assets/stylesheets/decidim/modules/_modules.scss +4 -0
  18. data/app/assets/stylesheets/decidim/modules/_navbar.scss +26 -0
  19. data/app/assets/stylesheets/decidim/modules/_process-phase.scss +49 -0
  20. data/app/assets/stylesheets/decidim/modules/_reveal.scss +39 -0
  21. data/app/assets/stylesheets/decidim/modules/_typography.scss +45 -0
  22. data/app/assets/stylesheets/decidim/modules/_wizard-steps.scss +45 -0
  23. data/app/assets/stylesheets/decidim/utils/_helpers.scss +14 -0
  24. data/app/assets/stylesheets/decidim/utils/_mixins.scss +34 -0
  25. data/app/assets/stylesheets/decidim/utils/_settings.scss +6 -3
  26. data/app/commands/decidim/create_report.rb +2 -2
  27. data/app/controllers/decidim/devise/invitations_controller.rb +1 -1
  28. data/app/controllers/decidim/features/base_controller.rb +1 -1
  29. data/app/controllers/decidim/locales_controller.rb +1 -1
  30. data/app/forms/decidim/registration_form.rb +1 -1
  31. data/app/helpers/decidim/decidim_form_helper.rb +26 -1
  32. data/app/helpers/decidim/meta_tags_helper.rb +2 -0
  33. data/app/helpers/decidim/resource_reference_helper.rb +24 -0
  34. data/app/helpers/decidim/scopes_helper.rb +22 -4
  35. data/app/helpers/decidim/view_hooks_helper.rb +6 -3
  36. data/app/mailers/decidim/messaging/conversation_mailer.rb +2 -0
  37. data/app/models/decidim/abilities/base_ability.rb +1 -1
  38. data/app/models/decidim/action_log.rb +57 -0
  39. data/app/models/decidim/area.rb +25 -0
  40. data/app/models/decidim/area_type.rb +20 -0
  41. data/app/models/decidim/attachment.rb +3 -0
  42. data/app/models/decidim/attachment_collection.rb +16 -0
  43. data/app/models/decidim/authorization.rb +1 -0
  44. data/app/models/decidim/categorization.rb +2 -0
  45. data/app/models/decidim/category.rb +8 -0
  46. data/app/models/decidim/feature.rb +6 -1
  47. data/app/models/decidim/moderation.rb +7 -0
  48. data/app/models/decidim/newsletter.rb +7 -0
  49. data/app/models/decidim/organization.rb +9 -0
  50. data/app/models/decidim/participatory_process_user_role.rb +9 -0
  51. data/app/models/decidim/resource_link.rb +8 -1
  52. data/app/models/decidim/scope.rb +7 -0
  53. data/app/models/decidim/static_page.rb +7 -0
  54. data/app/models/decidim/user.rb +5 -1
  55. data/app/models/decidim/user_group.rb +7 -0
  56. data/app/presenters/decidim/admin_log/feature_presenter.rb +43 -0
  57. data/app/presenters/decidim/admin_log/moderation_presenter.rb +48 -0
  58. data/app/presenters/decidim/admin_log/newsletter_presenter.rb +47 -0
  59. data/app/presenters/decidim/admin_log/newsletter_resource_presenter.rb +18 -0
  60. data/app/presenters/decidim/admin_log/organization_presenter.rb +84 -0
  61. data/app/presenters/decidim/admin_log/scope_presenter.rb +54 -0
  62. data/app/presenters/decidim/admin_log/static_page_presenter.rb +48 -0
  63. data/app/presenters/decidim/admin_log/static_page_resource_presenter.rb +18 -0
  64. data/app/presenters/decidim/admin_log/user_group_presenter.rb +31 -0
  65. data/app/presenters/decidim/admin_log/user_presenter.rb +59 -0
  66. data/app/presenters/decidim/area_presenter.rb +14 -0
  67. data/app/presenters/decidim/area_type_presenter.rb +14 -0
  68. data/app/presenters/decidim/category_presenter.rb +14 -0
  69. data/app/presenters/decidim/log/base_presenter.rb +253 -0
  70. data/app/presenters/decidim/log/diff_presenter.rb +120 -0
  71. data/app/presenters/decidim/log/resource_presenter.rb +71 -0
  72. data/app/presenters/decidim/log/space_presenter.rb +63 -0
  73. data/app/presenters/decidim/log/user_presenter.rb +85 -0
  74. data/app/presenters/decidim/log/value_types/area_presenter.rb +28 -0
  75. data/app/presenters/decidim/log/value_types/date_presenter.rb +20 -0
  76. data/app/presenters/decidim/log/value_types/default_presenter.rb +43 -0
  77. data/app/presenters/decidim/log/value_types/locale_presenter.rb +20 -0
  78. data/app/presenters/decidim/log/value_types/percentage_presenter.rb +21 -0
  79. data/app/presenters/decidim/log/value_types/scope_presenter.rb +28 -0
  80. data/app/presenters/decidim/log/value_types/scope_type_presenter.rb +28 -0
  81. data/app/presenters/decidim/user_presenter.rb +1 -1
  82. data/app/services/decidim/action_authorizer.rb +1 -1
  83. data/app/services/decidim/action_logger.rb +108 -0
  84. data/app/services/decidim/events_manager.rb +1 -1
  85. data/app/services/decidim/log/diff_changeset_calculator.rb +135 -0
  86. data/app/services/decidim/resource_search.rb +2 -8
  87. data/app/services/decidim/settings_change.rb +31 -0
  88. data/app/services/decidim/traceability.rb +57 -11
  89. data/app/types/decidim/core/component_type.rb +12 -0
  90. data/app/types/decidim/core/date_time_type.rb +12 -0
  91. data/app/types/decidim/core/date_type.rb +13 -0
  92. data/app/types/decidim/core/decidim_type.rb +23 -0
  93. data/app/types/decidim/core/localized_string_type.rb +14 -0
  94. data/app/types/decidim/core/organization_type.rb +20 -0
  95. data/app/types/decidim/core/participatory_space_type.rb +12 -0
  96. data/app/types/decidim/core/session_type.rb +19 -0
  97. data/app/types/decidim/core/statistic_type.rb +22 -0
  98. data/app/types/decidim/core/translated_field_type.rb +45 -0
  99. data/app/types/decidim/core/user_group_type.rb +39 -0
  100. data/app/types/decidim/core/user_type.rb +41 -0
  101. data/app/validators/etiquette_validator.rb +1 -1
  102. data/app/views/decidim/application/_attachments.html.erb +4 -0
  103. data/app/views/decidim/application/_collection.html.erb +14 -0
  104. data/app/views/decidim/application/_document.html.erb +19 -0
  105. data/app/views/decidim/application/_documents.html.erb +8 -23
  106. data/app/views/decidim/shared/_announcement.html.erb +3 -2
  107. data/app/views/decidim/shared/_tags.html.erb +1 -0
  108. data/app/views/decidim/widgets/_data_picker.html.erb +6 -0
  109. data/app/views/layouts/decidim/mailer.html.erb +2 -2
  110. data/config/initializers/devise.rb +3 -3
  111. data/config/locales/ca.yml +65 -11
  112. data/config/locales/en.yml +64 -10
  113. data/config/locales/es.yml +66 -12
  114. data/config/locales/eu.yml +61 -10
  115. data/config/locales/fi.yml +64 -10
  116. data/config/locales/fr.yml +64 -10
  117. data/config/locales/gl.yml +64 -10
  118. data/config/locales/it.yml +64 -10
  119. data/config/locales/nl.yml +64 -10
  120. data/config/locales/pl.yml +60 -10
  121. data/config/locales/pt-BR.yml +64 -10
  122. data/config/locales/pt.yml +64 -10
  123. data/config/locales/sv.yml +64 -10
  124. data/config/locales/uk.yml +72 -0
  125. data/db/migrate/20170215115407_add_organization_custom_reference.rb +1 -1
  126. data/db/migrate/20170313095436_add_available_authorizations_to_organization.rb +1 -1
  127. data/db/migrate/20171207182729_create_decidim_attachment_collections.rb +12 -0
  128. data/db/migrate/20180130093153_add_action_log.rb +18 -0
  129. data/db/migrate/20180206143340_fix_reference_for_all_resources.rb +13 -0
  130. data/db/migrate/20180215104821_create_decidim_area_types.rb +11 -0
  131. data/db/migrate/20180215104945_create_decidim_areas.rb +12 -0
  132. data/db/migrate/20180221101934_fix_nickname_index.rb +1 -1
  133. data/db/migrate/20180226140756_add_version_to_action_logs.rb +19 -0
  134. data/db/migrate/20180314085339_rename_maximum_votes_per_proposal_to_threshold_per_proposal.rb +27 -0
  135. data/db/migrate/20180326075746_change_event_name_and_class_to_rename_to_publish_proposal_event.rb +17 -0
  136. data/db/seeds.rb +28 -0
  137. data/lib/decidim/abilities/participatory_process_role_ability.rb +2 -2
  138. data/lib/decidim/api/author_interface.rb +25 -0
  139. data/lib/decidim/api/component_interface.rb +16 -0
  140. data/lib/decidim/api/participatory_space_interface.rb +38 -0
  141. data/lib/decidim/authorable.rb +8 -0
  142. data/lib/decidim/core.rb +27 -9
  143. data/lib/decidim/core/engine.rb +3 -4
  144. data/lib/decidim/core/test.rb +2 -0
  145. data/lib/decidim/core/test/factories.rb +62 -6
  146. data/lib/decidim/core/test/shared_examples/announcements_examples.rb +1 -1
  147. data/lib/decidim/core/test/shared_examples/comments_examples.rb +47 -5
  148. data/lib/decidim/core/test/shared_examples/component_type.rb +7 -0
  149. data/lib/decidim/core/test/shared_examples/has_attachment_collections.rb +63 -0
  150. data/lib/decidim/core/test/shared_examples/has_attachments.rb +21 -0
  151. data/lib/decidim/core/test/shared_examples/has_reference.rb +1 -1
  152. data/lib/decidim/core/test/shared_examples/simple_event.rb +27 -1
  153. data/lib/decidim/core/version.rb +1 -1
  154. data/lib/decidim/events/author_event.rb +1 -1
  155. data/lib/decidim/events/base_event.rb +1 -1
  156. data/lib/decidim/events/simple_event.rb +21 -0
  157. data/lib/decidim/exporters/excel.rb +1 -1
  158. data/lib/decidim/feature_manifest.rb +2 -0
  159. data/lib/decidim/form_builder.rb +37 -3
  160. data/lib/decidim/has_attachment_collections.rb +18 -0
  161. data/lib/decidim/has_attachments.rb +14 -0
  162. data/lib/decidim/has_category.rb +5 -0
  163. data/lib/decidim/has_reference.rb +4 -4
  164. data/lib/decidim/loggable.rb +32 -0
  165. data/lib/decidim/participable.rb +0 -2
  166. data/lib/decidim/participatory_space_manifest.rb +2 -0
  167. data/lib/decidim/publicable.rb +2 -2
  168. data/lib/decidim/query_extensions.rb +46 -14
  169. data/lib/decidim/resourceable.rb +15 -6
  170. data/lib/decidim/scopable.rb +35 -1
  171. data/lib/decidim/scopable_feature.rb +16 -0
  172. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.ca.js +0 -0
  173. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.es.js +0 -0
  174. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.eu.js +0 -0
  175. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.fi.js +0 -0
  176. data/vendor/assets/javascripts/leaflet.markercluster.js +0 -0
  177. metadata +91 -38
  178. data/app/commands/decidim/remove_admin.rb +0 -25
  179. data/app/helpers/decidim/feature_reference_helper.rb +0 -25
  180. data/lib/decidim/core/api.rb +0 -13
  181. data/lib/decidim/core/api/author_interface.rb +0 -18
  182. data/lib/decidim/core/api/decidim_type.rb +0 -17
  183. data/lib/decidim/core/api/localized_string_type.rb +0 -12
  184. data/lib/decidim/core/api/process_step_type.rb +0 -19
  185. data/lib/decidim/core/api/process_type.rb +0 -17
  186. data/lib/decidim/core/api/session_type.rb +0 -17
  187. data/lib/decidim/core/api/translated_field_type.rb +0 -43
  188. data/lib/decidim/core/api/user_group_type.rb +0 -37
  189. data/lib/decidim/core/api/user_type.rb +0 -39
  190. data/lib/decidim/devise_failure_app.rb +0 -36
  191. data/lib/decidim/has_scope.rb +0 -25
  192. data/lib/decidim/i18n_exceptions.rb +0 -15
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Log
5
+ module ValueTypes
6
+ # This class presents the given value as a percentage. Check
7
+ # the `DefaultPresenter` for more info on how value
8
+ # presenters work.
9
+ class PercentagePresenter < DefaultPresenter
10
+ # Public: Presents the value as a percentage. For clarity,
11
+ # it strips the insignificant zeros.
12
+ #
13
+ # Returns an HTML-safe String.
14
+ def present
15
+ return unless value
16
+ h.number_to_percentage(value, strip_insignificant_zeros: true)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Log
5
+ module ValueTypes
6
+ # This class presents the given value as a Decidim::Scope. Check
7
+ # the `DefaultPresenter` for more info on how value
8
+ # presenters work.
9
+ class ScopePresenter < DefaultPresenter
10
+ # Public: Presents the value as a Decidim::Scope. If the scope can
11
+ # be found, it shows its title. Otherwise it shows its ID.
12
+ #
13
+ # Returns an HTML-safe String.
14
+ def present
15
+ return unless value
16
+ return h.translated_attribute(scope.name) if scope
17
+ I18n.t("not_found", id: value, scope: "decidim.log.value_types.scope_presenter")
18
+ end
19
+
20
+ private
21
+
22
+ def scope
23
+ @scope ||= Decidim::Scope.where(id: value).first
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Log
5
+ module ValueTypes
6
+ # This class presents the given value as a Decidim::ScopeType. Check
7
+ # the `DefaultPresenter` for more info on how value
8
+ # presenters work.
9
+ class ScopeTypePresenter < DefaultPresenter
10
+ # Public: Presents the value as a Percentage. If the scope can
11
+ # be found, it shows its title. Otherwise it shows its ID.
12
+ #
13
+ # Returns an HTML-safe String.
14
+ def present
15
+ return unless value
16
+ return h.translated_attribute(scope_type.name) if scope_type
17
+ I18n.t("not_found", id: value, scope: "decidim.log.value_types.scope_type_presenter")
18
+ end
19
+
20
+ private
21
+
22
+ def scope_type
23
+ @scope_type ||= Decidim::ScopeType.where(id: value).first
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -12,7 +12,7 @@ module Decidim
12
12
  # nickname presented in a twitter-like style
13
13
  #
14
14
  def nickname
15
- "@#{super}"
15
+ "@#{__getobj__.nickname}"
16
16
  end
17
17
 
18
18
  def badge
@@ -43,7 +43,7 @@ module Decidim
43
43
  def authorization
44
44
  return nil unless user && authorization_handler_name
45
45
 
46
- @authorization ||= Verifications::Authorizations.new(user: user, name: authorization_handler_name).first
46
+ @authorization ||= Verifications::Authorizations.new(organization: user.organization, user: user, name: authorization_handler_name).first
47
47
  end
48
48
 
49
49
  def authorization_handler
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # Use this class to log actions by any user. You probably shouldn't
5
+ # use this class dfirectly, but rather use `Decidim.traceability` instead.
6
+ # Check the docs on `Decidim::Traceability` for more info.
7
+ #
8
+ # Usage:
9
+ #
10
+ # ActionLogger.log(:create, user, new_proposal, extra_data)
11
+ class ActionLogger
12
+ # Public: Logs the given `action` by the given `user` on the given `resource`.
13
+ # Delegates the work to the instance method.
14
+ #
15
+ # action - a String representing the name of the action
16
+ # user - the Decidim::User that performed the action
17
+ # resource - the resource onn which the action was performed
18
+ # version_id - the ID of the `PaperTrail::Version` that was created on that action
19
+ # resource_extra - a Hash with resource_extra info to be recorded
20
+ #
21
+ # Returns the newly created `Decidim::ActionLog` resource.
22
+ def self.log(action, user, resource, version_id, resource_extra = {})
23
+ new(action, user, resource, version_id, resource_extra).log!
24
+ end
25
+
26
+ # Public: Initializes the instance.
27
+ #
28
+ # action - a String representing the name of the action
29
+ # user - the Decidim::User that performed the action
30
+ # resource - the resource onn which the action was performed
31
+ # version_id - the ID of the `PaperTrail::Version` that was created on that action
32
+ # resource_extra - a Hash with resource_extra info to be recorded
33
+ def initialize(action, user, resource, version_id = nil, resource_extra = {})
34
+ @action = action
35
+ @user = user
36
+ @resource = resource
37
+ @version_id = version_id
38
+ @resource_extra = resource_extra
39
+ end
40
+
41
+ # Public: Logs the given `action` by the given `user` on the given
42
+ # `resource`.
43
+ #
44
+ # Returns the newly created `Decidim::ActionLog` resource.
45
+ def log!
46
+ Decidim::ActionLog.create!(
47
+ user: user,
48
+ organization: organization,
49
+ action: action,
50
+ resource: resource,
51
+ participatory_space: participatory_space,
52
+ feature: feature,
53
+ version_id: version_id,
54
+ extra: extra_data
55
+ )
56
+ end
57
+
58
+ private
59
+
60
+ attr_reader :action, :user, :resource, :resource_extra, :version_id
61
+
62
+ def organization
63
+ user.organization
64
+ end
65
+
66
+ def feature
67
+ resource.feature if resource.respond_to?(:feature)
68
+ end
69
+
70
+ def participatory_space
71
+ return feature.participatory_space if feature.respond_to?(:participatory_space)
72
+ resource.participatory_space if resource.respond_to?(:participatory_space)
73
+ end
74
+
75
+ def title_for(resource)
76
+ resource.try(:title) || resource.try(:name) || resource.try(:subject)
77
+ end
78
+
79
+ def participatory_space_manifest_name
80
+ participatory_space.try(:class).try(:participatory_space_manifest).try(:name)
81
+ end
82
+
83
+ # Private: Defines some extra data that will be saved in the action log `extra`
84
+ # field.
85
+ #
86
+ # Returns a Hash.
87
+ def extra_data
88
+ {
89
+ feature: {
90
+ manifest_name: feature.try(:manifest_name),
91
+ title: title_for(feature)
92
+ }.compact,
93
+ participatory_space: {
94
+ manifest_name: participatory_space_manifest_name,
95
+ title: title_for(participatory_space)
96
+ }.compact,
97
+ resource: {
98
+ title: title_for(resource)
99
+ }.compact,
100
+ user: {
101
+ ip: user.current_sign_in_ip,
102
+ name: user.name,
103
+ nickname: user.nickname
104
+ }.compact
105
+ }.deep_merge(resource_extra)
106
+ end
107
+ end
108
+ end
@@ -23,7 +23,7 @@ module Decidim
23
23
  event,
24
24
  event_class: event_class.name,
25
25
  resource: resource,
26
- recipient_ids: recipient_ids,
26
+ recipient_ids: recipient_ids.compact.uniq,
27
27
  extra: extra
28
28
  )
29
29
  end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Log
5
+ # This class takes a changeset from a `PaperTrail::Version` and
6
+ # a field mapping and cleans the changeset so it can be easily
7
+ # rendered in the log section. It is intended to be used by the
8
+ # `Decidim::Log::BasePresenter` class, which handles most of the
9
+ # work to render a log.
10
+ #
11
+ # Example:
12
+ #
13
+ # version = resource.versions.last
14
+ # fields_mapping = { updated_at: :date, title: :string }
15
+ # i18n_labels_scope = "activemodel.attributes.my_resource"
16
+ #
17
+ # DiffChangesetCalculator
18
+ # .new(version.changeset, fields_mapping, i18n_labels_scope)
19
+ # .changeset
20
+ class DiffChangesetCalculator
21
+ # original_changeset - a `changeset` from a PaperTrail::Version instance
22
+ # fields_mapping - a Hash mapping attribute names and a type to render them
23
+ # i18n_labels_scope - a String representing the I18n scope where the attribtue
24
+ # labels can be found
25
+ def initialize(original_changeset, fields_mapping, i18n_labels_scope)
26
+ @original_changeset = original_changeset
27
+ @fields_mapping = fields_mapping
28
+ @i18n_labels_scope = i18n_labels_scope
29
+ end
30
+
31
+ # Calculates the changeset that should be rendered, from the
32
+ # `original_changeset` and the `fields_mapping` values.
33
+ #
34
+ # Returns an Array of Hashes.
35
+ def changeset
36
+ original_changeset.inject([]) do |diff, (attribute, values)|
37
+ attribute = attribute.to_sym
38
+
39
+ type = :default
40
+ type = fields_mapping[attribute] unless fields_mapping.nil?
41
+
42
+ if type.blank? || values[0] == values[1]
43
+ diff
44
+ else
45
+ diff.concat(calculate_changeset(attribute, values, type))
46
+ end
47
+ end.compact
48
+ end
49
+
50
+ private
51
+
52
+ attr_reader :fields_mapping, :original_changeset, :i18n_labels_scope
53
+
54
+ # Private: Generates the data structure for the changeset attribute,
55
+ # needed so that the attribute can be rendered in the diff.
56
+ #
57
+ # attribute - the name of the attribute
58
+ # values - an Array of the attribute values: [old_value, new_value]
59
+ # type - a symbol or a String representing the value type presenter
60
+ #
61
+ # Returns an array of hashes.
62
+ def calculate_changeset(attribute, values, type)
63
+ return generate_i18n_changeset(attribute, values, type) if type == :i18n
64
+
65
+ generate_changeset(attribute, values, type)
66
+ end
67
+
68
+ # Private: Generates the data structure for an i18n attribute,
69
+ # needed so that the attribute can be rendered in the diff.
70
+ # Sets the label for the given attribute as: `AttributeName (locale)`.
71
+ #
72
+ # attribute - the name of the attribute
73
+ # values - an Array of the attribute values: [old_value, new_value]
74
+ # type - a symbol or a String representing the value type presenter
75
+ #
76
+ # Returns an array of hashes.
77
+ def generate_i18n_changeset(attribute, values, type)
78
+ values.map! { |value| value.is_a?(String) ? JSON.parse(value) : value }
79
+
80
+ locales = values[0].to_h.keys | values[1].to_h.keys
81
+ locales.flat_map do |locale|
82
+ previous_value = values.first.try(:[], locale)
83
+ new_value = values.last.try(:[], locale)
84
+ if previous_value == new_value
85
+ nil
86
+ else
87
+ label = generate_label(attribute, locale)
88
+ generate_changeset(attribute, [previous_value, new_value], type, label)
89
+ end
90
+ end
91
+ end
92
+
93
+ # Private: Generates the structure needed for the given attribute,
94
+ # values, type and label.
95
+ #
96
+ # attribute - the name of the attribute
97
+ # values - an Array of the attribute values: [old_value, new_value]
98
+ # type - a symbol or a String representing the value type presenter
99
+ # label - the label for the current attribute
100
+ #
101
+ # Returns an array of Hashes.
102
+ def generate_changeset(attribute, values, type, label = nil)
103
+ [
104
+ {
105
+ attribute_name: attribute,
106
+ label: label || generate_label(attribute),
107
+ new_value: values[1],
108
+ previous_value: values[0],
109
+ type: type
110
+ }
111
+ ]
112
+ end
113
+
114
+ # Generates the label for the given attribute. If the `locale` is set,
115
+ # it appends the locale at the end: `AttributeName (LocaleName)`.
116
+ #
117
+ # attribute - A Symbol representing the attribute name. It will retrive
118
+ # this key from the I18n scope set at `i18n_labels_scope`.
119
+ # locale - a String representing the name of the locale.
120
+ #
121
+ # Returns a String.
122
+ def generate_label(attribute, locale = nil)
123
+ label = if i18n_labels_scope
124
+ I18n.t(attribute, scope: i18n_labels_scope, default: attribute.to_s.humanize)
125
+ else
126
+ attribute.to_s.humanize
127
+ end
128
+ return label unless locale
129
+
130
+ locale_name = I18n.t("locale.name", locale: locale)
131
+ "#{label} (#{locale_name})"
132
+ end
133
+ end
134
+ end
135
+ end
@@ -47,15 +47,9 @@ module Decidim
47
47
 
48
48
  conditions = []
49
49
  conditions << "decidim_scope_id IS NULL" if clean_scope_ids.delete("global")
50
+ conditions.concat(["? = ANY(decidim_scopes.part_of)"] * clean_scope_ids.count) if clean_scope_ids.any?
50
51
 
51
- clean_scope_ids.map!(&:to_i)
52
-
53
- if clean_scope_ids.any?
54
- conditions.concat(["? = ANY(decidim_scopes.part_of)"] * clean_scope_ids.count)
55
- conditions << "decidim_scopes.id IN (?)"
56
- end
57
-
58
- query.includes(:scope).references(:decidim_scopes).where(conditions.join(" OR "), *clean_scope_ids, clean_scope_ids)
52
+ query.includes(:scope).references(:decidim_scopes).where(conditions.join(" OR "), *clean_scope_ids.map(&:to_i))
59
53
  end
60
54
 
61
55
  private
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This is a helper class in order to publish feature and settings changes
5
+ # so that components can react to these changes and send notifications to users.
6
+ class SettingsChange
7
+ # Publishes a change to ActiveSupport::Notifications.
8
+ #
9
+ # feature - The Decidim::Feature where the changes have been applied.
10
+ # previous_settings - A Hash or a Decidim::SettingsManifest schema with the settings before changing them.
11
+ # current_settings - A Hash or a Decidim::SettingsManifest schema with the current settings.
12
+ def self.publish(feature, previous_settings, current_settings)
13
+ ActiveSupport::Notifications.publish(
14
+ "decidim.settings_change.#{feature.manifest_name}",
15
+ feature_id: feature.id,
16
+ previous_settings: previous_settings.to_h.deep_symbolize_keys,
17
+ current_settings: current_settings.to_h.deep_symbolize_keys
18
+ )
19
+ end
20
+
21
+ # Creates a subscription to setting changes.
22
+ #
23
+ # scope - The String manifest name of the component so it only receives relevant changes.
24
+ # block - The block to be executed when an event is received.
25
+ def self.subscribe(scope, &block)
26
+ ActiveSupport::Notifications.subscribe(/^decidim\.settings_change\.#{scope}/) do |_event_name, data|
27
+ block.call(data)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -24,11 +24,12 @@ module Decidim
24
24
  #
25
25
  # klass - An ActiveRecord class that implements `Decidim::Traceable`
26
26
  # author - An object that implements `to_gid` or a String
27
- # params - a Hash
27
+ # params - a Hash with the attributes of the new resource
28
+ # extra_log_info - a Hash with extra info that will be saved to the log
28
29
  #
29
30
  # Returns an instance of `klass`.
30
- def create(klass, author, params)
31
- PaperTrail.whodunnit(gid(author)) do
31
+ def create(klass, author, params, extra_log_info = {})
32
+ perform_action!(:create, klass, author, extra_log_info) do
32
33
  klass.create(params)
33
34
  end
34
35
  end
@@ -37,25 +38,51 @@ module Decidim
37
38
  #
38
39
  # klass - An ActiveRecord class that implements `Decidim::Traceable`
39
40
  # author - An object that implements `to_gid` or a String
40
- # params - a Hash
41
+ # params - a Hash with the attributes of the new resource
42
+ # extra_log_info - a Hash with extra info that will be saved to the log
41
43
  #
42
44
  # Returns an instance of `klass`.
43
- def create!(klass, author, params)
44
- PaperTrail.whodunnit(gid(author)) do
45
+ def create!(klass, author, params, extra_log_info = {})
46
+ perform_action!(:create, klass, author, extra_log_info) do
45
47
  klass.create!(params)
46
48
  end
47
49
  end
48
50
 
49
- # Updates the `resource` with `update_attributes!` and sets the author of the version.
51
+ # Performs the given block and sets the author of the action.
52
+ # It also logs the action with the given `action` parameter.
53
+ # The action and the logging are run inside a transaction.
50
54
  #
55
+ # action - a String or Symbol representing the action performed
51
56
  # resource - An ActiveRecord instance that implements `Decidim::Traceable`
52
57
  # author - An object that implements `to_gid` or a String
53
- # params - a Hash
58
+ # extra_log_info - a Hash with extra info that will be saved to the log
54
59
  #
55
- # Returns the updated `resource`.
56
- def update!(resource, author, params)
60
+ # Returns whatever the given block returns.
61
+ def perform_action!(action, resource, author, extra_log_info = {})
57
62
  PaperTrail.whodunnit(gid(author)) do
58
- resource.update_attributes!(params)
63
+ klass = resource.is_a?(Class) ? resource : resource.class
64
+ klass.transaction do
65
+ Decidim::ApplicationRecord.transaction do
66
+ result = block_given? ? yield : nil
67
+ loggable_resource = resource.is_a?(Class) ? result : resource
68
+ log(action, author, loggable_resource, extra_log_info)
69
+ result
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ # Updates the `resource` with `update!` and sets the author of the version.
76
+ #
77
+ # resource - An ActiveRecord instance that implements `Decidim::Traceable`
78
+ # author - An object that implements `to_gid` or a String
79
+ # params - a Hash with the attributes to update to the resource
80
+ # extra_log_info - a Hash with extra info that will be saved to the log
81
+ #
82
+ # Returns the updated `resource`.
83
+ def update!(resource, author, params, extra_log_info = {})
84
+ perform_action!(:update, resource, author, extra_log_info) do
85
+ resource.update!(params)
59
86
  resource
60
87
  end
61
88
  end
@@ -87,5 +114,24 @@ module Decidim
87
114
  return author.to_gid if author.respond_to?(:to_gid)
88
115
  author
89
116
  end
117
+
118
+ def log(action, user, resource, extra_log_info = {})
119
+ return unless user.is_a?(Decidim::User)
120
+
121
+ Decidim::ActionLogger.log(
122
+ action,
123
+ user,
124
+ resource,
125
+ version_id(resource),
126
+ extra_log_info
127
+ )
128
+ end
129
+
130
+ def version_id(resource)
131
+ return nil unless resource.is_a?(Decidim::Traceable)
132
+ return nil if resource.versions.blank?
133
+
134
+ resource.versions.last.id
135
+ end
90
136
  end
91
137
  end