decidim-core 0.8.4 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (249) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +1 -1
  3. data/app/assets/images/decidim/decidim-logo.svg +23 -23
  4. data/app/assets/images/decidim/default-avatar.svg +7 -7
  5. data/app/assets/images/decidim/icons.svg +1 -0
  6. data/app/assets/javascripts/decidim.js.es6 +5 -2
  7. data/app/assets/javascripts/decidim/append_redirect_url_to_modals.js.es6 +84 -0
  8. data/app/assets/javascripts/decidim/data_picker.js.es6 +177 -0
  9. data/app/assets/javascripts/decidim/form_filter.component.js.es6 +25 -20
  10. data/app/assets/javascripts/decidim/form_filter.component.test.js +24 -13
  11. data/app/assets/javascripts/decidim/history.js.es6 +3 -3
  12. data/app/assets/stylesheets/decidim/_decidim.scss +1 -4
  13. data/app/assets/stylesheets/decidim/_variables.scss +4 -4
  14. data/app/assets/stylesheets/decidim/editor.scss +13 -0
  15. data/app/assets/stylesheets/decidim/email.css +9 -1
  16. data/app/assets/stylesheets/decidim/extras/_add_comments.scss +3 -3
  17. data/app/assets/stylesheets/decidim/extras/_announcement.scss +1 -1
  18. data/app/assets/stylesheets/decidim/extras/_collection-sort-controls.scss +2 -2
  19. data/app/assets/stylesheets/decidim/extras/_embed.scss +6 -5
  20. data/app/assets/stylesheets/decidim/extras/_impersonation-bar.scss +4 -0
  21. data/app/assets/stylesheets/decidim/extras/_label-required.scss +1 -1
  22. data/app/assets/stylesheets/decidim/extras/_leaflet.scss +6 -4
  23. data/app/assets/stylesheets/decidim/extras/_meeting-registrations.scss +4 -4
  24. data/app/assets/stylesheets/decidim/extras/_process_stats.scss +11 -4
  25. data/app/assets/stylesheets/decidim/extras/_proposal_form.scss +3 -3
  26. data/app/assets/stylesheets/decidim/extras/_quill.scss +1 -1
  27. data/app/assets/stylesheets/decidim/extras/_reference.scss +3 -3
  28. data/app/assets/stylesheets/decidim/extras/_register_form.scss +3 -3
  29. data/app/assets/stylesheets/decidim/extras/_results-per-page.scss +10 -10
  30. data/app/assets/stylesheets/decidim/extras/_social_icons_mini.scss +4 -3
  31. data/app/assets/stylesheets/decidim/layouts/_highlighted_banner.scss +38 -0
  32. data/app/assets/stylesheets/decidim/layouts/_home.scss +33 -3
  33. data/app/assets/stylesheets/decidim/layouts/_user.scss +33 -5
  34. data/app/assets/stylesheets/decidim/layouts/_view.scss +1 -2
  35. data/app/assets/stylesheets/decidim/map.css +5 -3
  36. data/app/assets/stylesheets/decidim/modules/_address.scss +1 -0
  37. data/app/assets/stylesheets/decidim/modules/_author-avatar.scss +24 -9
  38. data/app/assets/stylesheets/decidim/modules/_buttons.scss +24 -10
  39. data/app/assets/stylesheets/decidim/modules/_callout.scss +5 -1
  40. data/app/assets/stylesheets/decidim/modules/_card-grid.scss +1 -1
  41. data/app/assets/stylesheets/decidim/modules/_cards.scss +102 -31
  42. data/app/assets/stylesheets/decidim/modules/_comments.scss +21 -20
  43. data/app/assets/stylesheets/decidim/modules/_cookie-bar.scss +4 -0
  44. data/app/assets/stylesheets/decidim/modules/_data-picker.scss +159 -0
  45. data/app/assets/stylesheets/decidim/modules/_datepicker.scss +36 -36
  46. data/app/assets/stylesheets/decidim/modules/_definition-data.scss +12 -9
  47. data/app/assets/stylesheets/decidim/modules/_extra.scss +4 -1
  48. data/app/assets/stylesheets/decidim/modules/_filter-tags.scss +3 -0
  49. data/app/assets/stylesheets/decidim/modules/_filters.scss +9 -5
  50. data/app/assets/stylesheets/decidim/modules/_flag.scss +1 -0
  51. data/app/assets/stylesheets/decidim/modules/_footer.scss +10 -0
  52. data/app/assets/stylesheets/decidim/modules/_forms.scss +2 -1
  53. data/app/assets/stylesheets/decidim/modules/_help.scss +2 -0
  54. data/app/assets/stylesheets/decidim/modules/_icons.scss +1 -0
  55. data/app/assets/stylesheets/decidim/modules/_layout.scss +4 -0
  56. data/app/assets/stylesheets/decidim/modules/_list-docs.scss +3 -0
  57. data/app/assets/stylesheets/decidim/modules/_main-container.scss +13 -7
  58. data/app/assets/stylesheets/decidim/modules/_map.scss +13 -1
  59. data/app/assets/stylesheets/decidim/modules/_margins.scss +1 -0
  60. data/app/assets/stylesheets/decidim/modules/_messages.scss +2 -1
  61. data/app/assets/stylesheets/decidim/modules/_modules.scss +3 -0
  62. data/app/assets/stylesheets/decidim/modules/_navbar.scss +36 -16
  63. data/app/assets/stylesheets/decidim/modules/_omnipresent_banner.scss +26 -0
  64. data/app/assets/stylesheets/decidim/modules/_opinion-toggle.scss +5 -0
  65. data/app/assets/stylesheets/decidim/modules/_order-by.scss +10 -10
  66. data/app/assets/stylesheets/decidim/modules/_pagination.scss +3 -2
  67. data/app/assets/stylesheets/decidim/modules/_process-header.scss +11 -2
  68. data/app/assets/stylesheets/decidim/modules/_process-info.scss +9 -1
  69. data/app/assets/stylesheets/decidim/modules/_process-nav.scss +20 -3
  70. data/app/assets/stylesheets/decidim/modules/_process-phase.scss +12 -3
  71. data/app/assets/stylesheets/decidim/modules/_progress-bar.scss +69 -0
  72. data/app/assets/stylesheets/decidim/modules/_reference.scss +1 -2
  73. data/app/assets/stylesheets/decidim/modules/_reveal.scss +1 -1
  74. data/app/assets/stylesheets/decidim/modules/_share.scss +2 -0
  75. data/app/assets/stylesheets/decidim/modules/_signup.scss +5 -1
  76. data/app/assets/stylesheets/decidim/modules/_static-pages.scss +6 -0
  77. data/app/assets/stylesheets/decidim/modules/_status-labels.scss +2 -0
  78. data/app/assets/stylesheets/decidim/modules/_tags.scss +3 -1
  79. data/app/assets/stylesheets/decidim/modules/_timeline.scss +41 -30
  80. data/app/assets/stylesheets/decidim/modules/_title-action.scss +2 -1
  81. data/app/assets/stylesheets/decidim/modules/_typography.scss +13 -4
  82. data/app/assets/stylesheets/decidim/modules/_user-form.scss +1 -0
  83. data/app/assets/stylesheets/decidim/modules/_video.scss +2 -2
  84. data/app/assets/stylesheets/decidim/utils/_fontface.scss +22 -20
  85. data/app/assets/stylesheets/decidim/utils/_helpers.scss +6 -6
  86. data/app/assets/stylesheets/decidim/utils/_keyframes.scss +6 -6
  87. data/app/assets/stylesheets/decidim/utils/_mixins.scss +24 -7
  88. data/app/assets/stylesheets/decidim/utils/_settings.scss +50 -52
  89. data/app/assets/stylesheets/decidim/utils/_toggle-expand.scss +1 -0
  90. data/app/commands/decidim/create_omniauth_registration.rb +3 -0
  91. data/app/commands/decidim/create_registration.rb +3 -1
  92. data/app/commands/decidim/destroy_account.rb +1 -0
  93. data/app/commands/decidim/invite_user_again.rb +1 -1
  94. data/app/commands/decidim/messaging/reply_to_conversation.rb +1 -3
  95. data/app/commands/decidim/unsubscribe_settings.rb +29 -0
  96. data/app/commands/decidim/update_account.rb +16 -3
  97. data/app/controllers/concerns/decidim/action_authorization.rb +1 -1
  98. data/app/controllers/concerns/decidim/devise_controllers.rb +10 -0
  99. data/app/controllers/concerns/decidim/form_factory.rb +2 -1
  100. data/app/controllers/concerns/decidim/impersonate_users.rb +13 -8
  101. data/app/controllers/concerns/decidim/participatory_space_context.rb +1 -1
  102. data/app/controllers/decidim/application_controller.rb +16 -0
  103. data/app/controllers/decidim/cookie_policy_controller.rb +2 -0
  104. data/app/controllers/decidim/devise/invitations_controller.rb +9 -1
  105. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +10 -1
  106. data/app/controllers/decidim/devise/sessions_controller.rb +9 -1
  107. data/app/controllers/decidim/locales_controller.rb +2 -3
  108. data/app/controllers/decidim/messaging/conversations_controller.rb +2 -2
  109. data/app/controllers/decidim/newsletters_controller.rb +60 -0
  110. data/app/controllers/decidim/pages_controller.rb +1 -0
  111. data/app/controllers/decidim/profiles_controller.rb +23 -0
  112. data/app/controllers/decidim/scopes_controller.rb +19 -20
  113. data/app/events/decidim/profile_updated_event.rb +27 -0
  114. data/app/forms/decidim/account_form.rb +34 -0
  115. data/app/forms/decidim/form.rb +1 -0
  116. data/app/forms/decidim/messaging/conversation_form.rb +5 -2
  117. data/app/forms/decidim/omniauth_registration_form.rb +5 -0
  118. data/app/forms/decidim/registration_form.rb +8 -1
  119. data/app/helpers/decidim/action_authorization_helper.rb +2 -2
  120. data/app/helpers/decidim/application_helper.rb +8 -0
  121. data/app/helpers/decidim/feature_path_helper.rb +12 -2
  122. data/app/helpers/decidim/feature_reference_helper.rb +1 -1
  123. data/app/helpers/decidim/messaging/conversation_helper.rb +27 -9
  124. data/app/helpers/decidim/newsletters_helper.rb +49 -0
  125. data/app/helpers/decidim/scopes_helper.rb +43 -2
  126. data/app/helpers/decidim/translations_helper.rb +6 -2
  127. data/app/mailers/decidim/decidim_devise_mailer.rb +1 -3
  128. data/app/mailers/decidim/messaging/conversation_mailer.rb +1 -1
  129. data/app/mailers/decidim/newsletter_mailer.rb +7 -8
  130. data/app/models/decidim/abilities/everyone_ability.rb +1 -0
  131. data/app/models/decidim/authorization.rb +19 -5
  132. data/app/models/decidim/impersonation_log.rb +2 -1
  133. data/app/models/decidim/messaging/conversation.rb +2 -0
  134. data/app/models/decidim/messaging/message.rb +4 -0
  135. data/app/models/decidim/organization.rb +1 -0
  136. data/app/models/decidim/scope.rb +10 -2
  137. data/app/models/decidim/user.rb +10 -2
  138. data/app/presenters/decidim/home_stats_presenter.rb +10 -4
  139. data/app/presenters/decidim/user_group_presenter.rb +28 -0
  140. data/app/presenters/decidim/user_presenter.rb +42 -0
  141. data/app/services/decidim/action_authorizer.rb +32 -68
  142. data/app/services/decidim/notification_generator_for_recipient.rb +8 -3
  143. data/app/uploaders/decidim/avatar_uploader.rb +2 -2
  144. data/app/views/decidim/account/delete.html.erb +1 -1
  145. data/app/views/decidim/account/show.html.erb +4 -1
  146. data/app/views/decidim/devise/invitations/edit.html.erb +2 -0
  147. data/app/views/decidim/devise/omniauth_registrations/new.html.erb +7 -1
  148. data/app/views/decidim/devise/registrations/new.html.erb +7 -1
  149. data/app/views/decidim/messaging/conversations/_message.html.erb +6 -12
  150. data/app/views/decidim/messaging/conversations/_reply.html.erb +1 -1
  151. data/app/views/decidim/messaging/conversations/index.html.erb +1 -1
  152. data/app/views/decidim/messaging/conversations/update.js.erb +1 -0
  153. data/app/views/decidim/newsletter_mailer/newsletter.html.erb +11 -0
  154. data/app/views/decidim/newsletters/show.html.erb +11 -0
  155. data/app/views/decidim/newsletters/unsubscribe.html.erb +4 -0
  156. data/app/views/decidim/notifications/_notification.html.erb +1 -1
  157. data/app/views/decidim/profiles/show.html.erb +64 -0
  158. data/app/views/decidim/scopes/_scopes_picker_input.html.erb +6 -0
  159. data/app/views/decidim/scopes/picker.html.erb +36 -0
  160. data/app/views/decidim/shared/_action_authorization_modal.html.erb +25 -51
  161. data/app/views/decidim/shared/_author.html.erb +21 -0
  162. data/app/views/decidim/shared/_author_reference.html.erb +12 -0
  163. data/app/views/layouts/decidim/_application.html.erb +1 -0
  164. data/app/views/layouts/decidim/_impersonation_warning.html.erb +1 -1
  165. data/app/views/layouts/decidim/_mailer_logo.html.erb +6 -1
  166. data/app/views/layouts/decidim/_omnipresent_banner.html.erb +14 -0
  167. data/app/views/layouts/decidim/_user_menu.html.erb +3 -0
  168. data/app/views/layouts/decidim/mailer.html.erb +16 -4
  169. data/app/views/layouts/decidim/widget.html.erb +14 -9
  170. data/app/views/pages/home.html.erb +2 -0
  171. data/app/views/pages/home/_highlighted_content_banner.html.erb +26 -0
  172. data/config/initializers/devise.rb +1 -3
  173. data/config/locales/ca.yml +67 -9
  174. data/config/locales/en.yml +65 -4
  175. data/config/locales/es.yml +74 -14
  176. data/config/locales/eu.yml +66 -4
  177. data/config/locales/fi.yml +87 -25
  178. data/config/locales/fr.yml +71 -9
  179. data/config/locales/gl.yml +493 -0
  180. data/config/locales/it.yml +79 -17
  181. data/config/locales/nl.yml +71 -9
  182. data/config/locales/pl.yml +66 -4
  183. data/config/locales/pt-BR.yml +493 -0
  184. data/config/locales/pt.yml +99 -37
  185. data/config/locales/ru.yml +85 -13
  186. data/config/locales/sv.yml +493 -0
  187. data/config/locales/uk.yml +78 -16
  188. data/config/routes.rb +11 -1
  189. data/db/migrate/20171212103803_create_unique_nicknames.rb +29 -0
  190. data/db/migrate/20180115090038_extend_user_profile.rb +8 -0
  191. data/db/migrate/20180123125308_add_enable_omnipresent_banner_to_decidim_organizations.rb +7 -0
  192. data/db/migrate/20180123125409_add_omnipresent_banner_title_to_decidim_organizations.rb +7 -0
  193. data/db/migrate/20180123125432_add_omnipresent_banner_short_description_to_decidim_organizations.rb +7 -0
  194. data/db/migrate/20180123125452_add_omnipresent_banner_url_to_decidim_organizations.rb +7 -0
  195. data/db/migrate/20180125063433_add_highlighted_content_banner_to_decidim_organizations.rb +13 -0
  196. data/db/seeds.rb +8 -2
  197. data/lib/decidim/abilities/participatory_process_role_ability.rb +1 -3
  198. data/lib/decidim/content_parsers.rb +8 -0
  199. data/lib/decidim/content_parsers/base_parser.rb +58 -0
  200. data/lib/decidim/content_parsers/user_parser.rb +46 -0
  201. data/lib/decidim/content_processor.rb +84 -0
  202. data/lib/decidim/content_renderers.rb +8 -0
  203. data/lib/decidim/content_renderers/base_renderer.rb +37 -0
  204. data/lib/decidim/content_renderers/user_renderer.rb +32 -0
  205. data/lib/decidim/core.rb +66 -1
  206. data/lib/decidim/core/api/author_interface.rb +3 -3
  207. data/lib/decidim/core/api/user_group_type.rb +10 -8
  208. data/lib/decidim/core/api/user_type.rb +13 -7
  209. data/lib/decidim/core/engine.rb +7 -5
  210. data/lib/decidim/core/test.rb +1 -1
  211. data/lib/decidim/core/test/factories.rb +21 -45
  212. data/lib/decidim/core/test/shared_examples/announcements_examples.rb +3 -2
  213. data/lib/decidim/core/test/shared_examples/comments_examples.rb +5 -2
  214. data/lib/decidim/core/test/shared_examples/scope_helper_examples.rb +40 -3
  215. data/lib/decidim/core/test/shared_examples/simple_event.rb +73 -0
  216. data/lib/decidim/core/version.rb +1 -1
  217. data/lib/decidim/events.rb +2 -0
  218. data/lib/decidim/events/author_event.rb +41 -0
  219. data/lib/decidim/events/base_event.rb +28 -3
  220. data/lib/decidim/events/email_event.rb +1 -1
  221. data/lib/decidim/events/notification_event.rb +1 -1
  222. data/lib/decidim/events/simple_event.rb +79 -0
  223. data/lib/decidim/filter_form_builder.rb +2 -3
  224. data/lib/decidim/form_builder.rb +39 -27
  225. data/lib/decidim/friendly_dates.rb +26 -0
  226. data/lib/decidim/has_feature.rb +1 -0
  227. data/lib/decidim/has_reference.rb +1 -1
  228. data/lib/decidim/i18n_exceptions.rb +1 -3
  229. data/lib/decidim/menu.rb +1 -1
  230. data/lib/decidim/newsletter_encryptor.rb +22 -0
  231. data/lib/decidim/nicknamizable.rb +56 -0
  232. data/lib/decidim/participable.rb +8 -0
  233. data/lib/decidim/participatory_space_manifest.rb +10 -1
  234. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.gl.js +13 -0
  235. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.pt-br.js +14 -0
  236. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.pt.js +5 -1
  237. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.ru.js +4 -1
  238. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.sv.js +14 -0
  239. data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.uk.js +4 -1
  240. data/vendor/assets/javascripts/form_datepicker.js.es6 +4 -2
  241. data/vendor/assets/javascripts/foundation-datepicker.js +42 -26
  242. metadata +124 -84
  243. data/app/assets/javascripts/decidim/select2.field.js.es6 +0 -47
  244. data/app/assets/javascripts/decidim/select2.js.es6 +0 -11
  245. data/app/assets/stylesheets/decidim/editor.sass +0 -4
  246. data/app/assets/stylesheets/decidim/plugins/_select2.scss +0 -63
  247. data/app/helpers/decidim/datetime_helper.rb +0 -23
  248. data/app/queries/decidim/freetext_scopes.rb +0 -39
  249. data/lib/decidim/core/test/shared_examples/manage_moderations_examples.rb +0 -64
