decidim-decidim_awesome 0.7.0 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +32 -9
  3. data/app/assets/config/decidim_admin_decidim_awesome_manifest.css +2 -2
  4. data/app/assets/config/decidim_admin_decidim_awesome_manifest.js +1 -2
  5. data/app/assets/config/decidim_decidim_awesome_manifest.css +2 -2
  6. data/app/assets/config/decidim_decidim_awesome_manifest.js +3 -2
  7. data/app/assets/config/legacy_decidim_admin_decidim_awesome_manifest.js +2 -0
  8. data/app/assets/config/legacy_decidim_decidim_awesome_manifest.js +4 -0
  9. data/app/assets/javascripts/decidim/decidim_awesome/admin/auto_edit.js.es6 +77 -0
  10. data/app/assets/javascripts/decidim/decidim_awesome/admin/codemirror.js.es6 +2 -3
  11. data/app/assets/javascripts/decidim/decidim_awesome/admin/form_builder.js.es6 +80 -0
  12. data/app/assets/javascripts/decidim/decidim_awesome/admin/legacy_form_builder.js.es6 +80 -0
  13. data/app/assets/javascripts/decidim/decidim_awesome/admin/user_picker.js.es6 +24 -0
  14. data/app/assets/javascripts/decidim/decidim_awesome/awesome_admin.js +7 -0
  15. data/app/assets/javascripts/decidim/decidim_awesome/{application.js → awesome_application.js} +1 -2
  16. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/layers.js.es6 +3 -2
  17. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/load_map.js.es6 +15 -0
  18. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/map.js.es6 +52 -56
  19. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/meetings.js.es6 +2 -2
  20. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/proposals.js.es6 +1 -1
  21. data/app/assets/javascripts/decidim/decidim_awesome/awesome_map/utilities.js.es6 +33 -24
  22. data/app/assets/javascripts/decidim/decidim_awesome/editors/legacy_quill_editor.js.es6 +14 -2
  23. data/app/assets/javascripts/decidim/decidim_awesome/editors/quill_editor.js.es6 +18 -4
  24. data/app/assets/javascripts/decidim/decidim_awesome/editors/tabs_focus.js.es6 +24 -0
  25. data/app/assets/javascripts/decidim/decidim_awesome/forms/custom_fields_builder.js.es6 +211 -0
  26. data/app/assets/javascripts/decidim/decidim_awesome/forms/rich_text_plugin.js.es6 +106 -0
  27. data/app/assets/javascripts/decidim/decidim_awesome/legacy_admin.js +5 -1
  28. data/app/assets/javascripts/decidim/decidim_awesome/legacy_application.js +0 -1
  29. data/app/assets/javascripts/decidim/decidim_awesome/proposals/custom_fields.js.es6 +21 -0
  30. data/app/assets/stylesheets/decidim/decidim_awesome/admin/auto_edits.scss +15 -0
  31. data/app/assets/stylesheets/decidim/decidim_awesome/admin/codemirror.scss +15 -4
  32. data/app/assets/stylesheets/decidim/decidim_awesome/admin/constraints.scss +12 -0
  33. data/app/assets/stylesheets/decidim/decidim_awesome/admin/custom_fields.scss +66 -0
  34. data/app/assets/stylesheets/decidim/decidim_awesome/admin/user_picker.scss +35 -0
  35. data/app/assets/stylesheets/decidim/decidim_awesome/{admin.scss → awesome_admin.scss} +12 -0
  36. data/app/assets/stylesheets/decidim/decidim_awesome/awesome_application.scss +22 -0
  37. data/app/assets/stylesheets/decidim/decidim_awesome/awesome_map/map.scss +0 -1
  38. data/app/assets/stylesheets/decidim/decidim_awesome/editors/quill_editor.scss +16 -1
  39. data/app/awesome_overrides/forms/decidim/proposals/proposal_wizard_create_step_form_override.rb +28 -0
  40. data/app/cells/decidim/decidim_awesome/content_blocks/map/show.erb +74 -0
  41. data/app/cells/decidim/decidim_awesome/content_blocks/map_cell.rb +54 -0
  42. data/app/cells/decidim/decidim_awesome/content_blocks/map_form/show.erb +61 -0
  43. data/app/cells/decidim/decidim_awesome/content_blocks/map_form_cell.rb +19 -0
  44. data/app/commands/concerns/decidim/decidim_awesome/admin/needs_constraint_helpers.rb +32 -0
  45. data/app/commands/decidim/decidim_awesome/admin/create_proposal_custom_field.rb +45 -0
  46. data/app/commands/decidim/decidim_awesome/admin/create_scoped_admin.rb +38 -0
  47. data/app/commands/decidim/decidim_awesome/admin/destroy_constraint.rb +4 -0
  48. data/app/commands/decidim/decidim_awesome/admin/destroy_proposal_custom_field.rb +40 -0
  49. data/app/commands/decidim/decidim_awesome/admin/destroy_scoped_admin.rb +40 -0
  50. data/app/commands/decidim/decidim_awesome/admin/destroy_scoped_style.rb +1 -1
  51. data/app/commands/decidim/decidim_awesome/admin/rename_scope_label.rb +58 -0
  52. data/app/commands/decidim/decidim_awesome/admin/update_config.rb +1 -0
  53. data/app/controllers/concerns/decidim/decidim_awesome/admin_not_found_redirect.rb +39 -0
  54. data/app/controllers/decidim/decidim_awesome/admin/config_controller.rb +31 -18
  55. data/app/controllers/decidim/decidim_awesome/admin/constraints_controller.rb +4 -0
  56. data/app/controllers/decidim/decidim_awesome/admin/proposal_custom_fields_controller.rb +38 -0
  57. data/app/controllers/decidim/decidim_awesome/admin/scoped_admins_controller.rb +38 -0
  58. data/app/controllers/decidim/decidim_awesome/admin/scoped_styles_controller.rb +38 -0
  59. data/app/forms/decidim/decidim_awesome/admin/config_form.rb +39 -0
  60. data/app/forms/decidim/decidim_awesome/admin/constraint_form.rb +3 -1
  61. data/app/helpers/decidim/decidim_awesome/admin/config_constraints_helpers.rb +11 -7
  62. data/app/helpers/decidim/decidim_awesome/amendments_helper_override.rb +48 -0
  63. data/app/helpers/decidim/decidim_awesome/map_helper.rb +67 -16
  64. data/app/helpers/decidim/decidim_awesome/proposals/application_helper_override.rb +78 -0
  65. data/app/middleware/decidim/decidim_awesome/current_config.rb +182 -0
  66. data/app/models/decidim/decidim_awesome/awesome_config.rb +15 -0
  67. data/app/models/decidim/decidim_awesome/user_override.rb +25 -0
  68. data/app/permissions/decidim/decidim_awesome/admin/permissions.rb +2 -0
  69. data/app/views/decidim/decidim_awesome/admin/checks/index.html.erb +1 -1
  70. data/app/views/decidim/decidim_awesome/admin/config/_autoedit_box_label.html.erb +7 -0
  71. data/app/views/decidim/decidim_awesome/admin/config/_constraints.html.erb +2 -2
  72. data/app/views/decidim/decidim_awesome/admin/config/_form_admins.html.erb +21 -0
  73. data/app/views/decidim/decidim_awesome/admin/config/_form_editors.html.erb +0 -3
  74. data/app/views/decidim/decidim_awesome/admin/config/_form_proposal_custom_fields.html.erb +25 -0
  75. data/app/views/decidim/decidim_awesome/admin/config/_form_proposals.html.erb +0 -2
  76. data/app/views/decidim/decidim_awesome/admin/config/_form_styles.html.erb +4 -7
  77. data/app/views/decidim/decidim_awesome/admin/proposals/_editor.html.erb +4 -0
  78. data/app/views/decidim/decidim_awesome/custom_fields/_form_render.html.erb +6 -0
  79. data/app/views/decidim/decidim_awesome/map_component/map/show.html.erb +0 -2
  80. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +101 -0
  81. data/app/views/decidim/proposals/collaborative_drafts/_edit_form_fields.html.erb +83 -0
  82. data/app/views/decidim/proposals/collaborative_drafts/show.html.erb +1 -0
  83. data/app/views/layouts/decidim/admin/decidim_awesome.html.erb +25 -11
  84. data/app/views/layouts/decidim/decidim_awesome/_awesome_config.html.erb +4 -0
  85. data/app/views/layouts/decidim/decidim_awesome/_custom_styles.html.erb +1 -1
  86. data/app/views/v0.23/decidim/proposals/collaborative_drafts/_show.html.erb +134 -0
  87. data/app/views/v0.23/layouts/decidim/_head.html.erb +1 -1
  88. data/app/views/v0.23/layouts/decidim/admin/_header.html.erb +1 -1
  89. data/app/views/v0.24/decidim/proposals/collaborative_drafts/_show.html.erb +128 -0
  90. data/app/views/v0.24/layouts/decidim/_head.html.erb +2 -2
  91. data/app/views/v0.24/layouts/decidim/admin/_header.html.erb +2 -2
  92. data/config/locales/ca.yml +62 -3
  93. data/config/locales/cs.yml +62 -3
  94. data/config/locales/en.yml +90 -11
  95. data/config/locales/es.yml +61 -2
  96. data/config/locales/eu.yml +63 -4
  97. data/config/locales/fr.yml +62 -3
  98. data/config/locales/it.yml +284 -0
  99. data/config/locales/ja.yml +284 -0
  100. data/config/locales/nl.yml +62 -3
  101. data/config/locales/sv.yml +62 -3
  102. data/db/migrate/20210628150825_change_awesome_config_var_type.rb +12 -0
  103. data/lib/decidim/decidim_awesome/admin_engine.rb +16 -4
  104. data/lib/decidim/decidim_awesome/awesome_helpers.rb +17 -10
  105. data/lib/decidim/decidim_awesome/checksums.yml +17 -4
  106. data/lib/decidim/decidim_awesome/config.rb +53 -6
  107. data/lib/decidim/decidim_awesome/context_analyzers/request_analyzer.rb +27 -21
  108. data/lib/decidim/decidim_awesome/custom_fields.rb +94 -0
  109. data/lib/decidim/decidim_awesome/engine.rb +62 -6
  110. data/lib/decidim/decidim_awesome/test/shared_examples/box_label_editor.rb +116 -0
  111. data/lib/decidim/decidim_awesome/test/shared_examples/current_config_examples.rb +143 -0
  112. data/lib/decidim/decidim_awesome/test/shared_examples/editor_examples.rb +4 -0
  113. data/lib/decidim/decidim_awesome/test/shared_examples/scoped_admins_examples.rb +428 -0
  114. data/lib/decidim/decidim_awesome/version.rb +1 -1
  115. data/lib/decidim/decidim_awesome.rb +41 -8
  116. data/vendor/assets/javascripts/delta.min.js +405 -0
  117. data/vendor/assets/javascripts/delta.min.js.map +1 -0
  118. data/vendor/assets/javascripts/europa.min.js +4 -0
  119. data/vendor/assets/javascripts/form-builder.min.js +19 -0
  120. data/vendor/assets/javascripts/form-render.min.js +19 -0
  121. data/vendor/assets/javascripts/inscrybmde.min.js +1 -1
  122. data/vendor/assets/javascripts/jquery-ui.min.js +13 -0
  123. data/vendor/assets/javascripts/select2.js +6147 -0
  124. data/vendor/assets/langs/en-US.lang +110 -0
  125. data/vendor/assets/stylesheets/inscrybmde.min.scss +14 -0
  126. data/vendor/assets/stylesheets/jquery-ui.min.css +7 -0
  127. data/vendor/assets/stylesheets/select2-foundation-theme.css +249 -0
  128. data/vendor/assets/stylesheets/select2.css +515 -0
  129. metadata +68 -27
  130. data/app/assets/images/decidim/decidim_awesome/loading.gif +0 -0
  131. data/app/assets/javascripts/decidim/decidim_awesome/admin.js +0 -3
  132. data/app/assets/javascripts/decidim/decidim_awesome/editors/markdown_view.js.es6 +0 -12
  133. data/app/assets/stylesheets/decidim/decidim_awesome/application.scss +0 -8
  134. data/app/assets/stylesheets/decidim/decidim_awesome/editors/markdown_view.scss +0 -27
  135. data/app/awesome_overrides/presenters/decidim/proposals/proposal_presenter_override.rb +0 -58
  136. data/lib/decidim/decidim_awesome/content_renderers/markdown_renderer.rb +0 -18
  137. data/lib/decidim/decidim_awesome/content_renderers.rb +0 -9
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Admin
6
+ class RenameScopeLabel < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # params - A constraint params
10
+ def initialize(params, organization)
11
+ @text = params[:text]&.strip&.gsub(" ", "_")&.parameterize&.truncate(64)
12
+ @scope = params[:scope]
13
+ @key = params[:key]
14
+ @attribute = params[:attribute]
15
+ @organization = organization
16
+ end
17
+
18
+ # Executes the command. Broadcasts these events:
19
+ #
20
+ # - :ok when everything is valid.
21
+ # - :invalid if we couldn't proceed.
22
+ #
23
+ # Returns nothing.
24
+ def call
25
+ raise StandardError, "empty value" if @text.blank?
26
+ raise StandardError, "key already exists" if config.value.keys.include? @text
27
+
28
+ transaction do
29
+ config.value[@text] = config.value.delete @key
30
+ config.save!
31
+ if config_scope
32
+ config_scope.var = scope
33
+ config_scope.save!
34
+ end
35
+ end
36
+
37
+ broadcast(:ok, { scope: scope, key: @text })
38
+ rescue StandardError => e
39
+ broadcast(:invalid, e.message)
40
+ end
41
+
42
+ private
43
+
44
+ def scope
45
+ @scope.gsub(/_#{@key}$/, "_#{@text}")
46
+ end
47
+
48
+ def config
49
+ @config ||= Decidim::DecidimAwesome::AwesomeConfig.find_by!(var: @attribute, organization: @organization)
50
+ end
51
+
52
+ def config_scope
53
+ @config_scope ||= Decidim::DecidimAwesome::AwesomeConfig.find_by(var: @scope, organization: @organization)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -20,6 +20,7 @@ module Decidim
20
20
  def call
21
21
  if form.invalid?
22
22
  message = form.errors[:scoped_styles].join("; ") if @form.errors[:scoped_styles].any?
23
+ message = form.errors[:scoped_admins].join("; ") if @form.errors[:scoped_admins].any?
23
24
  return broadcast(:invalid, message, form.errors)
24
25
  end
25
26
 
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module AdminNotFoundRedirect
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ # rubocop:disable Rails/LexicallyScopedActionFilter:
10
+ before_action :redirect_unallowed_scoped_admins, only: :not_found
11
+ # rubocop:enable Rails/LexicallyScopedActionFilter
12
+
13
+ private
14
+
15
+ def redirect_unallowed_scoped_admins
16
+ return unless request.original_fullpath =~ %r{^(/+)admin}
17
+ return unless Decidim::User.respond_to? :awesome_potential_admins
18
+ return unless defined? current_user
19
+ return unless Decidim::User.awesome_potential_admins.include? current_user.id
20
+
21
+ # assiging a flash message here does not work after redirection due the order of middleware in Rails
22
+ # as a workaround, send a message through a get parameter
23
+ path = "/admin/?unauthorized"
24
+ referer = request.headers["Referer"]
25
+ if referer
26
+ uri = URI(referer)
27
+ params = Rack::Utils.parse_query uri.query
28
+ unless request.params.has_key? "unauthorized"
29
+ params["unauthorized"] = nil
30
+ path = "#{uri.path}?#{Rack::Utils.build_query(params)}"
31
+ end
32
+ end
33
+
34
+ redirect_to path
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -11,7 +11,7 @@ module Decidim
11
11
 
12
12
  layout "decidim/admin/decidim_awesome"
13
13
 
14
- helper_method :constraints_for
14
+ helper_method :constraints_for, :users_for
15
15
  before_action do
16
16
  enforce_permission_to :edit_config, configs
17
17
  end
@@ -35,32 +35,37 @@ module Decidim
35
35
  end
36
36
  end
37
37
 
38
- def new_scoped_style
39
- CreateScopedStyle.call(current_organization) do
40
- on(:ok) do |key|
41
- flash[:notice] = I18n.t("config.create_scoped_style.success", key: key, scope: "decidim.decidim_awesome.admin")
42
- end
43
-
44
- on(:invalid) do |message|
45
- flash[:alert] = I18n.t("config.create_scoped_style.error", error: message, scope: "decidim.decidim_awesome.admin")
38
+ def users
39
+ respond_to do |format|
40
+ format.json do
41
+ if (term = params[:term].to_s).present?
42
+ query = current_organization.users.order(name: :asc)
43
+ query = query.where("name ILIKE :term OR nickname ILIKE :term OR email ILIKE :term", term: "%#{term}%")
44
+
45
+ render json: query.all.collect { |u| { id: u.id, text: format_user_name(u) } }
46
+ else
47
+ render json: []
48
+ end
46
49
  end
47
50
  end
48
-
49
- redirect_to decidim_admin_decidim_awesome.config_path(:styles)
50
51
  end
51
52
 
52
- def destroy_scoped_style
53
- DestroyScopedStyle.call(params[:key], current_organization) do
54
- on(:ok) do |key|
55
- flash[:notice] = I18n.t("config.destroy_scoped_style.success", key: key, scope: "decidim.decidim_awesome.admin")
53
+ def rename_scope_label
54
+ RenameScopeLabel.call(params, current_organization) do
55
+ on(:ok) do |result|
56
+ render json: result.merge({
57
+ html: render_to_string(partial: "decidim/decidim_awesome/admin/config/constraints",
58
+ locals: {
59
+ key: result[:scope],
60
+ constraints: constraints_for(result[:scope])
61
+ })
62
+ })
56
63
  end
57
64
 
58
65
  on(:invalid) do |message|
59
- flash[:alert] = I18n.t("config.destroy_scoped_style.error", error: message, scope: "decidim.decidim_awesome.admin")
66
+ render json: { error: message }, status: :unprocessable_entity
60
67
  end
61
68
  end
62
-
63
- redirect_to decidim_admin_decidim_awesome.config_path(:styles)
64
69
  end
65
70
 
66
71
  private
@@ -74,6 +79,14 @@ module Decidim
74
79
 
75
80
  DecidimAwesome.config.keys
76
81
  end
82
+
83
+ def users_for(ids_list)
84
+ Decidim::User.where(id: ids_list).map { |user| OpenStruct.new(text: format_user_name(user), id: user.id) }
85
+ end
86
+
87
+ def format_user_name(user)
88
+ "<span class='#{"is-admin" if user.read_attribute("admin")}'>#{user.name} (@#{user.nickname} - #{user.email})</span>"
89
+ end
77
90
  end
78
91
  end
79
92
  end
@@ -127,6 +127,10 @@ module Decidim
127
127
  case key
128
128
  when /^scoped_style_/
129
129
  :scoped_styles
130
+ when /^scoped_admin_/
131
+ :scoped_admins
132
+ when /^proposal_custom_field_/
133
+ :proposal_custom_fields
130
134
  else
131
135
  key
132
136
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Admin
6
+ # Global configuration controller
7
+ class ProposalCustomFieldsController < DecidimAwesome::Admin::ConfigController
8
+ def create
9
+ CreateProposalCustomField.call(current_organization) do
10
+ on(:ok) do |key|
11
+ flash[:notice] = I18n.t("config.create_proposal_custom_field.success", key: key, scope: "decidim.decidim_awesome.admin")
12
+ end
13
+
14
+ on(:invalid) do |message|
15
+ flash[:alert] = I18n.t("config.create_proposal_custom_field.error", error: message, scope: "decidim.decidim_awesome.admin")
16
+ end
17
+ end
18
+
19
+ redirect_to decidim_admin_decidim_awesome.config_path(:proposal_custom_fields)
20
+ end
21
+
22
+ def destroy
23
+ DestroyProposalCustomField.call(params[:key], current_organization) do
24
+ on(:ok) do |key|
25
+ flash[:notice] = I18n.t("config.destroy_proposal_custom_field.success", key: key, scope: "decidim.decidim_awesome.admin")
26
+ end
27
+
28
+ on(:invalid) do |message|
29
+ flash[:alert] = I18n.t("config.destroy_proposal_custom_field.error", error: message, scope: "decidim.decidim_awesome.admin")
30
+ end
31
+ end
32
+
33
+ redirect_to decidim_admin_decidim_awesome.config_path(:proposal_custom_fields)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Admin
6
+ # Global configuration controller
7
+ class ScopedAdminsController < DecidimAwesome::Admin::ConfigController
8
+ def create
9
+ CreateScopedAdmin.call(current_organization) do
10
+ on(:ok) do |key|
11
+ flash[:notice] = I18n.t("config.create_scoped_admin.success", key: key, scope: "decidim.decidim_awesome.admin")
12
+ end
13
+
14
+ on(:invalid) do |message|
15
+ flash[:alert] = I18n.t("config.create_scoped_admin.error", error: message, scope: "decidim.decidim_awesome.admin")
16
+ end
17
+ end
18
+
19
+ redirect_to decidim_admin_decidim_awesome.config_path(:admins)
20
+ end
21
+
22
+ def destroy
23
+ DestroyScopedAdmin.call(params[:key], current_organization) do
24
+ on(:ok) do |key|
25
+ flash[:notice] = I18n.t("config.destroy_scoped_admin.success", key: key, scope: "decidim.decidim_awesome.admin")
26
+ end
27
+
28
+ on(:invalid) do |message|
29
+ flash[:alert] = I18n.t("config.destroy_scoped_admin.error", error: message, scope: "decidim.decidim_awesome.admin")
30
+ end
31
+ end
32
+
33
+ redirect_to decidim_admin_decidim_awesome.config_path(:admins)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Admin
6
+ # Global configuration controller
7
+ class ScopedStylesController < DecidimAwesome::Admin::ConfigController
8
+ def create
9
+ CreateScopedStyle.call(current_organization) do
10
+ on(:ok) do |key|
11
+ flash[:notice] = I18n.t("config.create_scoped_style.success", key: key, scope: "decidim.decidim_awesome.admin")
12
+ end
13
+
14
+ on(:invalid) do |message|
15
+ flash[:alert] = I18n.t("config.create_scoped_style.error", error: message, scope: "decidim.decidim_awesome.admin")
16
+ end
17
+ end
18
+
19
+ redirect_to decidim_admin_decidim_awesome.config_path(:styles)
20
+ end
21
+
22
+ def destroy
23
+ DestroyScopedStyle.call(params[:key], current_organization) do
24
+ on(:ok) do |key|
25
+ flash[:notice] = I18n.t("config.destroy_scoped_style.success", key: key, scope: "decidim.decidim_awesome.admin")
26
+ end
27
+
28
+ on(:invalid) do |message|
29
+ flash[:alert] = I18n.t("config.destroy_scoped_style.error", error: message, scope: "decidim.decidim_awesome.admin")
30
+ end
31
+ end
32
+
33
+ redirect_to decidim_admin_decidim_awesome.config_path(:styles)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -4,6 +4,8 @@ module Decidim
4
4
  module DecidimAwesome
5
5
  module Admin
6
6
  class ConfigForm < Decidim::Form
7
+ include ActionView::Helpers::SanitizeHelper
8
+
7
9
  attribute :allow_images_in_full_editor, Boolean
8
10
  attribute :allow_images_in_small_editor, Boolean
9
11
  attribute :allow_images_in_proposals, Boolean
@@ -11,6 +13,8 @@ module Decidim
11
13
  attribute :allow_images_in_markdown_editor, Boolean
12
14
  attribute :auto_save_forms, Boolean
13
15
  attribute :scoped_styles, Hash
16
+ attribute :proposal_custom_fields, Hash
17
+ attribute :scoped_admins, Hash
14
18
  attribute :menu, Array[MenuForm]
15
19
  attribute :intergram_for_admins, Boolean
16
20
  attribute :intergram_for_admins_settings, IntergramForm
@@ -21,21 +25,56 @@ module Decidim
21
25
  attr_accessor :valid_keys
22
26
 
23
27
  validate :css_syntax, if: ->(form) { form.scoped_styles.present? }
28
+ validate :json_syntax, if: ->(form) { form.proposal_custom_fields.present? }
29
+
30
+ # TODO: validate non general admins are here
24
31
 
25
32
  def self.from_params(params, additional_params = {})
26
33
  instance = super(params, additional_params)
27
34
  instance.valid_keys = params.keys.map(&:to_sym) || []
35
+ instance.sanitize_labels!
28
36
  instance
29
37
  end
30
38
 
31
39
  def css_syntax
32
40
  scoped_styles.each do |key, code|
41
+ next unless code
42
+
33
43
  SassC::Engine.new(code).render
34
44
  rescue SassC::SyntaxError => e
35
45
  errors.add(:scoped_styles, I18n.t("config.form.errors.incorrect_css", key: key, scope: "decidim.decidim_awesome.admin"))
36
46
  errors.add(key.to_sym, e.message)
37
47
  end
38
48
  end
49
+
50
+ def json_syntax
51
+ proposal_custom_fields.each do |key, code|
52
+ next unless code
53
+
54
+ JSON.parse(code)
55
+ rescue JSON::ParserError => e
56
+ errors.add(:scoped_styles, I18n.t("config.form.errors.incorrect_json", key: key, scope: "decidim.decidim_awesome.admin"))
57
+ errors.add(key.to_sym, e.message)
58
+ end
59
+ end
60
+
61
+ # formBuilder has a bug and do not sanitize text if users copy/paste text with format in the label input
62
+ def sanitize_labels!
63
+ return unless proposal_custom_fields
64
+
65
+ proposal_custom_fields.transform_values! do |code|
66
+ next unless code
67
+
68
+ json = JSON.parse(code)
69
+ json.map! do |item|
70
+ item["label"] = strip_tags(item["label"])
71
+ item
72
+ end
73
+ JSON.generate(json)
74
+ rescue JSON::ParserError
75
+ code
76
+ end
77
+ end
39
78
  end
40
79
  end
41
80
  end
@@ -10,7 +10,9 @@ module Decidim
10
10
  attribute :component_manifest, String
11
11
  attribute :component_id, Integer
12
12
 
13
- validates :component_manifest, absence: true, if: ->(form) { form.component_id.present? || form.participatory_space_manifest == "system" }
13
+ validates :component_manifest, absence: true, if: lambda { |form|
14
+ form.component_id.present? || ConfigConstraintsHelpers::OTHER_MANIFESTS.include?(form.participatory_space_manifest&.to_sym)
15
+ }
14
16
  validates :component_id, absence: true, if: ->(form) { form.component_manifest.present? }
15
17
  end
16
18
  end
@@ -4,6 +4,8 @@ module Decidim
4
4
  module DecidimAwesome
5
5
  module Admin
6
6
  module ConfigConstraintsHelpers
7
+ OTHER_MANIFESTS = [:none, :system, :process_groups].freeze
8
+
7
9
  include Decidim::TranslatableAttributes
8
10
 
9
11
  def check(status)
@@ -28,7 +30,7 @@ module Decidim
28
30
  end
29
31
 
30
32
  def participatory_space_manifests
31
- manifests = { system: I18n.t("decidim.decidim_awesome.admin.config.system") }
33
+ manifests = OTHER_MANIFESTS.map { |m| [m, I18n.t("decidim.decidim_awesome.admin.config.#{m}")] }.to_h
32
34
  Decidim.participatory_space_manifests.pluck(:name).each do |name|
33
35
  manifests[name.to_sym] = I18n.t("decidim.admin.menu.#{name}")
34
36
  end
@@ -36,7 +38,7 @@ module Decidim
36
38
  end
37
39
 
38
40
  def component_manifests(space = nil)
39
- return {} if space == "system"
41
+ return {} if OTHER_MANIFESTS.include?(space)
40
42
 
41
43
  Decidim.component_manifests.pluck(:name).map do |name|
42
44
  [name.to_sym, I18n.t("decidim.components.#{name}.name")]
@@ -44,17 +46,17 @@ module Decidim
44
46
  end
45
47
 
46
48
  def participatory_spaces_list(manifest)
47
- space = participatory_space_for_manifest(manifest)
49
+ space = model_for_manifest(manifest)
48
50
  return {} if space.blank?
49
51
 
50
52
  space.where(organization: current_organization).map do |item|
51
- [item.slug, translated_attribute(item.title)]
53
+ [item.try(:slug) || item.id.to_s, translated_attribute(item.title)]
52
54
  end.to_h
53
55
  end
54
56
 
55
57
  def components_list(manifest, slug)
56
- space = participatory_space_for_manifest(manifest)
57
- return {} if space.blank?
58
+ space = model_for_manifest(manifest)
59
+ return {} unless space&.column_names&.include? "slug"
58
60
 
59
61
  components = Component.where(participatory_space: space.find_by(slug: slug))
60
62
  components.map do |item|
@@ -84,9 +86,11 @@ module Decidim
84
86
 
85
87
  private
86
88
 
87
- def participatory_space_for_manifest(manifest)
89
+ def model_for_manifest(manifest)
88
90
  return nil if manifest.blank?
89
91
 
92
+ return Decidim::ParticipatoryProcessGroup if manifest == "process_groups"
93
+
90
94
  space = Decidim.find_participatory_space_manifest(manifest)
91
95
  return nil unless space
92
96
 
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module AmendmentsHelperOverride
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ # original method
10
+ alias_method :decidim_amendments_form_field_for, :amendments_form_field_for
11
+
12
+ # override with custom fields if scope applies
13
+ def amendments_form_field_for(attribute, form, original_resource)
14
+ custom_fields = awesome_custom_fields(attribute, form)
15
+ return decidim_amendments_form_field_for(attribute, form, original_resource) if custom_fields.blank?
16
+
17
+ render_amendment_custom_fields_override(custom_fields, attribute, form, original_resource)
18
+ end
19
+
20
+ private
21
+
22
+ def render_amendment_custom_fields_override(fields, attribute, form, original_resource)
23
+ custom_fields = Decidim::DecidimAwesome::CustomFields.new(fields)
24
+ custom_fields.translate!
25
+ body = amendments_form_fields_value(original_resource, attribute)
26
+ custom_fields.apply_xml(body) if body.present?
27
+ # TODO: find a way to add errors as form is not the parent form
28
+ # form.object.errors.add(attribute, custom_fields.errors) if custom_fields.errors
29
+ render partial: "decidim/decidim_awesome/custom_fields/form_render", locals: { spec: custom_fields.to_json, form: form, name: attribute }
30
+ end
31
+
32
+ # Amendments don't use a URL specifying participatory space and component
33
+ # context for awesome config constraints must be obtained from the resource
34
+ def awesome_custom_fields(attribute, _form)
35
+ return unless attribute == :body
36
+
37
+ component = amendable.try(:component)
38
+ return unless component
39
+ return if component.settings.participatory_texts_enabled?
40
+
41
+ awesome_config = Decidim::DecidimAwesome::Config.new(component.organization)
42
+ awesome_config.context_from_component(component)
43
+ awesome_config.collect_sub_configs_values("proposal_custom_field")
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -5,11 +5,19 @@ module Decidim
5
5
  module MapHelper
6
6
  include Decidim::MapHelper
7
7
 
8
+ # rubocop:disable Metrics/CyclomaticComplexity
9
+ # rubocop:disable Metrics/PerceivedComplexity:
8
10
  def awesome_map_for(components, &block)
9
- map = dynamic_map_for({}, {}, &block)
10
- return unless map
11
+ return unless map_utility_dynamic
11
12
 
12
- map_html_options = {
13
+ map = awesome_builder.map_element({ class: "google-map" }, &block)
14
+ help = content_tag(:div, class: "map__help") do
15
+ sr_content = content_tag(:p, t("screen_reader_explanation", scope: "decidim.map.dynamic"), class: "show-for-sr")
16
+
17
+ sr_content
18
+ end
19
+
20
+ html_options = {
13
21
  "class" => "awesome-map",
14
22
  "id" => "awesome-map",
15
23
  "data-components" => components.map do |component|
@@ -21,20 +29,40 @@ module Decidim
21
29
  amendments: component.manifest.name == :proposals ? Decidim::Proposals::Proposal.where(component: component).only_emendations.count : 0
22
30
  }
23
31
  end.to_json,
24
- "data-collapsed" => current_component.settings.collapse,
25
- "data-truncate" => current_component.settings.truncate,
26
- "data-map-center" => current_component.settings.map_center,
27
- "data-map-zoom" => current_component.settings.map_zoom,
28
- "data-menu-amendments" => current_component.settings.menu_amendments,
29
- "data-menu-meetings" => current_component.settings.menu_meetings,
30
- "data-menu-hashtags" => current_component.settings.menu_hashtags,
31
- "data-show-not-answered" => current_component.current_settings.show_not_answered,
32
- "data-show-accepted" => current_component.current_settings.show_accepted,
33
- "data-show-withdrawn" => current_component.current_settings.show_withdrawn,
34
- "data-show-evaluating" => current_component.current_settings.show_evaluating,
35
- "data-show-rejected" => current_component.current_settings.show_rejected
32
+ "data-hide-controls" => settings_source.try(:hide_controls),
33
+ "data-collapsed" => global_settings.collapse,
34
+ "data-truncate" => global_settings.truncate || 255,
35
+ "data-map-center" => global_settings.map_center,
36
+ "data-map-zoom" => global_settings.map_zoom || 8,
37
+ "data-menu-amendments" => global_settings.menu_amendments,
38
+ "data-menu-meetings" => global_settings.menu_meetings,
39
+ "data-menu-hashtags" => global_settings.menu_hashtags,
40
+ "data-show-not-answered" => step_settings&.show_not_answered,
41
+ "data-show-accepted" => step_settings&.show_accepted,
42
+ "data-show-withdrawn" => step_settings&.show_withdrawn,
43
+ "data-show-evaluating" => step_settings&.show_evaluating,
44
+ "data-show-rejected" => step_settings&.show_rejected
36
45
  }
37
- content_tag(:div, map, map_html_options)
46
+
47
+ content_tag(:div, html_options) do
48
+ content_tag :div, class: "row column" do
49
+ help + map
50
+ end
51
+ end
52
+ end
53
+ # rubocop:enable Metrics/CyclomaticComplexity
54
+ # rubocop:enable Metrics/PerceivedComplexity:
55
+
56
+ def step_settings
57
+ settings_source.try(:current_settings)
58
+ end
59
+
60
+ def global_settings
61
+ settings_source.try(:settings)
62
+ end
63
+
64
+ def settings_source
65
+ try(:current_component) || self
38
66
  end
39
67
 
40
68
  # rubocop:disable Rails/HelperInstanceVariable
@@ -56,6 +84,29 @@ module Decidim
56
84
 
57
85
  private
58
86
 
87
+ def awesome_builder
88
+ options = {
89
+ popup_template_id: "marker-popup",
90
+ markers: []
91
+ }
92
+ builder = map_utility_dynamic.create_builder(self, options)
93
+
94
+ unless snippets.any?(:map)
95
+ snippets.add(:map, builder.stylesheet_snippets)
96
+ snippets.add(:map, builder.javascript_snippets)
97
+ snippets.add(:head, snippets.for(:map))
98
+ end
99
+
100
+ unless snippets.any?(:awesome_map)
101
+ snippets.add(:awesome_map, stylesheet_link_tag("decidim/decidim_awesome/awesome_map/map"))
102
+ snippets.add(:awesome_map, javascript_include_tag("decidim/decidim_awesome/awesome_map/map"))
103
+ snippets.add(:awesome_map, javascript_include_tag("decidim/decidim_awesome/awesome_map/load_map"))
104
+ snippets.add(:head, snippets.for(:awesome_map))
105
+ end
106
+
107
+ builder
108
+ end
109
+
59
110
  # rubocop:disable Style/FormatStringToken
60
111
  def append_category(category)
61
112
  @h += @golden_ratio_conjugate