decidim-decidim_awesome 0.10.2 → 0.10.3

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/CHANGELOG.md +16 -1
  3. data/README.md +134 -15
  4. data/app/cells/concerns/decidim/decidim_awesome/proposal_m_cell_override.rb +4 -24
  5. data/app/cells/decidim/decidim_awesome/content_blocks/map_cell.rb +9 -5
  6. data/app/commands/concerns/decidim/decidim_awesome/admin/needs_constraint_helpers.rb +1 -1
  7. data/app/commands/concerns/decidim/decidim_awesome/proposals/admin/update_proposal_override.rb +31 -0
  8. data/app/commands/concerns/decidim/decidim_awesome/proposals/create_collaborative_draft_override.rb +27 -0
  9. data/app/commands/concerns/decidim/decidim_awesome/proposals/create_proposal_override.rb +27 -0
  10. data/app/commands/concerns/decidim/decidim_awesome/proposals/update_collaborative_draft_override.rb +27 -0
  11. data/app/commands/concerns/decidim/decidim_awesome/proposals/update_proposal_override.rb +26 -0
  12. data/app/commands/decidim/decidim_awesome/admin/create_proposal_custom_field.rb +4 -3
  13. data/app/commands/decidim/decidim_awesome/admin/destroy_proposal_custom_field.rb +6 -3
  14. data/app/controllers/concerns/decidim/decidim_awesome/admin/maintenance_context.rb +43 -0
  15. data/app/controllers/concerns/decidim/decidim_awesome/admin_accountability/admin/filterable_helper.rb +3 -2
  16. data/app/controllers/concerns/decidim/decidim_awesome/limit_pending_amendments.rb +35 -0
  17. data/app/controllers/concerns/decidim/decidim_awesome/proposals/orderable_override.rb +9 -22
  18. data/app/controllers/decidim/decidim_awesome/admin/admin_accountability_controller.rb +7 -7
  19. data/app/controllers/decidim/decidim_awesome/admin/application_controller.rb +2 -0
  20. data/app/controllers/decidim/decidim_awesome/admin/checks_controller.rb +7 -3
  21. data/app/controllers/decidim/decidim_awesome/admin/config_controller.rb +8 -5
  22. data/app/controllers/decidim/decidim_awesome/admin/constraints_controller.rb +3 -1
  23. data/app/controllers/decidim/decidim_awesome/admin/custom_redirects_controller.rb +0 -2
  24. data/app/controllers/decidim/decidim_awesome/admin/maintenance_controller.rb +76 -0
  25. data/app/controllers/decidim/decidim_awesome/admin/menu_hacks_controller.rb +0 -2
  26. data/app/controllers/decidim/decidim_awesome/admin/proposal_custom_fields_controller.rb +12 -4
  27. data/app/controllers/decidim/decidim_awesome/iframe_component/iframe_controller.rb +8 -1
  28. data/app/forms/concerns/decidim/decidim_awesome/proposals/proposal_form_override.rb +21 -0
  29. data/app/forms/{decidim → concerns/decidim}/decidim_awesome/proposals/proposal_wizard_create_step_form_override.rb +1 -0
  30. data/app/forms/decidim/decidim_awesome/admin/config_form.rb +35 -9
  31. data/app/helpers/{decidim → concerns/decidim}/decidim_awesome/amendments_helper_override.rb +17 -7
  32. data/app/helpers/concerns/decidim/decidim_awesome/proposals/application_helper_override.rb +126 -0
  33. data/app/helpers/decidim/decidim_awesome/admin/config_constraints_helpers.rb +5 -26
  34. data/app/jobs/decidim/decidim_awesome/destroy_private_data_job.rb +22 -0
  35. data/app/models/concerns/decidim/decidim_awesome/has_proposal_extra_fields.rb +38 -9
  36. data/app/models/decidim/decidim_awesome/paper_trail_version.rb +5 -1
  37. data/app/models/decidim/decidim_awesome/proposal_extra_field.rb +35 -1
  38. data/app/overrides/decidim/proposals/admin/proposals/show/add_private_body.html.erb.deface +7 -0
  39. data/app/overrides/decidim/proposals/admin/proposals/show/replace_body.html.erb.deface +5 -0
  40. data/app/overrides/decidim/proposals/proposals/show/limit_amendments_modal.html.erb.deface +5 -0
  41. data/app/packs/entrypoints/decidim_decidim_awesome_map.scss +1 -1
  42. data/app/packs/src/decidim/decidim_awesome/admin/auto_edit.js +5 -3
  43. data/app/packs/src/decidim/decidim_awesome/admin/constraints.js +1 -1
  44. data/app/packs/src/decidim/decidim_awesome/admin/custom_fields_builder.js +5 -4
  45. data/app/packs/src/decidim/decidim_awesome/amendments/show_modal_on_limits.js +30 -0
  46. data/app/packs/src/decidim/decidim_awesome/awesome_application.js +1 -0
  47. data/app/packs/src/decidim/decidim_awesome/editors/editor.js +1 -6
  48. data/app/packs/src/decidim/decidim_awesome/editors/tabs_focus.js +18 -9
  49. data/app/packs/src/decidim/decidim_awesome/forms/custom_fields_renderer.js +35 -26
  50. data/app/packs/src/decidim/decidim_awesome/proposals/custom_fields.js +31 -15
  51. data/app/packs/stylesheets/decidim/decidim_awesome/admin/codemirror.scss +12 -7
  52. data/app/packs/stylesheets/decidim/decidim_awesome/admin/constraints.scss +69 -25
  53. data/app/packs/stylesheets/decidim/decidim_awesome/admin/custom_fields.scss +34 -27
  54. data/app/packs/stylesheets/decidim/decidim_awesome/admin/user_picker.scss +2 -2
  55. data/app/packs/stylesheets/decidim/decidim_awesome/awesome_admin.scss +3 -3
  56. data/app/packs/stylesheets/decidim/decidim_awesome/awesome_admin_global.scss +28 -0
  57. data/app/packs/stylesheets/decidim/decidim_awesome/awesome_application.scss +3 -2
  58. data/app/packs/stylesheets/decidim/decidim_awesome/awesome_map/map.scss +15 -15
  59. data/app/packs/stylesheets/decidim/decidim_awesome/editors/quill_editor.scss +3 -3
  60. data/app/packs/stylesheets/decidim/decidim_awesome/forms/autosave.scss +3 -3
  61. data/app/packs/stylesheets/decidim/decidim_awesome/forms/custom_fields.scss +187 -0
  62. data/app/packs/stylesheets/decidim/decidim_awesome/shared/spinner.scss +47 -0
  63. data/app/packs/stylesheets/decidim/decidim_awesome/voting/voting_cards.scss +7 -7
  64. data/app/permissions/decidim/decidim_awesome/admin/permissions.rb +14 -3
  65. data/app/presenters/concerns/decidim/decidim_awesome/proposals/proposal_presenter_override.rb +20 -0
  66. data/app/presenters/decidim/decidim_awesome/admin_log/component_presenter_override.rb +30 -0
  67. data/app/presenters/decidim/decidim_awesome/private_data_presenter.rb +70 -0
  68. data/app/queries/decidim/decidim_awesome/private_data_finder.rb +19 -0
  69. data/app/serializers/concerns/decidim/decidim_awesome/proposal_serializer_override.rb +1 -0
  70. data/app/serializers/concerns/decidim/decidim_awesome/proposals/proposal_serializer_methods.rb +72 -0
  71. data/app/serializers/concerns/decidim/decidim_awesome/proposals/proposal_serializer_override.rb +38 -0
  72. data/app/serializers/decidim/decidim_awesome/proposals/private_proposal_serializer.rb +42 -0
  73. data/app/types/concerns/decidim/decidim_awesome/add_proposal_type_custom_fields.rb +59 -0
  74. data/app/types/concerns/decidim/decidim_awesome/{proposal_type_override.rb → add_proposal_type_vote_weights.rb} +3 -1
  75. data/app/views/decidim/decidim_awesome/admin/checks/index.html.erb +52 -48
  76. data/app/views/decidim/decidim_awesome/admin/config/_autoedit_box_label.html.erb +7 -2
  77. data/app/views/decidim/decidim_awesome/admin/config/_constraints.html.erb +16 -8
  78. data/app/views/decidim/decidim_awesome/admin/config/_form_admins.html.erb +1 -1
  79. data/app/views/decidim/decidim_awesome/admin/config/_form_editors.html.erb +12 -16
  80. data/app/views/decidim/decidim_awesome/admin/config/_form_proposal_custom_fields.html.erb +35 -15
  81. data/app/views/decidim/decidim_awesome/admin/config/_form_proposal_private_custom_fields.html.erb +1 -0
  82. data/app/views/decidim/decidim_awesome/admin/config/_form_proposals.html.erb +22 -22
  83. data/app/views/decidim/decidim_awesome/admin/config/_form_styles.html.erb +1 -1
  84. data/app/views/decidim/decidim_awesome/admin/config/show.html.erb +1 -1
  85. data/app/views/decidim/decidim_awesome/admin/custom_redirects/index.html.erb +14 -13
  86. data/app/views/decidim/decidim_awesome/admin/maintenance/_private_data.html.erb +44 -0
  87. data/app/views/decidim/decidim_awesome/admin/maintenance/show.html.erb +44 -0
  88. data/app/views/decidim/decidim_awesome/admin/menu_hacks/index.html.erb +28 -29
  89. data/app/views/decidim/decidim_awesome/admin/proposals/_editor.html.erb +8 -5
  90. data/app/views/decidim/decidim_awesome/admin/proposals/_private_body.html.erb +20 -0
  91. data/app/views/decidim/decidim_awesome/amendments/_modal.html.erb +16 -0
  92. data/app/views/decidim/decidim_awesome/custom_fields/_form_render.html.erb +10 -2
  93. data/app/views/layouts/decidim/decidim_awesome/admin/_base.html.erb +21 -0
  94. data/app/views/layouts/decidim/decidim_awesome/admin/application.html.erb +1 -73
  95. data/app/views/layouts/decidim/decidim_awesome/admin/maintenance.html.erb +19 -0
  96. data/config/i18n-tasks.yml +22 -3
  97. data/config/locales/ca.yml +97 -9
  98. data/config/locales/cs.yml +109 -6
  99. data/config/locales/de.yml +92 -6
  100. data/config/locales/en.yml +102 -8
  101. data/config/locales/es.yml +96 -8
  102. data/config/locales/eu.yml +15 -1
  103. data/config/locales/fr.yml +94 -6
  104. data/config/locales/hu.yml +53 -4
  105. data/config/locales/it.yml +16 -6
  106. data/config/locales/ja.yml +94 -6
  107. data/config/locales/lt.yml +0 -2
  108. data/config/locales/nl.yml +9 -4
  109. data/config/locales/pt-BR.yml +16 -7
  110. data/config/locales/ro-RO.yml +11 -2
  111. data/config/locales/sv.yml +17 -6
  112. data/db/migrate/20240531224204_add_decidim_awesome_proposal_private_fields.rb +29 -0
  113. data/db/migrate/20240729164227_add_decidim_awesome_proposal_private_fields_date.rb +20 -0
  114. data/lib/decidim/decidim_awesome/admin_engine.rb +22 -6
  115. data/lib/decidim/decidim_awesome/api/types/localized_custom_fields_type.rb +22 -0
  116. data/lib/decidim/decidim_awesome/api/types/translated_custom_fields_type.rb +52 -0
  117. data/lib/decidim/decidim_awesome/awesome.rb +45 -8
  118. data/lib/decidim/decidim_awesome/awesome_helpers.rb +5 -1
  119. data/lib/decidim/decidim_awesome/checksums.yml +23 -0
  120. data/lib/decidim/decidim_awesome/custom_fields.rb +8 -0
  121. data/lib/decidim/decidim_awesome/engine.rb +143 -52
  122. data/lib/decidim/decidim_awesome/lock.rb +47 -0
  123. data/lib/decidim/decidim_awesome/menu.rb +146 -0
  124. data/lib/decidim/decidim_awesome/test/initializer.rb +4 -1
  125. data/lib/decidim/decidim_awesome/test/shared_examples/{box_label_editor.rb → box_label_editor_examples.rb} +1 -1
  126. data/lib/decidim/decidim_awesome/test/shared_examples/config_examples.rb +20 -2
  127. data/lib/decidim/decidim_awesome/test/shared_examples/custom_fields_examples.rb +155 -0
  128. data/lib/decidim/decidim_awesome/test/shared_examples/editor_examples.rb +24 -0
  129. data/lib/decidim/decidim_awesome/test/shared_examples/menu_hack_contexts.rb +2 -2
  130. data/lib/decidim/decidim_awesome/test/shared_examples/scoped_admins_examples.rb +3 -5
  131. data/lib/decidim/decidim_awesome/test/shared_examples/summary_examples.rb +78 -12
  132. data/lib/decidim/decidim_awesome/version.rb +1 -1
  133. data/package.json +2 -2
  134. metadata +52 -13
  135. data/app/helpers/decidim/decidim_awesome/proposals/application_helper_override.rb +0 -78
  136. /data/app/presenters/{decidim → concerns/decidim}/decidim_awesome/menu_item_presenter_override.rb +0 -0
  137. /data/app/presenters/{decidim → concerns/decidim}/decidim_awesome/menu_presenter_override.rb +0 -0