@@ -11,8 +11,12 @@ module Decidim
11
11
  #
12
12
  # Returns a String with the translation.
13
13
  def translated_attribute(attribute)
14
- attribute.try(:[], I18n.locale.to_s).presence ||
15
- attribute.try(:[], current_organization.default_locale).presence ||
14
+ return "" if attribute.nil?
15
+ return attribute unless attribute.is_a?(Hash)
16
+
17
+ attribute[I18n.locale.to_s].presence ||
18
+ attribute[current_organization.default_locale].presence ||
19
+ attribute[attribute.keys.first].presence ||
16
20
  ""
17
21
  end
18
22
 
@@ -19,9 +19,7 @@ module Decidim
19
19
  @organization = user.organization
20
20
  @opts = opts
21
21
 
22
- if opts[:invitation_instructions]
23
- opts[:subject] = I18n.t("devise.mailer.#{opts[:invitation_instructions]}.subject", organization: user.organization.name)
24
- end
22
+ opts[:subject] = I18n.t("devise.mailer.#{opts[:invitation_instructions]}.subject", organization: user.organization.name) if opts[:invitation_instructions]
25
23
  end
26
24
 
27
25
  devise_mail(user, opts[:invitation_instructions] || :invitation_instructions, opts)
@@ -19,7 +19,7 @@ module Decidim
19
19
  from: sender,
