decidim-participatory_documents 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yml +2 -2
  3. data/.github/workflows/test.yml +15 -5
  4. data/.ruby-version +1 -1
  5. data/.simplecov +15 -9
  6. data/Gemfile.lock +196 -192
  7. data/README.md +42 -28
  8. data/app/cells/decidim/participatory_documents/suggestion/admin_answer.erb +1 -1
  9. data/app/commands/decidim/participatory_documents/admin/create_document.rb +6 -2
  10. data/app/commands/decidim/participatory_documents/create_suggestion.rb +3 -1
  11. data/app/controllers/concerns/decidim/participatory_documents/needs_pdf_document.rb +8 -1
  12. data/app/controllers/decidim/participatory_documents/admin/documents_controller.rb +6 -4
  13. data/app/controllers/decidim/participatory_documents/admin/suggestions_controller.rb +30 -2
  14. data/app/controllers/decidim/participatory_documents/admin/valuation_assignments_controller.rb +13 -3
  15. data/app/controllers/decidim/participatory_documents/document_suggestions_controller.rb +14 -0
  16. data/app/controllers/decidim/participatory_documents/section_suggestions_controller.rb +4 -0
  17. data/app/forms/decidim/participatory_documents/admin/document_form.rb +9 -1
  18. data/app/jobs/decidim/participatory_documents/export_my_suggestions_job.rb +16 -0
  19. data/app/models/decidim/participatory_documents/document.rb +11 -0
  20. data/app/models/decidim/participatory_documents/suggestion.rb +12 -0
  21. data/app/packs/entrypoints/decidim_participatory_documents.scss +8 -0
  22. data/app/packs/entrypoints/decidim_participatory_documents_editor.js +1 -3
  23. data/app/packs/entrypoints/decidim_participatory_documents_viewer_off.js +6 -0
  24. data/app/packs/images/export_suggestions.svg +1 -0
  25. data/app/packs/src/decidim/participatory_documents/pdf/pdf_modal_manager.js +4 -1
  26. data/app/packs/src/decidim/participatory_documents/pdf/suggestion_form.js +1 -1
  27. data/app/packs/src/decidim/participatory_documents/pdf.js +46 -0
  28. data/app/packs/src/decidim/participatory_documents/pdf_off.js +39 -0
  29. data/app/packs/stylesheets/decidim/participatory_documents/pdf/admin_modals.scss +9 -5
  30. data/app/packs/stylesheets/decidim/participatory_documents/pdf/admin_tweaks.scss +0 -21
  31. data/app/packs/stylesheets/decidim/participatory_documents/{decidim_admin_classes.scss → pdf/decidim_admin_styles.scss} +0 -3
  32. data/app/packs/stylesheets/decidim/participatory_documents/pdf/modals.scss +49 -0
  33. data/app/packs/stylesheets/decidim/participatory_documents/pdf/tweaks.scss +76 -7
  34. data/app/permissions/decidim/participatory_documents/admin/permissions.rb +32 -25
  35. data/app/permissions/decidim/participatory_documents/permissions.rb +2 -0
  36. data/app/serializers/decidim/participatory_documents/my_suggestion_serializer.rb +22 -0
  37. data/app/serializers/decidim/participatory_documents/suggestion_serializer.rb +1 -1
  38. data/app/uploaders/decidim/participatory_documents/pdf_document_uploader.rb +4 -0
  39. data/app/views/decidim/participatory_documents/admin/documents/_editor_modal.html.erb +1 -1
  40. data/app/views/decidim/participatory_documents/admin/documents/edit_pdf.html.erb +1 -0
  41. data/app/views/decidim/participatory_documents/admin/documents/pdf_viewer.html.erb +1 -1
  42. data/app/views/decidim/participatory_documents/admin/sections/_form.html.erb +0 -3
  43. data/app/views/decidim/participatory_documents/admin/suggestions/_add_valuators.html.erb +1 -1
  44. data/app/views/decidim/participatory_documents/admin/suggestions/_suggestion.html.erb +1 -1
  45. data/app/views/decidim/participatory_documents/admin/suggestions/_valuators.html.erb +1 -2
  46. data/app/views/decidim/participatory_documents/documents/_export_modal.html.erb +25 -0
  47. data/app/views/decidim/participatory_documents/documents/_pdfjs_base.html.erb +5 -2
  48. data/app/views/decidim/participatory_documents/documents/index.html.erb +2 -2
  49. data/app/views/decidim/participatory_documents/documents/pdf_viewer.html.erb +40 -33
  50. data/config/assets.rb +1 -0
  51. data/config/locales/ca.yml +261 -0
  52. data/config/locales/de.yml +261 -0
  53. data/config/locales/en.yml +15 -0
  54. data/config/locales/es.yml +261 -0
  55. data/lib/decidim/participatory_documents/component.rb +6 -4
  56. data/lib/decidim/participatory_documents/engine.rb +5 -1
  57. data/lib/decidim/participatory_documents/version.rb +2 -2
  58. data/lib/decidim/participatory_documents.rb +9 -3
  59. metadata +12 -3
