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
@@ -14,6 +14,7 @@ module Decidim
14
14
  helper_method :page, :stats
15
15
  helper CtaButtonHelper
16
16
  helper Decidim::SanitizeHelper
17
+ skip_before_action :store_current_location
17
18
 
18
19
  def index
19
20
  @pages = current_organization.static_pages.all.to_a.sort do |a, b|
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # The controller to handle the user's public profile page.
5
+ class ProfilesController < Decidim::ApplicationController
6
+ skip_authorization_check
7
+
8
+ helper Decidim::Messaging::ConversationHelper
9
+
10
+ helper_method :user
11
+
12
+ def show; end
13
+
14
+ private
15
+
16
+ def user
17
+ @user ||= Decidim::User.find_by!(
18
+ nickname: params[:nickname],
19
+ organization: current_organization
20
+ )
21
+ end
22
+ end
23
+ end
@@ -3,27 +3,26 @@
3
3
  module Decidim
4
4
  # Exposes the scopes text search so users can choose a scope writing its name.
5
5
  class ScopesController < Decidim::ApplicationController
6
- def search
7
- authorize! :search, Scope
8
- root = Scope.where(id: params[:root], organization: current_organization).first
9
- scopes = if params[:term].present?
10
- FreetextScopes.for(current_organization, I18n.locale, params[:term], root)
11
- elsif root
12
- root.children
13
- else
14
- current_organization.top_scopes
15
- end
16
- root_option = if params[:include_root] == "true" && params[:term].blank?
17
- if root
18
- [{ id: root.id.to_s, text: root.name[I18n.locale.to_s] }]
19
- else
20
- [{ id: "global", text: I18n.t("decidim.scopes.global") }]
21
- end
22
- else
23
- []
24
- end
6
+ skip_before_action :store_current_location
25
7
 
26
- render json: { results: root_option + scopes.map { |scope| { id: scope.id.to_s, text: scope.name[I18n.locale.to_s] } } }
8
+ def picker
9
+ authorize! :pick, Scope
10
+
11
+ title = params[:title] || t("decidim.scopes.picker.title", field: params[:field]&.downcase)
12
+ root = Scope.find(params[:root]) if params[:root]
13
+ context = root ? { root: root.id, title: title } : { title: title }
14
+ required = params[:required] && params[:required] != "false"
15
+ if params[:current]
16
+ current = (root&.descendants || current_organization.scopes).find_by(id: params[:current]) || root
17
+ scopes = current.children
18
+ parent_scopes = current.part_of_scopes(root)
19
+ else
20
+ current = root
21
+ scopes = root&.children || Scope.top_level
22
+ parent_scopes = [root].compact
23
+ end
24
+ render :picker, layout: nil, locals: { required: required, title: title, root: root, current: current, scopes: scopes.order(name: :asc),
25
+ parent_scopes: parent_scopes, global_value: params[:global_value], context: context }
27
26
  end
28
27
  end
29
28
  end
@@ -0,0 +1,27 @@
1
+ # frozen-string_literal: true
2
+
3
+ module Decidim
4
+ class ProfileUpdatedEvent < Decidim::Events::SimpleEvent
5
+ i18n_attributes :nickname, :name
6
+
7
+ delegate :profile_path, :profile_url, :nickname, :name, to: :updated_user
8
+
9
+ private
10
+
11
+ def resource_path
12
+ profile_path
13
+ end
14
+
15
+ def resource_title
16
+ name
17
+ end
18
+
19
+ def resource_url
20
+ profile_url
21
+ end
22
+
23
+ def updated_user
24
+ @updated_user ||= Decidim::UserPresenter.new(resource)
25
+ end
26
+ end
27
+ end
@@ -7,21 +7,36 @@ module Decidim
7
7
  mimic :user
8
8
 
9
9
  attribute :name
10
+ attribute :nickname
10
11
  attribute :email
11
12
  attribute :password
12
13
  attribute :password_confirmation
13
14
  attribute :avatar
14
15
  attribute :remove_avatar
16
+ attribute :personal_url
17
+ attribute :about
15
18
 
16
19
  validates :name, presence: true
17
20
  validates :email, presence: true
21
+ validates :nickname, presence: true
18
22
 
23
+ validates :nickname, length: { maximum: Decidim::User.nickname_max_length, allow_blank: true }
19
24
  validates :password, confirmation: true
20
25
  validates :password, length: { in: Decidim::User.password_length, allow_blank: true }
21
26
  validates :password_confirmation, presence: true, if: :password_present
22
27
  validates :avatar, file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_avatar_size } }
23
28
 
24
29
  validate :unique_email