20
20
  to: user,
21
21
  conversation: conversation,
22
- action: "new_conversation"
22
+ action: "new_message"
23
23
  )
24
24
  end
25
25
 
@@ -3,24 +3,23 @@
3
3
  module Decidim
4
4
  class NewsletterMailer < ApplicationMailer
5
5
  helper Decidim::SanitizeHelper
6
+ include Decidim::NewslettersHelper
7
+
6
8
  add_template_helper Decidim::TranslationsHelper
7
9
 
8
10
  def newsletter(user, newsletter)
9
11
  @organization = user.organization
10
12
  @newsletter = newsletter
13
+ @user = user
11
14
 
15
+ @custom_url_for_mail_root = custom_url_for_mail_root(@organization, @newsletter.id) if Decidim.config.track_newsletter_links
16
+ @encrypted_token = Decidim::NewsletterEncryptor.sent_at_encrypted(@user.id, @newsletter.sent_at)
12
17
  with_user(user) do
13
- @subject = parse_interpolations(@newsletter.subject[I18n.locale.to_s], user)
14
- @body = parse_interpolations(@newsletter.body[I18n.locale.to_s], user)
18
+ @subject = parse_interpolations(@newsletter.subject[I18n.locale.to_s], user, @newsletter.id)
19
+ @body = parse_interpolations(@newsletter.body[I18n.locale.to_s], user, @newsletter.id)
15
20
 