@@ -5,7 +5,7 @@ module Decidim
5
5
  module Admin
6
6
  class AdminAccountabilityController < DecidimAwesome::Admin::ApplicationController
7
7
  include NeedsAwesomeConfig
8
- include Decidim::DecidimAwesome::AdminAccountability::Admin::Filterable
8
+ include AdminAccountability::Admin::Filterable
9
9
 
10
10
  helper_method :admin_actions, :collection, :export_params, :global?, :global_users_missing_date
11
11
 
@@ -20,9 +20,9 @@ module Decidim
20
20
  def export
21
21
  filters = export_params[:q]
22
22
 
23
- Decidim::DecidimAwesome::ExportAdminActionsJob.perform_later(current_user,
24
- params[:format].to_s,
25
- admin_actions.ransack(filters).result.ids)
23
+ ExportAdminActionsJob.perform_later(current_user,
24
+ params[:format].to_s,
25
+ admin_actions.ransack(filters).result.ids)
26
26
 
27
27
  redirect_back fallback_location: decidim_admin_decidim_awesome.admin_accountability_path,
28
28
  notice: t("decidim.decidim_awesome.admin.admin_accountability.exports.notice")
@@ -39,11 +39,11 @@ module Decidim
39
39
  end
40
40
 
41
41
  def space_role_actions