data/README.md CHANGED
@@ -8,12 +8,17 @@
8
8
 
9
9
  This module allows to upload PDF (and possibilty other formats) and define areas on top of it that will become spaces for suggestions, improvements and other participative activities.
10
10
 
11
- > NOTE: in development, not ready for production.
12
-
13
11
  ## Installation
14
12
 
15
13
  Add this line to your application's Gemfile:
16
14
 
15
+ ```ruby
16
+ gem 'decidim-participatory_documents
17
+ ```
18
+
19
+ Or, if you want to stay up to date with the latest changes use this line instead:
20
+
21
+
17
22
  ```ruby
18
23
  gem 'decidim-participatory_documents', git: "https://github.com/openpoke/decidim-module-participatory-documents"
19
24
  ```
@@ -23,44 +28,53 @@ And then execute:
23
28
  ```
24
29
  bundle
25
30
  bundle exec rails decidim_participatory_documents:install:migrations
31
+ bundle exec rails decidim_participatory_documents:install_pdf_js
32
+ bundle exec rails db:migrate
26
33
  ```
27
34
 
35
+ Depending on your Decidim version, you can choose the corresponding version to ensure compatibility:
36
+
37
+ | Version | Compatible Decidim versions |
38
+ |---|---|
39
+ | 0.2.x | 0.27.x |
40
+
41
+
28
42
  ## Usage
29
43
 
30
- TODO...
44
+ This module adds a new component to Decidim called `Participatory Documents` that allows to upload PDFs and define areas on top of it that will become spaces for suggestions or comments.
31
45
 
46
+ The administrator must upload a PDF file and then define areas on top of it by drawing polygons.
47
+ Each area will become a new zone that will allow users to create suggestions.
32
48
 
33
- Options such as `:max_suggestion_length`, `:min_suggestion_length` and etc. are configured at the component level in
34
- the application. Defaults to the configured in the module:
35
- ```ruby
36
- # lib/decidim/participatory_documents.rb
37
-
38
- module Decidim
39
- # This namespace holds the logic of the `decidim-participatory_documents` module.
40
- module ParticipatoryDocuments
41
- include ActiveSupport::Configurable
42
-
43
- # Public: The minimum length of a suggestion to be considered valid.
44
- config_accessor :min_suggestion_length do
45
- 5
46
- end
47
-
48
- # Public: The maximum length of a suggestion to be considered valid.
49
- config_accessor :max_suggestion_length do
50
- 500
51
- end
52
- end
53
- end
54
- ```
49
+ ## Configuration
55
50
 
56
- ```ruby
57
- # config/initializers/participatory_documents.rb
51
+ By default, the module is configured to read the configuration from ENV variables.
52
+
53
+ Currently, the following ENV variables are supported:
54
+
55
+ | ENV variable | Description | Default value |
56
+ | ------------ | ----------- |-------|
57
+ | MAX_EXPORT_TEXT_LENGTH | If a positive number, it will truncate the exported suggestions before sending them by email | `0` |
58
+ | MIN_SUGGESTION_LENGTH | Minimum characters in a suggestion to be valid (this setting can be configured in each component as well by the admins) | `5` |
59
+ | MAX_SUGGESTION_LENGTH | Maximum characters in a suggestion to be valid (this setting can be configured in each component as well by the admins) | `1000` |
58
60
 
61
+ It is also possible to configure the module using the `decidim-participatory_documents` initializer:
62
+
63
+ ```ruby
59
64
  Decidim::ParticipatoryDocuments.configure do |config|
65
+ config.max_export_text_length = 0
66
+ config.min_suggestion_length = 5
67
+ config.max_suggestion_length = 1000
60
68
  end