16
21
  mail(to: "#{user.name} <#{user.email}>", subject: @subject)
17
22
  end
18
23
  end
19
-
20
- private
21
-
22
- def parse_interpolations(content, user)
23
- content.gsub("%{name}", user.name)
24
- end
25
24
  end
26
25
  end
@@ -14,6 +14,7 @@ module Decidim
14
14
  can :read, Feature, &:published?
15
15
 
16
16
  can :search, Scope
17
+ can :pick, Scope
17
18
 
18
19
  can :manage, User do |other|
19
20
  other == user
@@ -31,14 +31,28 @@ module Decidim
31
31
  !granted_at.nil?
32
32
  end
33
33
 
34
+ # Calculates at when this authorization will expire, if it needs to.
35
+ #
36
+ # Returns nil if the authorization does not expire.
37
+ # Returns an ActiveSupport::TimeWithZone if it expires.
38
+ def expires_at
39
+ return unless workflow_manifest
40
+ return if workflow_manifest.expires_in.zero?
41
+ (granted_at || created_at) + workflow_manifest.expires_in
42
+ end
43
+
44
+ def expired?
45
+ expires_at.present? && expires_at < Time.zone.now
46
+ end
47
+
34
48
  private
35
49
 
36
50
  def active_handler?
37
- if Decidim::Verifications.find_workflow_manifest(name)
38
- true
39
- else
40
- false
41
- end
51
+ workflow_manifest.present?
52
+ end
53
+
54
+ def workflow_manifest
55
+ @workflow_manifest ||= Decidim::Verifications.find_workflow_manifest(name)
42
56
  end