42
- @space_role_actions ||= Decidim::DecidimAwesome::PaperTrailVersion.space_role_actions(current_organization)
42
+ @space_role_actions ||= PaperTrailVersion.space_role_actions(current_organization)
43
43
  end
44
44
 
45
45
  def admin_role_actions
46
- @admin_role_actions ||= Decidim::DecidimAwesome::PaperTrailVersion.in_organization(current_organization).admin_role_actions(params[:admin_role_type])
46
+ @admin_role_actions ||= PaperTrailVersion.in_organization(current_organization).admin_role_actions(PaperTrailVersion.safe_admin_role_type(params[:admin_role_type]))
47
47
  end
48
48
 
49
49
  def export_params
@@ -61,7 +61,7 @@ module Decidim
61
61
  return unless global?
62
62
 
63
63
  @global_users_missing_date ||= begin
64
- first_version = Decidim::DecidimAwesome::PaperTrailVersion.where(item_type: "Decidim::UserBaseEntity").last
64
+ first_version = PaperTrailVersion.where(item_type: "Decidim::UserBaseEntity").last
65
65
  first_user = Decidim::User.first
66
66
  first_version.created_at if first_user && first_version && (first_version.created_at > first_user.created_at + 1.second)
67
67
  end
@@ -9,6 +9,8 @@ module Decidim
9
9
  # Note that it inherits from `Decidim::Admin::Components::BaseController`, which