61
69
  ```
62
70
 
63
- And, of course, having these values in your `config/secrets.yml` file.
71
+ ## Antivirus compatibility
72
+
73
+ This module has a builtin compatibility with https://github.com/mainio/decidim-module-antivirus to scan the uploaded documents (it is also possible to directly use the gem https://github.com/mainio/ratonvirus if configuring it in a initializer).
74
+
75
+ If the antivirus is not installed, the module will still work but the documents will not be scanned.
76
+
77
+ > Note: this module only checks for the existance of the class `AntivirusValidator` so it is possible to use any other antivirus validator as well (a custom one for instance).
64
78
 
65
79
  ## Contributing
66
80
 
@@ -3,5 +3,5 @@
3
3
  <strong><%= answered_at %></strong>
4
4
  <strong><%= t("suggestion_answer", scope: "decidim.participatory_documents.document") %></strong>
5
5
  </div>
6
- <%= translated_attribute(model.answer) %>
6
+ <%= sanitize translated_attribute(model.answer) %>
7
7
  </div>
@@ -21,8 +21,12 @@ module Decidim
21
21
  create_document
22
22
  end
23
23
  broadcast(:ok, document)
24
- rescue ActiveRecord::RecordInvalid
25
- form.errors.add(:file, document.errors[:file]) if document.errors.include? :file
24
+ rescue ActiveRecord::RecordInvalid => e
25
+ if document&.errors&.include? :file
26
+ form.errors.add(:file, document.errors[:file])
27
+ else
28
+ form.errors.add(:file, e.message)
29
+ end
26
30
  broadcast(:invalid)
27
31
  end
28
32
  end
@@ -29,10 +29,12 @@ module Decidim
29
29
  attr_reader :form, :suggestion, :suggestable
30
30
 
31
31
  def create_suggestion
32
+ sanitized_body = Decidim::ContentProcessor.sanitize(form.body)
33
+
32
34
  @suggestion = Decidim.traceability.create!(
33
35
  Decidim::ParticipatoryDocuments::Suggestion,
34
36
  form.current_user,
35
- { body: { I18n.locale => form.body },
37
+ { body: { I18n.locale => sanitized_body },
36
38
  suggestable: suggestable,
37
39
  author: form.current_user },
38
40
  visibility: "public-only"
@@ -7,7 +7,7 @@ module Decidim
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  included do
10
- helper_method :document, :box_color_as_rgba, :pdf_custom_style
10
+ helper_method :document, :box_color_as_rgba, :pdf_custom_style, :all_suggestions
11
11
 
12
12
  protected
13
13
 
@@ -22,6 +22,13 @@ module Decidim
22
22
  @document ||= Decidim::ParticipatoryDocuments::Document.find_by(component: current_component)
23
23
  end
24
24
 
25
+ def all_suggestions
26
+ @all_suggestions ||= begin
27
+ manifest = document.component.manifest.export_manifests.find { |man| man.name == :suggestions }
28
+ manifest.collection.call(document.component, current_user, :my_suggestions)
29
+ end
30
+ end
31
+
25
32
  def box_color_as_rgba(document, opacity: nil)
26
33
  opacity ||= document.box_opacity
27
34
  return "rgba(30, 152, 215, 0.12);" unless document.box_color.present? && opacity.present?
@@ -6,6 +6,7 @@ module Decidim
6
6
  # This controller allows admins to manage documents in a participatory process.
7
7
  class DocumentsController < Admin::ApplicationController
8
8
  include Decidim::ApplicationHelper
9
+ include Decidim::ComponentPathHelper
9
10
  include NeedsAdminSnippets
10
11
 
11
12
  helper Decidim::LayoutHelper
@@ -13,7 +14,6 @@ module Decidim
13
14
 
14
15
  before_action except: [:index, :new, :create] do
15
16
  redirect_to(documents_path) if document.blank?
16
- redirect_to(edit_document_path(document)) unless document.file.attached?
17
17
  end
18
18
 
19
19
  def index
@@ -22,7 +22,7 @@ module Decidim
22
22
 
23
23
  def new
24
24
  enforce_permission_to :create, :participatory_document
25
- @form = form(DocumentForm).from_params(params)
25
+ @form = form(DocumentForm).instance
26
26
  end
27
27
 
28
28
  def create
@@ -32,7 +32,9 @@ module Decidim
32
32
  CreateDocument.call(@form) do
33
33
  on(:ok) do |document|
34
34
  flash[:notice] = I18n.t("documents.create.success", scope: "decidim.participatory_documents.admin")
35
- redirect_to edit_pdf_documents_path(id: document.id)
35
+
36
+ redirect_to(edit_pdf_documents_path(id: document.id)) && return if document.file.attached?
37
+ redirect_to(manage_component_path(document.component)) && return unless document.file.attached?
36
38
  end
37
39
 
38
40
  on(:invalid) do
@@ -69,7 +71,7 @@ module Decidim
69
71
  end
70
72
 
71
73
  def pdf_viewer
72
- @form = form(SectionForm).from_params({})
74
+ @form = form(SectionForm).instance
73
75
  render layout: false
74
76
  end
75
77
 
@@ -11,9 +11,12 @@ module Decidim
11
11
  helper Decidim::ParticipatoryDocuments::Admin::SuggestionHelper
12
12
  helper Decidim::Messaging::ConversationHelper
13
13
 
14
- helper_method :suggestions, :suggestion, :notes_form, :find_valuators_for_select, :suggestion_ids, :suggestion_find
14
+ helper_method :suggestions, :suggestion, :notes_form, :find_valuators_for_select, :suggestion_ids,
15
+ :suggestion_find, :valuator_assigned_to_suggestion?
15
16
 
16
17
  def show
18
+ enforce_permission_to :show, :suggestion, suggestion: suggestion
19
+
17
20
  @form = form(Decidim::ParticipatoryDocuments::Admin::AnswerSuggestionForm).from_model(suggestion)
18
21
  end
19
22
 
@@ -133,11 +136,29 @@ module Decidim
133
136
  end
134
137
 
135
138
  def base_query
139
+ valuator_roles_exist? ? suggestions_for_valuator : all_document_suggestions
140
+ end
141
+
142
+ def all_document_suggestions
136
143
  Suggestion.where(suggestable: document).or(Suggestion.where(suggestable: document.sections))
137
144
  end
138
145
 
146
+ def suggestions_for_valuator
147
+ valuator_suggestions_ids = Decidim::ParticipatoryDocuments::ValuationAssignment
148
+ .where(valuator_role: valuator_roles).pluck(:decidim_participatory_documents_suggestion_id)
149
+ Suggestion.where(id: valuator_suggestions_ids)
150
+ end
151
+
152
+ def valuator_roles
153
+ current_participatory_space.user_roles(:valuator).where(user: current_user)
154
+ end
155
+
156
+ def valuator_roles_exist?
157
+ valuator_roles.exists?
158
+ end
159
+
139
160
  def suggestion
140
- base_query.find(params[:id])
161
+ base_query.find_by(id: params[:id])
141
162
  end
142
163
 
143
164
  # Internal: A method to cache to queries to find the valuators for the
@@ -154,6 +175,13 @@ module Decidim
154
175
  [valuator.name, role.id]
155
176
  end
156
177
  end
178
+
179
+ def valuator_assigned_to_suggestion?
180
+ @valuator_assigned_to_suggestion ||=
181
+ Decidim::ParticipatoryDocuments::ValuationAssignment
182
+ .where(suggestion: suggestion, valuator_role: valuator_roles)
183
+ .any?
184
+ end
157
185
  end
158
186
  end
159
187
  end
@@ -4,8 +4,10 @@ module Decidim
4
4
  module ParticipatoryDocuments
5
5
  module Admin
6
6
  class ValuationAssignmentsController < Admin::ApplicationController
7
+ helper_method :suggestion
8
+
7
9
  def create
8
- enforce_permission_to :assign_to_valuator, :suggestions
10
+ enforce_permission_to :assign_to_valuator, :suggestions, suggestion: suggestion
9
11
 
10
12
  @form = form(Admin::ValuationAssignmentForm).from_params(params)
11
13
 
@@ -25,12 +27,16 @@ module Decidim
25
27
  def destroy
26
28
  @form = form(Admin::ValuationAssignmentForm).from_params(destroy_params)
27
29
 
28
- enforce_permission_to :unassign_from_valuator, :suggestions, valuator: @form.valuator_user
30
+ enforce_permission_to :unassign_from_valuator, :suggestions, valuator: @form.valuator_user, suggestion: suggestion
29
31
 
30
32
  Admin::UnassignSuggestionsFromValuator.call(@form) do
31
33
  on(:ok) do |_proposal|
32
34
  flash.keep[:notice] = I18n.t("valuation_assignments.delete.success", scope: "decidim.participatory_documents.admin")
33
- redirect_back fallback_location: EngineRouter.admin_proxy(current_component).root_path
35
+ if current_user == @form.valuator_user
36
+ redirect_to EngineRouter.admin_proxy(current_component).root_path
37
+ else
38
+ redirect_back fallback_location: EngineRouter.admin_proxy(current_component).root_path
39
+ end
34
40
  end
35
41
 
36
42
  on(:invalid) do
@@ -42,6 +48,10 @@ module Decidim
42
48
 
43
49
  private
44
50
 
51
+ def suggestion
52
+ @suggestion ||= Decidim::ParticipatoryDocuments::Suggestion.find(params[:suggestion_ids] || [params[:suggestion_id]])
53
+ end
54
+
45
55
  def destroy_params
46
56
  {
47
57
  id: params.dig(:valuator_role, :id) || params[:id],
@@ -10,10 +10,14 @@ module Decidim
10
10
  layout false
11
11
 
12
12
  def index
13
+ enforce_permission_to :create, :suggestion
14
+
13
15
  @form = form(Decidim::ParticipatoryDocuments::SuggestionForm).instance
14
16
  end
15
17
 
16
18
  def create
19
+ enforce_permission_to :create, :suggestion
20
+
17
21
  @form = form(Decidim::ParticipatoryDocuments::SuggestionForm).from_params(params)
18
22
 
19
23
  CreateSuggestion.call(@form, section) do
@@ -26,6 +30,16 @@ module Decidim
26
30
  end
27
31
  end
28
32
 
33
+ def export
34
+ enforce_permission_to :create, :suggestion
35
+
36
+ return render json: { message: t(".empty") }, status: :unprocessable_entity unless all_suggestions.any?
37
+
38
+ ExportMySuggestionsJob.perform_later(current_user, document, "Excel")
39
+
40
+ render json: { message: t(".success", count: all_suggestions&.count, email: current_user&.email) }
41
+ end
42
+
29
43
  private
30
44
 
31
45
  def suggestions
@@ -10,10 +10,14 @@ module Decidim
10
10
  layout false
11
11
 
12
12
  def index
13
+ enforce_permission_to :create, :suggestion
14
+
13
15
  @form = form(Decidim::ParticipatoryDocuments::SuggestionForm).instance
14
16
  end
15
17
 
16
18
  def create
19
+ enforce_permission_to :create, :suggestion
20
+
17
21
  @form = form(Decidim::ParticipatoryDocuments::SuggestionForm).from_params(params)
18
22
 
19
23
  CreateSuggestion.call(@form, section) do
@@ -14,15 +14,23 @@ module Decidim
14
14
 
15
15
  attribute :box_color, String, default: "#1e98d7"
16
16
  attribute :box_opacity, Integer, default: 12
17
+ attribute :organization
17
18
 
18
- attribute :file
19
+ attribute :file, Decidim::Attributes::Blob
19
20
  attribute :remove_file, Boolean, default: false
20
21
 
22
+ validates :file, passthru: { to: Document }, if: ->(form) { form.file.present? }
23
+ validates :file, file_content_type: { allow: ["application/pdf"] }
24
+
21
25
  # ensure color and opacity are present
22
26
  def map_model(doc)
23
27
  self.box_color = doc.box_color.presence || "#1e98d7"
24
28
  self.box_opacity = doc.box_opacity.presence || 12
25
29
  end
30
+
31
+ def organization
32
+ attributes[:organization] || current_organization
33
+ end
26
34
  end
27
35
  end
28
36
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ParticipatoryDocuments
5
+ class ExportMySuggestionsJob < Decidim::ExportJob
6
+ def perform(user, document, format)
7
+ export_manifest = document.component.manifest.export_manifests.find { |manifest| manifest.name == :suggestions }
8
+ collection = export_manifest.collection.call(document.component, user, :my_suggestions)
9
+
10
+ export_data = Decidim::Exporters.find_exporter(format).new(collection, MySuggestionSerializer).export
11
+
12
+ ExportMailer.export(user, :suggestions, export_data).deliver_now
13
+ end
14
+ end
15
+ end
16
+ end
@@ -17,6 +17,8 @@ module Decidim
17
17
 
18
18
  has_one_attached :file
19
19
  validates_upload :file, uploader: Decidim::ParticipatoryDocuments::PdfDocumentUploader
20
+ # compatibility with ratonvirus (see https://github.com/mainio/decidim-module-antivirus)
21
+ validates :file, antivirus: true if ParticipatoryDocuments.antivirus_enabled
20
22
 
21
23
  has_many :sections, class_name: "Decidim::ParticipatoryDocuments::Section", dependent: :restrict_with_error
22
24
  has_many :suggestions, class_name: "Decidim::ParticipatoryDocuments::Suggestion", dependent: :restrict_with_error, as: :suggestable
@@ -24,6 +26,15 @@ module Decidim
24
26
 
25
27
  attr_accessor :remove_file
26
28
 
29
+ # the dynamic upload validator requires the organization,
30
+ # if the object is not created yet is assigned from the context by the UploadValidationForm using this method
31
+ attr_writer :organization
32
+
33
+ # override the delegate from HasComponent for the dynamic upload validator
34
+ def organization
35
+ component&.organization || @organization
36
+ end
37
+
27
38
  def self.log_presenter_class_for(_log)
28
39
  Decidim::ParticipatoryDocuments::AdminLog::DocumentPresenter
29
40
  end
@@ -29,6 +29,8 @@ module Decidim
29
29
  scope :missing_answer, -> { where(answered_at: nil) }
30
30
  scope :not_published, -> { where(answer_is_published: false) }
31
31
 
32
+ validate :validate_file_type, if: :attached?
33
+
32
34
  POSSIBLE_STATES = %w(not_answered evaluating accepted rejected withdrawn).freeze
33
35
 
34
36
  POSSIBLE_STATES.each do |possible|
@@ -162,6 +164,16 @@ module Decidim
162
164
  def self.export_serializer
163
165
  Decidim::ParticipatoryDocuments::SuggestionSerializer
164
166
  end
167
+
168
+ def validate_file_type
169
+ allowed_extensions = organization.file_upload_settings["allowed_file_extensions"]["default"]
170
+ allowed_content_types = organization.file_upload_settings["allowed_content_types"]["default"]
171
+
172
+ file_extension = File.extname(file.blob.filename.to_s)[1..]
173
+ file_content_type = file.blob.content_type
174
+
175
+ errors.add(:file, :invalid) if allowed_extensions.exclude?(file_extension) || allowed_content_types.exclude?(file_content_type)
176
+ end
165
177
  end
166
178
  end
167
179
  end
@@ -2,10 +2,18 @@
2
2
  @import "stylesheets/decidim/variables";
3
3
  @import "stylesheets/decidim/utils/mixins";
4
4
 
5
+ #loginModal-label {
6
+ outline: none;
7
+ }
8
+
5
9
  .pdf-viewer-container {
6
10
  height: calc(100vh - 30rem);
7
11
  min-height: 50em;
8
12
  padding: 0;
13
+
14
+ @media(max-width: 680px) {
15
+ margin-left: -1.6rem;
16
+ }
9
17
  }
10
18
 
11
19
  pdf-iframe {
@@ -1,10 +1,8 @@
1
1
  import "../src/decidim/participatory_documents/pdf_admin.js"
2
-
3
- import "entrypoints/decidim_participatory_documents_editor.scss";
4
-
5
2
  import $ from "jquery"; // eslint-disable-line id-length
6
3
  import "foundation-sites";
7
4
 
5
+ import "entrypoints/decidim_participatory_documents_editor.scss";
8
6
 
9
7
  const csrfToken = document.getElementsByName("csrf-token");
10
8
  $.ajaxSetup({
@@ -0,0 +1,6 @@
1
+ import "../src/decidim/participatory_documents/pdf_off.js"
2
+
3
+ // Images
4
+ require.context("../images", true)
5
+
6
+ import "entrypoints/decidim_participatory_documents_viewer.scss";
@@ -0,0 +1 @@
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 106.08"><defs><style>.cls-1{fill-rule:evenodd;}</style></defs><title>chat-download</title><path class="cls-1" d="M71.54,54.18A30.15,30.15,0,0,1,94.8,45.43V9a1.54,1.54,0,0,0-.47-1.1,1.59,1.59,0,0,0-1.1-.48H9A1.64,1.64,0,0,0,7.41,9V75.47a1.58,1.58,0,0,0,.47,1.11l.1.1A1.64,1.64,0,0,0,9,77H23.66a3.71,3.71,0,0,1,3.71,3.71v13L43.65,78.09a3.68,3.68,0,0,1,2.56-1H62.78a30,30,0,0,1,8.76-22.88Zm30.67-7.3A30.07,30.07,0,1,1,71.54,96.71a30.48,30.48,0,0,1-7.43-12.25H47.68L26.44,104.83a3.76,3.76,0,0,1-2.78,1.25,3.71,3.71,0,0,1-3.7-3.71V84.46H9a9,9,0,0,1-6.06-2.39l-.28-.26A9,9,0,0,1,0,75.47V9A9,9,0,0,1,9,0H93.23a9,9,0,0,1,9,9V46.88ZM25.42,33.31a3.71,3.71,0,0,1,0-7.41H76.79a3.71,3.71,0,1,1,0,7.41Zm0,21.93a3.71,3.71,0,0,1,0-7.41H59.81a3.71,3.71,0,1,1,0,7.41Zm80.2,18.59a2.56,2.56,0,0,1,2.17,1c1.14,1.72-.42,3.41-1.5,4.59-3.07,3.37-10,9.46-11.52,11.24a2.5,2.5,0,0,1-3.93,0C89.28,88.86,82,82.39,79.05,79.12c-1-1.14-2.27-2.69-1.21-4.27a2.57,2.57,0,0,1,2.17-1h5.46V64.05A3.08,3.08,0,0,1,88.55,61H97.1a3.08,3.08,0,0,1,3.08,3.07v9.78Z"/></svg>
@@ -8,6 +8,7 @@ export default class PdfModalManager {
8
8
  this.csrfToken = options.csrfToken;
9
9
  // UI
10
10
  this.modalLayout = document.getElementById("decidim");
11
+ this.modalLayout.addEventListener("click", this._closeHandler.bind(this));
11
12
  // events
12
13
  this.onSave = () => {};
13
14
  this.onDestroy = () => {};
@@ -29,6 +30,7 @@ export default class PdfModalManager {
29
30
  }
30
31
 
31
32
  displayModal(box) {
33
+ const modal = document.getElementById("editor-modal");
32
34
  const uiSave = document.getElementById("editor-modal-save");
33
35
  const uiClose = document.getElementById("editor-modal-close");
34
36
  const uiTitle = document.getElementById("editor-modal-title");
@@ -38,6 +40,7 @@ export default class PdfModalManager {
38
40
  this.modalLayout.classList.add("show");
39
41
  $(this.modalLayout).foundation();
40
42
 
43
+ modal.addEventListener("click", (evt) => evt.stopPropagation(), { once: true });
41
44
  uiClose.addEventListener("click", this._closeHandler.bind(this), { once: true });
42
45
  uiRemove.addEventListener("click", (evt) => this._removeHandler(box, evt), { once: true });
43
46
  uiSave.addEventListener("click", (evt) => this._saveHandler(box, evt), { once: true });
@@ -61,7 +64,7 @@ export default class PdfModalManager {
61
64
  if (response.ok) {
62
65
  return response.json();
63
66
  }
64
- throw new Error(" ");
67
+ throw new Error(response.statusText);
65
68
  }).
66
69
  then((resp) => {
67
70
  box.setInfo();
@@ -31,7 +31,7 @@ export default class SuggestionForm {
31
31
  }
32
32
 
33
33
  getCSRFToken() {
34
- const token = document.getElementsByName("csrf-token")
34
+ const token = document.getElementsByName("csrf-token");
35
35
 
36
36
  return token.length && token[0].content;
37
37
  }
@@ -15,12 +15,58 @@ window.InitDocumentManagers = (options) => {
15
15
  window.currentSuggestionForm.open();
16
16
  }
17
17
  });
18
+
19
+ const decidim = document.getElementById("decidim");
20
+ options.exportModal.addEventListener("click", (evt) => {
21
+ evt.stopPropagation();
22
+ });
23
+ options.exportButton.addEventListener("click", (evt) => {
24
+ evt.stopPropagation();
25
+ const uiClose = decidim.querySelector(".close-button");
26
+ uiClose.addEventListener("click", () => decidim.classList.remove("show"), { once: true });
27
+ decidim.addEventListener("click", () => decidim.classList.remove("show"), { once: true });
28
+
29
+ decidim.classList.add("show");
30
+ });
31
+ options.exportModal.querySelector(".export-button").addEventListener("click", (evt) => {
32
+ evt.stopPropagation();
33
+ fetch(evt.target.dataset.url, {
34
+ method: "POST",
35
+ headers: {
36
+ "Content-Type": "application/json",
37
+ "Accept": "application/json",
38
+ "X-CSRF-Token": document.getElementsByName("csrf-token").item(0).content
39
+ },
40
+ credentials: "include"
41
+ }).
42
+ then((response) => {
43
+ if (response.ok) {
44
+ return response.json();
45
+ }
46
+ return response.json().then((json) => {
47
+ throw new Error(json.message)
48
+ });
49
+ }).
50
+ then((resp) => {
51
+ // console.log("response ok", resp);
52
+ options.exportModal.querySelector(".content").innerHTML = `<div class="callout success">${resp.message}</div>`;
53
+ }).
54
+ catch((message) => {
55
+ options.exportModal.querySelector(".content").innerHTML = `<div class="callout alert">${message}</div>`;
56
+ // console.error("Error exporting", message);
57
+ });
58
+ });
18
59
  };
19
60
 
20
61
  // Call this on an annotation layer to initialize the polygon viewer (public side)
21
62
  window.InitPolygonViewer = (layer, boxes, options) => {
22
63
  let viewer = new PolygonViewer(layer, boxes, { i18n: options.i18n});
23
64
 
65
+ // prevent hiding the layout due onBoxBlur
66
+ options.participationLayout.addEventListener("click", (evt) => {
67
+ evt.stopPropagation();
68
+ });
69
+
24
70
  viewer.onBoxClick = (box, evt) => {
25
71
  console.log("click on box", box, evt);
26
72
  window.currentSuggestionForm = new SuggestionForm(options.participationLayout, options.documentPath, box.section);
@@ -0,0 +1,39 @@
1
+ /* eslint-disable no-alert */
2
+
3
+ import PolygonViewer from "src/decidim/participatory_documents/pdf/polygon_viewer";
4
+ import "src/decidim/participatory_documents/pdf_notifications";
5
+ import "src/decidim/participatory_documents/global";
6
+
7
+ const openLogin = (evt) => {
8
+ evt.stopPropagation();
9
+ if (!window.parent) {
10
+ // This shouldn't appear because is in a iframe, but just in case (or for developing)
11
+ alert("Login required");
12
+ }
13
+
14
+ // add the rediret_to otherwise is going to be the iframe url and the user will lose the context
15
+ const loginForm = window.parent.document.getElementById("login_new_user");
16
+ let parts = loginForm.action.split("?")
17
+ let params = new URLSearchParams(parts[1]);
18
+ params.append("redirect_url", window.parent.location.href);
19
+ loginForm.action = `${parts[0]}?${params.toString()}`;
20
+
21
+ window.parent.$("#loginModal").foundation("open")
22
+ };
23
+
24
+ window.InitDocumentManagers = (options) => {
25
+ options.globalSuggestionsButton.addEventListener("click", openLogin);
26
+
27
+ options.exportButton.addEventListener("click", openLogin);
28
+ };
29
+
30
+ // Call this on an annotation layer to initialize the polygon viewer (public side)
31
+ window.InitPolygonViewer = (layer, boxes, options) => {
32
+ let viewer = new PolygonViewer(layer, boxes, { i18n: options.i18n});
33
+
34
+ viewer.onBoxClick = (box, evt) => {
35
+ openLogin(evt);
36
+ }
37
+
38
+ return viewer;
39
+ };
@@ -4,10 +4,8 @@
4
4
  @import "stylesheets/decidim/utils/fontface";
5
5
  @import "stylesheets/decidim/admin/utils/settings";
6
6
 
7
- @keyframes blinker {
8
- 50% {
9
- background-color: rgba(var(--primary-rgb), .6);
10
- }
7
+ .reveal-overlay {
8
+ z-index: 100000;
11
9
  }
12
10
 
13
11
  #decidim {
@@ -25,7 +23,7 @@
25
23
  line-height: $global-lineheight;
26
24
  color: $body-font-color;
27
25
 
28
- @include meta.load-css("stylesheets/decidim/participatory_documents/decidim_admin_classes.scss");
26
+ @include meta.load-css("stylesheets/decidim/participatory_documents/pdf/decidim_admin_styles.scss");
29
27
 
30
28
  &.show {
31
29
  display: block;
@@ -39,3 +37,9 @@
39
37
  }
40
38
  }
41
39
  }
40
+
41
+ @keyframes blinker {
42
+ 50% {
43
+ background-color: rgba(var(--primary-rgb), .6);
44
+ }
45
+ }