43
57
  end
44
58
  end
@@ -10,7 +10,8 @@ module Decidim
10
10
 
11
11
  validate :same_organization, :non_active_impersonation
12
12
 
13
- scope :active, -> { where(ended_at: nil) }
13
+ scope :active, -> { where(ended_at: nil, expired_at: nil) }
14
+ scope :expired, -> { where(ended_at: nil).where.not(expired_at: nil) }
14
15
 
15
16
  def ended?
16
17
  ended_at.present?
@@ -26,6 +26,8 @@ module Decidim
26
26
  joins(:receipts).merge(Receipt.unread_by(user)).distinct
27
27
  }
28
28
 
29
+ default_scope { order(updated_at: :desc) }
30
+
29
31
  delegate :mark_as_read, to: :receipts
30
32
 
31
33
  #
@@ -8,6 +8,8 @@ module Decidim
8
8
  # message, namely, the interlocutors of the sender in the conversation.
9
9
  #
10
10
  class Message < ApplicationRecord
11
+ include Decidim::FriendlyDates
12
+
11
13
  belongs_to :sender,
12
14
  foreign_key: :decidim_sender_id,
13
15
  class_name: "Decidim::User"
@@ -24,6 +26,8 @@ module Decidim
24
26
  validates :sender, :body, presence: true