30
+ validate :unique_nickname
31
+ validate :personal_url_format
32
+
33
+ def personal_url
34
+ return if super.blank?
35
+
36
+ return "http://" + super unless super.match?(%r{\A(http|https)://}i)
37
+
38
+ super
39
+ end
25
40
 
26
41
  private
27
42
 
@@ -38,5 +53,24 @@ module Decidim
38
53
  errors.add :email, :taken
39
54
  false
40
55
  end
56
+
57
+ def unique_nickname
58
+ return true if Decidim::User.where(
59
+ organization: context.current_organization,
60
+ nickname: nickname
61
+ ).where.not(id: context.current_user.id).empty?
62
+
63
+ errors.add :nickname, :taken
64
+ false
65
+ end
66
+
67
+ def personal_url_format
68
+ return if personal_url.blank?
69
+
70
+ uri = URI.parse(personal_url)
71
+ errors.add :personal_url, :invalid if !uri.is_a?(URI::HTTP) || uri.host.nil?
72
+ rescue URI::InvalidURIError
73
+ errors.add :personal_url, :invalid
74
+ end
41
75
  end
42
76
  end
@@ -7,6 +7,7 @@ module Decidim
7
7
  delegate :current_organization,
8
8
  :current_user,
9
9
  :current_feature,
10
+ :current_participatory_space,
10
11
  to: :context, prefix: false, allow_nil: true
11
12
 
12
13
  delegate :available_locales, to: :current_organization, allow_nil: true
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module Messaging
5
- # A form object to be used when users want to follow a followable resource.
5
+ # A form object to be used when users want to message another user.
6
6
  class ConversationForm < Decidim::Form
7
7
  mimic :conversation
8
8
 
@@ -12,7 +12,10 @@ module Decidim
12
12
  validates :body, :recipient, presence: true
13
13
 
14
14
  def recipient
15
- Decidim::User.find_by(id: recipient_id)
15
+ @recipient ||= Decidim::User
16
+ .where.not(id: current_user.id)
17
+ .where(organization: current_user.organization)
18
+ .find_by(id: recipient_id)
16
19
  end
17
20
  end
18
21
  end
@@ -7,6 +7,7 @@ module Decidim
7
7
 
8
8
  attribute :email, String
9
9
  attribute :name, String
10
+ attribute :nickname, String
10
11
  attribute :provider, String
11
12
  attribute :uid, String
12
13
  attribute :tos_agreement, Boolean
@@ -20,5 +21,9 @@ module Decidim
20
21
  def self.create_signature(provider, uid)
21
22
  Digest::MD5.hexdigest("#{provider}-#{uid}-#{Rails.application.secrets.secret_key_base}")
22
23
  end
24
+
25
+ def normalized_nickname
26
+ User.nicknamize(nickname || name)
27
+ end
23
28
  end
24
29
  end
@@ -7,10 +7,11 @@ module Decidim
7
7
 
8
8
  attribute :sign_up_as, String
9
9
  attribute :name, String
10
+ attribute :nickname, String
10
11
  attribute :email, String
11
12
  attribute :password, String
12
13
  attribute :password_confirmation, String
13
- attribute :newsletter_notifications, Boolean
14
+ attribute :newsletter, Boolean
14
15
  attribute :tos_agreement, Boolean
15
16
 
16
17
  attribute :user_group_name, String
@@ -19,6 +20,7 @@ module Decidim
19
20
 
20
21
  validates :sign_up_as, inclusion: { in: %w(user user_group) }
21
22
  validates :name, presence: true
23
+ validates :nickname, presence: true
22
24
  validates :email, presence: true
23
25
  validates :password, presence: true, confirmation: true, length: { in: Decidim::User.password_length }
24
26
  validates :tos_agreement, allow_nil: false, acceptance: true
@@ -28,6 +30,7 @@ module Decidim
28
30
  validates :user_group_phone, presence: true, if: :user_group?
29
31
 
30
32
  validate :email_unique_in_organization
33
+ validate :nickname_unique_in_organization
31
34
  validate :user_group_name_unique_in_organization
32
35
  validate :user_group_document_number_unique_in_organization
33
36
 
@@ -41,6 +44,10 @@ module Decidim
41
44
  errors.add :email, :taken if User.where(email: email, organization: current_organization).first.present?
42
45
  end
43
46
 
47
+ def nickname_unique_in_organization
48
+ errors.add :nickname, :taken if User.where(nickname: nickname, organization: current_organization).first.present?
49
+ end
50
+
44
51
  def user_group_name_unique_in_organization
45
52
  errors.add :user_group_name, :taken if UserGroup.where(name: user_group_name, decidim_organization_id: current_organization.id).first.present?
46
53
  end
@@ -36,7 +36,7 @@ module Decidim
36
36
  unless current_user_authorized?(action)
37
37
  html_options ||= {}
38
38
  html_options["onclick"] = "event.preventDefault();"
39
- html_options["data-toggle"] = current_user ? "#{action.to_s.underscore}AuthorizationModal" : "loginModal"
39
+ html_options["data-open"] = current_user ? "#{action.to_s.underscore}AuthorizationModal" : "loginModal"
40
40
  url = ""
41
41
  end
42
42
 
@@ -67,7 +67,7 @@ module Decidim
67
67
  end
68
68
 
69
69
  unless current_user_authorized?(action)
70
- html_options["data-toggle"] = current_user ? "#{action.to_s.underscore}AuthorizationModal" : "loginModal"
70
+ html_options["data-open"] = current_user ? "#{action.to_s.underscore}AuthorizationModal" : "loginModal"
71
71
  url = ""
72
72
  end
73
73
 
@@ -24,5 +24,13 @@ module Decidim
24
24
 
25
25
  Truncato.truncate(text, options)
26
26
  end
27
+
28
+ def present(object)
29
+ presenter = "#{object.class.name}Presenter".constantize.new(object)
30
+
31
+ yield(presenter) if block_given?
32
+
33
+ presenter
34
+ end
27
35
  end
28
36
  end
@@ -7,17 +7,27 @@ module Decidim
7
7
  #
8
8
  # feature - the Feature we want to find the root path for.
9
9
  #
10
- # Returns a url.
10
+ # Returns a relative url.
11
11
  def main_feature_path(feature)
12
12
  current_params = try(:params) || {}
13
13
  EngineRouter.main_proxy(feature).root_path(locale: current_params[:locale])
14
14
  end
15
15
 
16
+ # Returns the defined root url for a given feature.
17
+ #
18
+ # feature - the Feature we want to find the root path for.
19
+ #
20
+ # Returns an absolute url.
21
+ def main_feature_url(feature)
22
+ current_params = try(:params) || {}
23
+ EngineRouter.main_proxy(feature).root_url(locale: current_params[:locale])
24
+ end
25
+
16
26
  # Returns the defined admin root path for a given feature.
17
27
  #
18
28
  # feature - the Feature we want to find the root path for.
19
29
  #
20
- # Returns a url.
30
+ # Returns a relative url.
21
31
  def manage_feature_path(feature)
22
32
  current_params = try(:params) || {}
23
33
  EngineRouter.admin_proxy(feature).root_path(locale: current_params[:locale])
@@ -13,7 +13,7 @@ module Decidim
13
13
  def feature_reference(feature, options = {})
14
14
  return unless feature.reference
15
15
  @reference = feature.reference
16
- "<div class='reference #{options[:class]}'>#{localized_reference}</div>".html_safe
16
+ "<div class='tech-info #{options[:class]}'>#{localized_reference}</div>".html_safe
17
17
  end
18
18
 
19
19
  private
@@ -4,21 +4,30 @@ module Decidim
4
4
  module Messaging
5
5
  module ConversationHelper
6
6
  #
7
- # Builds a link to the conversation between the current user and another
7
+ # Links to the conversation between the current user and another user
8
+ #
9
+ def link_to_current_or_new_conversation_with(user, title = t("decidim.contact"))
10
+ link_to current_or_new_conversation_path_with(user), title: title do
11
+ icon "envelope-closed", aria_label: title, class: "icon--small"
12
+ end
13
+ end
14
+
15
+ #
16
+ # Finds the right path to the conversation the current user and another
8
17
  # user.
9
18
  #
10
- # * If there's no current user, it links to the login form.
19
+ # * If there's no current user, it returns to the login form path.
11
20
  #
12
- # * If there's no prior existing conversation between the users, it links
13
- # to the new conversation form.
21
+ # * If there's no prior existing conversation between the users, it
22
+ # returns the new conversation form path.
14
23
  #
15
- # * Otherwise, it links to the existing conversation.
24
+ # * Otherwise, it returns the path to the existing conversation.
16
25
  #
17
26
  # @param user [Decidim::User] The user to link to a conversation with
18
27
  #
19
28
  # @return [String] The resulting route
20
29
  #
21
- def link_to_current_or_new_conversation_with(user)
30
+ def current_or_new_conversation_path_with(user)
22
31
  return decidim.new_user_session_path unless user_signed_in?
23
32
 
24
33
  conversation = conversation_between(current_user, user)
@@ -30,9 +39,18 @@ module Decidim
30
39
  end
31
40
  end
32
41
 
33
- def conversation_between(one_user, another_user)
34
- UserConversations.for(one_user).find do |conversation|
35
- conversation.participants.to_set == [one_user, another_user].to_set
42
+ #
43
+ # Finds the conversation between the given participants
44
+ #
45
+ # @param participants [Array<Decidim::User>] The participants to find a
46
+ # conversation between.
47
+ #
48
+ # @return [Decidim::Messaging::Conversation]
49
+ def conversation_between(*participants)
50
+ return if participants.to_set.length <= 1
51
+
52
+ UserConversations.for(participants.first).find do |conversation|
53
+ conversation.participants.to_set == participants.to_set
36
54
  end
37
55
  end
38
56
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # Helper that provides methods to render links with utm codes, and replaced name
5
+ module NewslettersHelper
6
+ # If the newsletter body there are some links and the Decidim.track_newsletter_links = true
7
+ # it will be replaced with the utm_codes method described below.
8
+ # for example transform "https://es.lipsum.com/" to "https://es.lipsum.com/?utm_source=localhost&utm_campaign=newsletter_11"
9
+ # And replace "%{name}" on the subject or content of newsletter to the user Name
10
+ # for example transform "%{name}" to "User Name"
11
+ def parse_interpolations(content, user = nil, id = nil)
12
+ if Decidim.config.track_newsletter_links
13
+ if id.present? && user.present?
14
+ host = user.organization.host.to_s
15
+ campaign = "newsletter_#{id}"
16
+
17
+ links = content.scan(/href\s*=\s*"([^"]*)"/)
18
+
19
+ links.each do |link|
20
+ link_replaced = link.first + utm_codes(host, campaign)
21
+ content = content.gsub(/href\s*=\s*"([^"]*#{link.first})"/, %(href="#{link_replaced}"))
22
+ end
23
+ end
24
+ end
25
+
26
+ if user.present?
27
+ content.gsub("%{name}", user.name)
28
+ else
29
+ content.gsub("%{name}", "")
30
+ end
31
+ end
32
+
33
+ # this method is used to generate the root link on mail with the utm_codes
34
+ # If the newsletter_id is nil, it returns the root_url
35
+ def custom_url_for_mail_root(organization, newsletter_id = nil)
36
+ if newsletter_id.present?
37
+ decidim.root_url(host: organization.host) + utm_codes(organization.host, newsletter_id.to_s)
38
+ else
39
+ decidim.root_url(host: organization.host)
40
+ end
41
+ end
42
+
43
+ # Method to specify the utm_codes.
44
+ # You can change or add utm_codes for track
45
+ def utm_codes(host, newsletter_id)
46
+ "?utm_source=#{host}&utm_campaign=#{newsletter_id}"
47
+ end
48
+ end
49
+ end
@@ -5,11 +5,52 @@ module Decidim
5
5
  module ScopesHelper
6
6
  Option = Struct.new(:id, :name)
7
7
 
8
- # Check whether the resource has a visible scope or not.
8
+ # Checks if the resource should show its scope or not.
9
+ # resource - the resource to analize
9
10
  #
10
11
  # Returns boolean.
11
12
  def has_visible_scopes?(resource)
12
- current_participatory_space.scopes_enabled? && current_participatory_space.scope.blank? && resource.scope.present?
13
+ try(:current_participatory_space)&.try(:scopes_enabled?) && resource.scope.present? && current_participatory_space.try(:scope)&.id != resource.scope&.id
14
+ end
15
+
16
+ # Retrieves the translated name and type for an scope.
17
+ # scope - a Decidim::Scope
18
+ # global_name - text to use when scope is nil
19
+ #
20
+ # Returns a string
21
+ def scope_name_for_picker(scope, global_name)
22
+ if scope
23
+ name = translated_attribute(scope.name)
24
+ name << " (#{translated_attribute(scope.scope_type.name)})" if scope.scope_type
25
+ name
26
+ else
27
+ global_name
28
+ end
29
+ end
30
+
31
+ # Renders a scopes picker field in a form.
32
+ # form - FormBuilder object
33
+ # name - attribute name
34
+ #
35
+ # Returns nothing.
36
+ def scopes_picker_field(form, name, root: false)
37
+ root = try(:current_participatory_space)&.scope if root == false
38
+ form.scopes_picker name do |scope|
39
+ { url: decidim.scopes_picker_path(root: root, current: scope&.id, field: form.label_for(name)),
40
+ text: scope_name_for_picker(scope, I18n.t("decidim.scopes.global")) }
41
+ end
42
+ end
43
+
44
+ # Renders a scopes picker field in a filter form.
45
+ # form - FilterFormBuilder object
46
+ # name - attribute name
47
+ #
48
+ # Returns nothing.
49
+ def scopes_picker_filter(form, name)
50
+ form.scopes_picker name, multiple: true, legend_title: I18n.t("decidim.scopes.scopes"), label: false do |scope|
51
+ { url: decidim.scopes_picker_path(root: try(:current_participatory_space)&.scope, current: scope&.id, title: I18n.t("decidim.scopes.prompt"), global_value: "global"),
52
+ text: scope_name_for_picker(scope, I18n.t("decidim.scopes.prompt")) }
53
+ end
13
54
  end
14
55
  end
15
56
  end