10
10
  # override its layout and provide all kinds of useful methods.
11
11
  class ApplicationController < Decidim::Admin::ApplicationController
12
+ layout "decidim/decidim_awesome/admin/application"
13
+
12
14
  def permission_class_chain
13
15
  [::Decidim::DecidimAwesome::Admin::Permissions] + super
14
16
  end
@@ -8,17 +8,17 @@ module Decidim
8
8
  # System compatibility analyzer
9
9
  class ChecksController < DecidimAwesome::Admin::ApplicationController
10
10
  include NeedsAwesomeConfig
11
+ include MaintenanceContext
12
+
11
13
  helper ConfigConstraintsHelpers
12
14
  helper SystemCheckerHelpers
13
15
 
14
- layout "decidim/decidim_awesome/admin/application"
15
-
16
16
  helper_method :head, :admin_head, :head_addons, :admin_addons
17
17
 
18
18
  def migrate_images
19
19
  Decidim::DecidimAwesome::MigrateLegacyImagesJob.perform_later(current_organization.id)
20
20
  flash[:notice] = I18n.t("image_migrations_started", scope: "decidim.decidim_awesome.admin.checks.index")
21
- redirect_to checks_path
21
+ redirect_to checks_maintenance_index_path
22
22
  end
23
23
 
24
24
  private
@@ -58,6 +58,10 @@ module Decidim
58
58
  rescue ActionView::Template::Error => e
59
59
  flash.now[:alert] = "Partial [#{partial}] has thrown an error: #{e.message}"
60
60
  end
61
+
62
+ def current_view
63
+ "checks"
64
+ end
61
65
  end
62
66
  end
63
67
  end
@@ -9,8 +9,6 @@ module Decidim
9
9
  include ConfigConstraintsHelpers
10
10
  helper ConfigConstraintsHelpers
11
11
 
12
- layout "decidim/decidim_awesome/admin/application"
13
-
14
12
  helper_method :constraints_for, :users_for, :config_var
15
13
  before_action do
16
14
  enforce_permission_to :edit_config, configs
@@ -19,7 +17,7 @@ module Decidim
19
17
  def show
20
18
  @form = form(ConfigForm).from_params(organization_awesome_config)
21
19
 
22
- redirect_to decidim_admin_decidim_awesome.checks_path unless config_var
20
+ redirect_to decidim_admin_decidim_awesome.checks_maintenance_index_path unless config_var
23
21
  end
24
22
 
25
23
  def update
@@ -43,8 +41,13 @@ module Decidim
43
41
  if (term = params[:term].to_s).present?
44
42
  query = current_organization.users.order(name: :asc)
45
43
  query = query.where("name ILIKE :term OR nickname ILIKE :term OR email ILIKE :term", term: "%#{term}%")
46
-
47
- render json: query.all.collect { |u| { id: u.id, text: format_user_name(u) } }
44
+ render json: query.all.collect { |u|
45
+ {
46
+ value: u.id,
47
+ text: format_user_name(u),
48
+ is_admin: u.read_attribute("admin")
49
+ }
50
+ }
48
51
  else
49
52
  render json: []
50
53
  end
@@ -10,7 +10,7 @@ module Decidim
10
10
 
11
11
  layout false
12
12
  before_action do
13
- enforce_permission_to :edit_config, constraint_key
13
+ render plain: "no permissions for #{constraint_key}" unless allowed_to? :edit_config, constraint_key
14
14
  end
15
15
 
16
16
  def new
@@ -131,6 +131,8 @@ module Decidim
131
131
  :scoped_admins
132
132
  when /^proposal_custom_field_/
133
133
  :proposal_custom_fields
134
+ when /^proposal_private_custom_field_/
135
+ :proposal_private_custom_fields
134
136
  else
135
137
  key
136
138
  end
@@ -8,8 +8,6 @@ module Decidim
8
8
  include NeedsAwesomeConfig
9
9
  include ConfigConstraintsHelpers
10
10
 
11
- layout "decidim/decidim_awesome/admin/application"
12
-
13
11
  before_action do
14
12
  enforce_permission_to :edit_config, :menu