25
27
  validates :body, length: { maximum: 1_000 }
26
28
 
29
+ default_scope { order(created_at: :asc) }
30
+
27
31
  validate :sender_is_participant
28
32
 
29
33
  #
@@ -23,6 +23,7 @@ module Decidim
23
23
  mount_uploader :official_img_footer, Decidim::OfficialImageFooterUploader
24
24
  mount_uploader :logo, Decidim::OrganizationLogoUploader
25
25
  mount_uploader :favicon, Decidim::OrganizationFaviconUploader
26
+ mount_uploader :highlighted_content_banner_image, ImageUploader
26
27
 
27
28
  # Returns top level scopes for this organization.
28
29
  #
@@ -47,11 +47,19 @@ module Decidim
47
47
  organization.scopes.where("? = ANY(decidim_scopes.part_of)", id)
48
48
  end
49
49
 
50
+ def ancestor_of?(scope)
51
+ scope && scope.part_of.member?(id)
52
+ end
53
+
50
54
  # Gets the scopes from the part_of list in descending order (first the top level scope, last itself)
51
55
  #
56
+ # root - The root scope to start retrieval. If present, ignores top level scopes until reaching the root scope.
57
+ #
52
58
  # Returns an array of Scope objects
53
- def part_of_scopes
54
- organization.scopes.where(id: part_of).sort { |s1, s2| part_of.index(s2.id) <=> part_of.index(s1.id) }
59
+ def part_of_scopes(root = nil)
60
+ scope_ids = part_of
61
+ scope_ids.select! { |id| id == root.id || !root.part_of.member?(id) } if root
62
+ organization.scopes.where(id: scope_ids).sort { |s1, s2| part_of.index(s2.id) <=> part_of.index(s1.id) }
55
63
  end
56
64
 
57
65
  private
@@ -6,6 +6,9 @@ require "valid_email2"
6
6
  module Decidim
7
7
  # A User is a citizen that wants to join the platform to participate.
8
8
  class User < ApplicationRecord
9
+ include Nicknamizable
10
+ include Decidim::Followable
11
+
9
12
  OMNIAUTH_PROVIDERS = [:facebook, :twitter, :google_oauth2, (:developer if Rails.env.development?)].compact
10
13
  ROLES = %w(admin user_manager).freeze
11
14
 
@@ -18,14 +21,14 @@ module Decidim
18
21
  has_many :identities, foreign_key: "decidim_user_id", class_name: "Decidim::Identity", dependent: :destroy
19
22
  has_many :memberships, class_name: "Decidim::UserGroupMembership", foreign_key: :decidim_user_id, dependent: :destroy
20
23
  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
24
  has_many :notifications, foreign_key: "decidim_user_id", class_name: "Decidim::Notification", dependent: :destroy
23
25
 
24
26
  validates :name, presence: true, unless: -> { deleted? }
27
+ validates :nickname, presence: true, unless: -> { deleted? || managed? }
25
28
  validates :locale, inclusion: { in: :available_locales }, allow_blank: true