15
13
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/decidim_awesome/version"
4
+
5
+ module Decidim
6
+ module DecidimAwesome
7
+ module Admin
8
+ # System compatibility analyzer
9
+ class MaintenanceController < DecidimAwesome::Admin::ApplicationController
10
+ include NeedsAwesomeConfig
11
+ include MaintenanceContext
12
+ include Decidim::Admin::Filterable
13
+ include ActionView::Helpers::DateHelper
14
+
15
+ helper ConfigConstraintsHelpers
16
+ helper_method :collection, :resource, :present, :time_ago
17
+
18
+ before_action do
19
+ enforce_permission_to :edit_config, :private_data, private_data: private_data
20
+ end
21
+
22
+ def show
23
+ respond_to do |format|
24
+ format.json do
25
+ render json: private_data_finder.for(params[:resources].to_s.split(",")).map { |resource| present(resource) }
26
+ end
27
+ format.all do
28
+ render :show
29
+ end
30
+ end
31
+ end
32
+
33
+ def destroy_private_data
34
+ if private_data && private_data.total.to_i.positive?
35
+ Decidim::ActionLogger.log("destroy_private_data", current_user, resource, nil, count: private_data.total)
36
+
37
+ Lock.new(current_organization).get!(resource)
38
+ DestroyPrivateDataJob.set(wait: 1.second).perform_later(resource)
39
+ end
40
+ redirect_to decidim_admin_decidim_awesome.maintenance_path("private_data"),
41
+ notice: I18n.t("destroying_private_data", scope: "decidim.decidim_awesome.admin.maintenance.private_data", title: present_private_data(resource).name)
42
+ end
43
+
44
+ private
45
+
46
+ def resource
47
+ @resource ||= Component.find_by(id: params[:resource_id])
48
+ end
49
+
50
+ def private_data
51
+ @private_data ||= present_private_data(resource) if resource
52
+ end
53
+
54
+ def collection
55
+ filtered_collection
56
+ end
57
+
58
+ def base_query
59
+ private_data_finder.query
60
+ end
61
+
62
+ def present(resource)
63
+ present_private_data(resource)
64
+ end
65
+
66
+ def private_data_finder
67
+ @private_data_finder ||= PrivateDataFinder.new
68
+ end
69
+
70
+ def time_ago
71
+ @time_ago ||= time_ago_in_words(Time.current - Decidim::DecidimAwesome.private_data_expiration_time)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -8,8 +8,6 @@ module Decidim
8
8
  include NeedsAwesomeConfig
9
9
  include ConfigConstraintsHelpers
10
10
 
11
- layout "decidim/decidim_awesome/admin/application"
12
-
13
11
  helper ConfigConstraintsHelpers
14
12
  helper_method :current_items, :visibility_options, :target_options
15
13
 
@@ -6,7 +6,7 @@ module Decidim
6
6
  # Global configuration controller
7
7
  class ProposalCustomFieldsController < DecidimAwesome::Admin::ConfigController
8
8
  def create
9
- CreateProposalCustomField.call(current_organization) do
9
+ CreateProposalCustomField.call(current_organization, config_var) do
10
10
  on(:ok) do |key|
11
11
  flash[:notice] = I18n.t("config.create_proposal_custom_field.success", key: key, scope: "decidim.decidim_awesome.admin")
12
12
  end
@@ -16,11 +16,11 @@ module Decidim
16
16
  end
17
17
  end
18
18
 
19
- redirect_to decidim_admin_decidim_awesome.config_path(:proposal_custom_fields)
19
+ redirect_to decidim_admin_decidim_awesome.config_path(config_var)
20
20
  end
21
21
 
22
22
  def destroy
23
- DestroyProposalCustomField.call(params[:key], current_organization) do
23
+ DestroyProposalCustomField.call(params[:key], current_organization, config_var) do
24
24
  on(:ok) do |key|
25
25
  flash[:notice] = I18n.t("config.destroy_proposal_custom_field.success", key: key, scope: "decidim.decidim_awesome.admin")
26
26
  end
@@ -30,7 +30,15 @@ module Decidim
30
30
  end
31
31
  end
32
32
 
33
- redirect_to decidim_admin_decidim_awesome.config_path(:proposal_custom_fields)
33
+ redirect_to decidim_admin_decidim_awesome.config_path(config_var)
34
+ end
35
+
36
+ private
37
+
38
+ def config_var
39
+ return :proposal_private_custom_fields if params[:private] == "true"
40
+
41
+ :proposal_custom_fields
34
42
  end
35
43
  end
36
44
  end
@@ -17,7 +17,14 @@ module Decidim
17
17
 
18
18
  def sanitize(html)
19
19
  sanitizer = Rails::Html::SafeListSanitizer.new
20
- sanitizer.sanitize(html, tags: %w(iframe), attributes: ALLOWED_ATTRIBUTES)
20
+ partially_sanitized_html = sanitizer.sanitize(html, tags: %w(iframe), attributes: ALLOWED_ATTRIBUTES)
21
+
22
+ document = Nokogiri::HTML::DocumentFragment.parse(partially_sanitized_html)
23
+ document.css("iframe").each do |iframe|
24
+ iframe["srcdoc"] = Loofah.fragment(iframe["srcdoc"]).scrub!(:prune).to_s if iframe["srcdoc"]
25
+ end
26
+
27
+ document.to_s
21
28
  end
22
29
 
23
30
  def remove_margins?
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Proposals
6
+ module ProposalFormOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ alias_method :decidim_original_map_model, :map_model
11
+ attribute :private_body, String
12
+
13
+ def map_model(model)
14
+ decidim_original_map_model(model)
15
+ self.private_body = model.private_body
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -15,6 +15,7 @@ module Decidim
15
15
  minimum: ->(form) { form.minimum_title_length },
16
16
  maximum: 150
17
17
  }
18
+
18
19
  validates :body, presence: true, unless: ->(form) { form.override_validations? || form.minimum_body_length.zero? }
19
20
  validates :body, etiquette: true, unless: ->(form) { form.override_validations? }
20
21
  validates :body, proposal_length: {
@@ -10,14 +10,17 @@ module Decidim
10
10
 
11
11
  attribute :allow_images_in_full_editor, Boolean
12
12
  attribute :allow_images_in_small_editor, Boolean
13
+ attribute :allow_videos_in_editors, Boolean
13
14
  attribute :allow_images_in_proposals, Boolean
14
15
  attribute :use_markdown_editor, Boolean
15
16
  attribute :allow_images_in_markdown_editor, Boolean
16
17
  attribute :auto_save_forms, Boolean
18
+ attribute :auto_save_forms, Boolean
17
19
  attribute :scoped_styles, Hash
18
20
  attribute :proposal_custom_fields, Hash
21
+ attribute :proposal_private_custom_fields, Hash
19
22
  attribute :scoped_admins, Hash
20
- attribute :menu, Array[MenuForm]
23
+ attribute :menu, [MenuForm]
21
24
  attribute :intergram_for_admins, Boolean
22
25
  attribute :intergram_for_admins_settings, IntergramForm
23
26
  attribute :intergram_for_public, Boolean
@@ -36,7 +39,8 @@ module Decidim
36
39
  attr_accessor :valid_keys
37
40
 
38
41
  validate :css_syntax, if: ->(form) { form.scoped_styles.present? }
39
- validate :json_syntax, if: ->(form) { form.proposal_custom_fields.present? }
42
+ validate :json_syntax
43
+
40
44
  validates :validate_title_min_length, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 100 }
41
45
  validates :validate_title_max_caps_percent, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }
42
46
  validates :validate_title_max_marks_together, presence: true, numericality: { greater_than_or_equal_to: 1 }
@@ -71,21 +75,41 @@ module Decidim
71
75
  end
72
76
 
73
77
  def json_syntax
74
- proposal_custom_fields.each do |key, code|
75
- next unless code
78
+ fields = {}
79
+ fields.merge!(proposal_custom_fields: proposal_custom_fields.values) if proposal_custom_fields.present?
80
+ fields.merge!(proposal_private_custom_fields: proposal_private_custom_fields.values) if proposal_private_custom_fields.present?
81
+ fields.each do |key, values|
82
+ next if values.blank?
76
83
 
77
- JSON.parse(code)
78
- rescue JSON::ParserError => e
79
- errors.add(:scoped_styles, I18n.t("config.form.errors.incorrect_json", key: key, scope: "decidim.decidim_awesome.admin"))
84
+ values.each { |code| JSON.parse(code) }
85
+ rescue JSON::JSONError => e
86
+ errors.add(key, I18n.t("config.form.errors.incorrect_json", key: key, scope: "decidim.decidim_awesome.admin"))
80
87
  errors.add(key.to_sym, e.message)
81
88
  end
82
89
  end
83
90
 
84
91
  # formBuilder has a bug and do not sanitize text if users copy/paste text with format in the label input
92
+ # rubocop:disable Metrics/CyclomaticComplexity
93
+ # rubocop:disable Metrics/PerceivedComplexity
85
94
  def sanitize_labels!
86
- return unless proposal_custom_fields
95
+ if proposal_custom_fields
96
+ proposal_custom_fields.transform_values! do |code|
97
+ next unless code
98
+
99
+ json = JSON.parse(code)
100
+ json.map! do |item|
101
+ item["label"] = strip_tags(item["label"])
102
+ item
103
+ end
104
+ JSON.generate(json)
105
+ rescue JSON::ParserError
106
+ code
107
+ end
108
+ end
109
+
110
+ return unless proposal_private_custom_fields
87
111
 
88
- proposal_custom_fields.transform_values! do |code|
112
+ proposal_private_custom_fields.transform_values! do |code|
89
113
  next unless code
90
114
 
91
115
  json = JSON.parse(code)
@@ -98,6 +122,8 @@ module Decidim
98
122
  code
99
123
  end
100
124
  end
125
+ # rubocop:enable Metrics/CyclomaticComplexity
126
+ # rubocop:enable Metrics/PerceivedComplexity
101
127
  end
102
128
  end
103
129
  end
@@ -11,21 +11,28 @@ module Decidim
11
11
 
12
12
  # override with custom fields if scope applies