26
29
  validates :tos_agreement, acceptance: true, allow_nil: false, on: :create
27
30
  validates :avatar, file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_avatar_size } }
28
- validates :email, uniqueness: { scope: :organization }, unless: -> { deleted? || managed? }
31
+ validates :email, :nickname, uniqueness: { scope: :organization }, unless: -> { deleted? || managed? }
29
32
  validates :email, 'valid_email_2/email': { disposable: true }
30
33
 
31
34
  validate :all_roles_are_valid
@@ -66,6 +69,11 @@ module Decidim
66
69
  deleted_at.present?
67
70
  end
68
71
 
72
+ # Public: whether the user has been officialized or not
73
+ def officialized?
74
+ !officialized_at.nil?
75
+ end
76
+
69
77
  def follows?(followable)
70
78
  Decidim::Follow.where(user: self, followable: followable).any?
71
79
  end
@@ -51,13 +51,13 @@ module Decidim
51
51
  def global_stats(conditions)
52
52
  Decidim.stats.except([:users_count, :processes_count])
53
53
  .filter(conditions)
54
- .with_context(published_features)
54
+ .with_context(organization)
55
55
  .map { |name, data| [name, data] }
56
56
  end
57
57
 
58
58
  def feature_stats(conditions)
59
- Decidim.feature_manifests.map do |feature|
60
- feature.stats.filter(conditions).with_context(published_features).map { |name, data| [name, data] }.flatten
59
+ Decidim.feature_manifests.flat_map do |feature|
60
+ feature.stats.filter(conditions).with_context(published_features).map { |name, data| [name, data] }
61
61
  end
62
62
  end
63
63
 
@@ -74,8 +74,14 @@ module Decidim
74
74
  end
75
75
  end
76
76
 
77
+ def public_participatory_spaces
78
+ @public_participatory_spaces ||= Decidim.participatory_space_manifests.flat_map do |manifest|
79
+ manifest.participatory_spaces.call(organization).public_spaces
80
+ end
81
+ end
82
+
77
83
  def published_features
78
- @published_features ||= Feature.where(participatory_space: ParticipatoryProcesses::OrganizationPublishedParticipatoryProcesses.new(organization).query).published
84
+ @published_features ||= Feature.where(participatory_space: public_participatory_spaces).published
79
85
  end
80
86
  end
81
87
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ #
5
+ # Decorator for user groups
6
+ #
7
+ class UserGroupPresenter < SimpleDelegator
8
+ def nickname
9
+ ""
10
+ end
11
+
12
+ def deleted?
13
+ false
14
+ end
15
+
16
+ def badge
17
+ return "" unless verified?
18
+
19
+ "verified-badge"
20
+ end
21
+
22
+ def profile_path
23
+ ""
24
+ end
25
+
26
+ delegate :url, to: :avatar, prefix: true
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ #
5
+ # Decorator for users
6
+ #
7
+ class UserPresenter < SimpleDelegator
8
+ include Rails.application.routes.mounted_helpers
9
+ include ActionView::Helpers::UrlHelper
10
+
11
+ #
12
+ # nickname presented in a twitter-like style
13
+ #
14
+ def nickname
15
+ "@#{super}"
16
+ end
17
+
18
+ def badge
19
+ return "" unless officialized?
20
+
21
+ "verified-badge"
22
+ end
23
+
24
+ delegate :url, to: :avatar, prefix: true
25
+
26
+ def profile_url
27
+ return "" if deleted?
28
+
29
+ decidim.profile_url(__getobj__.nickname, host: __getobj__.organization.host)
30
+ end
31
+
32
+ def profile_path
33
+ return "" if deleted?
34
+
35
+ decidim.profile_path(__getobj__.nickname)
36
+ end
37
+
38
+ def display_mention
39
+ link_to nickname, profile_path, class: "user-mention"
40
+ end
41
+ end
42
+ end
@@ -4,94 +4,64 @@ module Decidim
4
4
  # This class is used to authorize a user against an action in the context of a
5
5
  # feature.
6
6
  class ActionAuthorizer
7
- include Wisper::Publisher
8
-
7
+ #
9
8
  # Initializes the ActionAuthorizer.
10
9
  #
11
10
  # user - The user to authorize against.
12
11
  # feature - The feature to authenticate against.
13
12
  # action - The action to authenticate.
13
+ #
14
14
  def initialize(user, feature, action)
15
15
  @user = user
16
16
  @feature = feature
17
17
  @action = action.to_s if action
18
18
  end
19
19
 
20
- # Public: Broadcasts different events given the status of the authentication.
21
20
  #