13
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)
14
+ custom_fields, custom_private_fields = awesome_custom_fields(attribute, form)
15
+ content = if custom_fields.blank?
16
+ decidim_amendments_form_field_for(attribute, form, original_resource)
17
+ else
18
+ render_amendment_custom_fields_override(custom_fields, attribute, form, original_resource)
19
+ end
20
+ if custom_private_fields.present?
21
+ content = content_tag("div", content)
22
+ content += content_tag("div", render_amendment_custom_fields_override(custom_private_fields, :private_body, form, original_resource))
23
+ end
24
+ content
18
25
  end
19
26
 
20
27
  private
21
28
 
22
- def render_amendment_custom_fields_override(fields, attribute, form, original_resource)
23
- custom_fields = Decidim::DecidimAwesome::CustomFields.new(fields)
29
+ def render_amendment_custom_fields_override(custom_fields, attribute, form, original_resource)
24
30
  custom_fields.translate!
25
31
  body = amendments_form_fields_value(original_resource, attribute)
26
32
  custom_fields.apply_xml(body) if body.present?
27
33
  # TODO: find a way to add errors as form is not the parent form
28
34
  # form.object.errors.add(attribute, custom_fields.errors) if custom_fields.errors
35
+
29
36
  render partial: "decidim/decidim_awesome/custom_fields/form_render", locals: { spec: custom_fields.to_json, form: form, name: attribute }
30
37
  end
31
38
 
@@ -40,7 +47,10 @@ module Decidim
40
47
 
41
48
  awesome_config = Decidim::DecidimAwesome::Config.new(component.organization)
42
49
  awesome_config.context_from_component(component)
43
- awesome_config.collect_sub_configs_values("proposal_custom_field")
50
+
51
+ pub = awesome_config.collect_sub_configs_values("proposal_custom_field")
52
+ priv = awesome_config.collect_sub_configs_values("proposal_private_custom_field")
53
+ [pub.presence && Decidim::DecidimAwesome::CustomFields.new(pub), priv.presence && Decidim::DecidimAwesome::CustomFields.new(priv)]
44
54
  end
45
55
  end
46
56
  end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ module Proposals
6
+ module ApplicationHelperOverride
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ alias_method :decidim_text_editor_for_proposal_body, :text_editor_for_proposal_body
11
+ alias_method :decidim_render_proposal_body, :render_proposal_body
12
+
13
+ # If the content is safe, HTML tags are sanitized, otherwise, they are stripped.
14
+ def render_proposal_body(proposal)
15
+ if awesome_proposal_custom_fields.present? || awesome_config[:allow_images_in_full_editor] || awesome_config[:allow_images_in_small_editor]
16
+ content = present(proposal).body(links: true, strip_tags: false)
17
+ sanitized = decidim_sanitize_editor_admin(content, {})
18
+ Decidim::ContentProcessor.render_without_format(sanitized).html_safe
19
+ else
20
+ decidim_render_proposal_body(proposal)
21
+ end
22
+ end
23
+
24
+ # replace normal method to draw the editor
25
+ def text_editor_for_proposal_body(form)
26
+ custom_fields = awesome_proposal_custom_fields_for(:body)
27
+ custom_private_fields = awesome_proposal_custom_fields_for(:private_body)
28
+
29
+ content = if custom_fields.empty?
30
+ decidim_text_editor_for_proposal_body(form)
31
+ else
32
+ render_proposal_custom_fields_override(custom_fields, form, :body)
33
+ end
34
+
35
+ unless custom_private_fields.empty?
36
+ content = content_tag("div", content)
37
+ content += content_tag("div", render_proposal_custom_fields_override(custom_private_fields, form, :private_body))
38
+ end
39
+ content
40
+ end
41
+
42
+ # replace admin method to draw the editor (multi lang)
43
+ def admin_editor_for_proposal_body(form)
44
+ custom_fields = awesome_proposal_custom_fields_for(:body)
45
+
46
+ return if custom_fields.empty?
47
+
48
+ locales = form.send(:locales)
49
+ return render_proposal_custom_fields_override(custom_fields, form, "body_#{locales.first}", locales.first) if locales.length == 1
50
+
51
+ tabs_id = form.send(:sanitize_tabs_selector, form.options[:tabs_id] || "#{form.object_name}-body-tabs")
52
+
53
+ label_tabs = form.content_tag(:div, class: "label--tabs") do
54
+ language_selector = "".html_safe
55
+ language_selector = form.create_language_selector(locales, tabs_id, "body") if form.options[:label] != false
56
+
57
+ safe_join [content_tag("label"), language_selector]
58
+ end
59
+
60
+ tabs_content = form.content_tag(:div, class: "tabs-content", data: { tabs_content: tabs_id }) do
61
+ locales.each_with_index.inject("".html_safe) do |string, (locale, index)|
62
+ tab_content_id = "#{tabs_id}-body-panel-#{index}"
63
+ string + content_tag(:div, class: form.send(:tab_element_class_for, "panel", index), id: tab_content_id, "aria-hidden": index.zero? ? "false" : "true") do
64
+ render_proposal_custom_fields_override(custom_fields, form, "body_#{locale}", locale)
65
+ end
66
+ end
67
+ end
68
+
69
+ safe_join [label_tabs, tabs_content]
70
+ end
71
+
72
+ def render_proposal_custom_fields_override(custom_fields, form, name, locale = nil)
73
+ custom_fields.translate!
74
+
75
+ body = extract_body_content(name, locale)
76
+ apply_custom_fields(custom_fields, body, form, name)
77
+
78
+ render partial: "decidim/decidim_awesome/custom_fields/form_render", locals: { spec: custom_fields.to_json, form: form, name: name }
79
+ end
80
+
81
+ def awesome_proposal_custom_fields_for(name)
82
+ if name == :private_body
83
+ Decidim::DecidimAwesome::CustomFields.new(awesome_proposal_private_custom_fields)
84
+ else
85
+ Decidim::DecidimAwesome::CustomFields.new(awesome_proposal_custom_fields)
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def extract_body_content(name, locale)
92
+ case name
93
+ when :private_body
94
+ extract_private_body(locale)
95
+ else
96
+ extract_body(locale)
97
+ end
98
+ end
99
+
100
+ def extract_private_body(locale)
101
+ if form_presenter.proposal.private_body.is_a?(Hash) && locale.present?
102
+ form_presenter.private_body(extras: false, all_locales: locale.present?).with_indifferent_access[locale]
103
+ else
104
+ form_presenter.private_body(extras: false)
105
+ end
106
+ end
107
+
108
+ def extract_body(locale)
109
+ if form_presenter.proposal.body.is_a?(Hash) && locale.present?
110
+ form_presenter.body(extras: false, all_locales: locale.present?).with_indifferent_access[locale]
111
+ else
112
+ form_presenter.body(extras: false)
113
+ end
114
+ end
115
+
116
+ def apply_custom_fields(custom_fields, body, form, name)
117
+ if body.present?
118
+ custom_fields.apply_xml(body)
119
+ form.object.errors.add(name, custom_fields.errors) if custom_fields.errors
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -8,40 +8,19 @@ module Decidim
8
8
 