22
- # Broadcasts:
23
- # failed - When no valid authorization can be found.
24
- # unauthorized - When an authorization was found, but didn't match the credentials.
25
- # incomplete - An authorization was found, but lacks some required fields. User
26
- # should re-authenticate.
21
+ # Authorize user to perform an action in the context of a feature.
22
+ #
23
+ # Returns:
24
+ # :ok an empty hash - When there is no authorization handler related to the action.
25
+ # result of authorization handler check - When there is an authorization handler related to the action. Check Decidim::Verifications::DefaultActionAuthorizer class docs.
27
26
  #
28
- # Returns nil.
29
27
  def authorize
30
- status_code, fields = *status_data
31
-
32
- status(status_code, fields || {})
33
- end
34
-
35
- private
36
-
37
- def status_data
38
28
  raise AuthorizationError, "Missing data" unless feature && action
39
29
 
40
- if !authorization_handler_name
41
- :ok
42
- elsif !authorization
43
- :missing
44
- elsif !authorization.granted?
45
- :pending
46
- elsif unmatched_fields.any?
47
- [:invalid, fields: unmatched_fields]
48
- elsif missing_fields.any?
49
- [:incomplete, fields: missing_fields]
50
- else
51
- :ok
52
- end
53
- end
30
+ status_code, data = if authorization_handler_name
31
+ authorization_handler.authorize(authorization, permission_options)
32
+ else
33
+ [:ok, {}]
34
+ end
54
35
 
55
- def status(status_code, data = {})
56
- AuthorizationStatus.new(status_code, authorization_handler_name, data)
36
+ AuthorizationStatus.new(status_code, authorization_handler, data)
57
37
  end
58
38
 
39
+ private
40
+
59
41
  attr_reader :user, :feature, :action
60
42
 
61
43
  def authorization
62
- return nil unless user
44
+ return nil unless user && authorization_handler_name
63
45
 
64
- handler = permission["authorization_handler_name"]
65
- return nil unless handler
66
-
67
- @authorization ||= Verifications::Authorizations.new(user: user, name: handler).first
46
+ @authorization ||= Verifications::Authorizations.new(user: user, name: authorization_handler_name).first
68
47
  end
69
48
 
70
- def unmatched_fields
71
- (permission_options.keys & authorization.metadata.to_h.keys).each_with_object({}) do |field, unmatched|
72
- unmatched[field] = permission_options[field] if authorization.metadata[field] != permission_options[field]
73
- unmatched
74
- end
75
- end
49
+ def authorization_handler
50
+ return unless authorization_handler_name
76
51
 
77
- def missing_fields
78
- permission_options.keys.each_with_object([]) do |field, missing|
79
- missing << field if authorization.metadata[field].blank?
80
- missing
81
- end
82
- end
83
-
84
- def permission_options
85
- permission["options"] || {}
52
+ @authorization_handler ||= Verifications::Adapter.from_element(authorization_handler_name)
86
53
  end
87
54
 
88
55
  def authorization_handler_name
89
56
  permission&.fetch("authorization_handler_name", nil)
90
57
  end
91
58
 
59
+ def permission_options
60
+ permission&.fetch("options", {})
61
+ end
62
+
92
63
  def permission
93
- return nil unless feature
94
- return nil unless action
64
+ return nil unless feature && action
95
65
 
96
66
  @permission ||= feature.permissions&.fetch(action, nil)
97
67
  end
@@ -99,32 +69,26 @@ module Decidim
99
69
  class AuthorizationStatus
100
70
  attr_reader :code, :data
101
71
 
102
- def initialize(code, handler_name, data)
72
+ def initialize(code, authorization_handler, data)
103
73
  @code = code.to_sym
104
- @handler_name = handler_name
74
+ @authorization_handler = authorization_handler
105
75
  @data = data.symbolize_keys
106
76
  end
107
77
 
108
- def auth_method
109
- return unless @handler_name
110
-
111
- @auth_method ||= Verifications::Adapter.from_element(@handler_name)
112
- end
113
-
114
78
  def current_path(redirect_url: nil)
115
- return unless auth_method
79
+ return unless @authorization_handler
116
80
 
117
81
  if pending?
118
- auth_method.resume_authorization_path(redirect_url: redirect_url)
82
+ @authorization_handler.resume_authorization_path(redirect_url: redirect_url)
119
83
  else
120
- auth_method.root_path(redirect_url: redirect_url)
84
+ @authorization_handler.root_path(redirect_url: redirect_url)
121
85
  end
122
86
  end
123
87
 
124
88
  def handler_name
125
- return unless auth_method
89
+ return unless @authorization_handler
126
90
 
127
- auth_method.key
91
+ @authorization_handler.key
128
92
  end
129
93
 
130
94
  def ok?