9
9
  include Decidim::TranslatableAttributes
10
10
 
11
- def check(status)
12
- content_tag(:span, icon(status ? "check" : "x", class: "icon", aria_label: status, role: "img"), class: "text-#{status ? "success" : "alert"}")
13
- end
11
+ delegate :menus, :config_enabled?, to: "Decidim::DecidimAwesome::Menu"
14
12
 
15
- def menus
16
- @menus ||= {
17
- editors: config_enabled?([:allow_images_in_full_editor, :allow_images_in_small_editor, :use_markdown_editor, :allow_images_in_markdown_editor]),
18
- proposals: config_enabled?([:allow_images_in_proposals,
19
- :validate_title_min_length, :validate_title_max_caps_percent,
20
- :validate_title_max_marks_together, :validate_title_start_with_caps,
21
- :validate_body_min_length, :validate_body_max_caps_percent,
22
- :validate_body_max_marks_together, :validate_body_start_with_caps]),
23
- surveys: config_enabled?(:auto_save_forms),
24
- styles: config_enabled?(:scoped_styles),
25
- proposal_custom_fields: config_enabled?(:proposal_custom_fields),
26
- admins: config_enabled?(:scoped_admins),
27
- menu_hacks: config_enabled?(:menu),
28
- custom_redirects: config_enabled?(:custom_redirects),
29
- livechat: config_enabled?([:intergram_for_admins, :intergram_for_public])
30
- }
13
+ def check(status)
14
+ content_tag(:span, icon(status ? "check" : "close", class: "inline-block", aria_label: status, role: "img"), class: "text-#{status ? "success" : "alert"}")
31
15
  end
32
16
 
33
17
  # returns only non :disabled vars in config
34
18
  def enabled_configs(vars)
35
- vars.filter do |var|
36
- config_enabled? var
19
+ vars.filter do |conf|
20
+ config_enabled?(conf)
37
21
  end
38
22
  end
39
23
 
40
- # ensure boolean value
41
- def config_enabled?(var)
42
- DecidimAwesome.enabled?(var)
43
- end
44
-
45
24
  def participatory_space_manifests
46
25
  manifests = OTHER_MANIFESTS.index_with { |m| I18n.t("decidim.decidim_awesome.admin.config.#{m}") }
47
26
  Decidim.participatory_space_manifests.pluck(:name).each do |name|
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DecidimAwesome
5
+ class DestroyPrivateDataJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ # Destroys private data associated with the resource
9
+ def perform(resource)
10
+ extra_fields = Decidim::DecidimAwesome::ProposalExtraField.where(
11
+ proposal: Decidim::Proposals::Proposal.where(component: resource)
12
+ ).where("private_body_updated_at < ?", DecidimAwesome.private_data_expiration_time.ago)
13
+
14
+ extra_fields.find_each do |extra_field|
15
+ extra_field.update(private_body: nil)
16
+ end
17
+
18
+ Lock.new(resource.organization).release!(resource)
19
+ end
20
+ end
21
+ end
22
+ end