decidim-participatory_documents 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/lint.yml +2 -2
- data/.github/workflows/test.yml +15 -5
- data/.ruby-version +1 -1
- data/.simplecov +15 -9
- data/Gemfile.lock +196 -192
- data/README.md +42 -28
- data/app/cells/decidim/participatory_documents/suggestion/admin_answer.erb +1 -1
- data/app/commands/decidim/participatory_documents/admin/create_document.rb +6 -2
- data/app/commands/decidim/participatory_documents/create_suggestion.rb +3 -1
- data/app/controllers/concerns/decidim/participatory_documents/needs_pdf_document.rb +8 -1
- data/app/controllers/decidim/participatory_documents/admin/documents_controller.rb +6 -4
- data/app/controllers/decidim/participatory_documents/admin/suggestions_controller.rb +30 -2
- data/app/controllers/decidim/participatory_documents/admin/valuation_assignments_controller.rb +13 -3
- data/app/controllers/decidim/participatory_documents/document_suggestions_controller.rb +14 -0
- data/app/controllers/decidim/participatory_documents/section_suggestions_controller.rb +4 -0
- data/app/forms/decidim/participatory_documents/admin/document_form.rb +9 -1
- data/app/jobs/decidim/participatory_documents/export_my_suggestions_job.rb +16 -0
- data/app/models/decidim/participatory_documents/document.rb +11 -0
- data/app/models/decidim/participatory_documents/suggestion.rb +12 -0
- data/app/packs/entrypoints/decidim_participatory_documents.scss +8 -0
- data/app/packs/entrypoints/decidim_participatory_documents_editor.js +1 -3
- data/app/packs/entrypoints/decidim_participatory_documents_viewer_off.js +6 -0
- data/app/packs/images/export_suggestions.svg +1 -0
- data/app/packs/src/decidim/participatory_documents/pdf/pdf_modal_manager.js +4 -1
- data/app/packs/src/decidim/participatory_documents/pdf/suggestion_form.js +1 -1
- data/app/packs/src/decidim/participatory_documents/pdf.js +46 -0
- data/app/packs/src/decidim/participatory_documents/pdf_off.js +39 -0
- data/app/packs/stylesheets/decidim/participatory_documents/pdf/admin_modals.scss +9 -5
- data/app/packs/stylesheets/decidim/participatory_documents/pdf/admin_tweaks.scss +0 -21
- data/app/packs/stylesheets/decidim/participatory_documents/{decidim_admin_classes.scss → pdf/decidim_admin_styles.scss} +0 -3
- data/app/packs/stylesheets/decidim/participatory_documents/pdf/modals.scss +49 -0
- data/app/packs/stylesheets/decidim/participatory_documents/pdf/tweaks.scss +76 -7
- data/app/permissions/decidim/participatory_documents/admin/permissions.rb +32 -25
- data/app/permissions/decidim/participatory_documents/permissions.rb +2 -0
- data/app/serializers/decidim/participatory_documents/my_suggestion_serializer.rb +22 -0
- data/app/serializers/decidim/participatory_documents/suggestion_serializer.rb +1 -1
- data/app/uploaders/decidim/participatory_documents/pdf_document_uploader.rb +4 -0
- data/app/views/decidim/participatory_documents/admin/documents/_editor_modal.html.erb +1 -1
- data/app/views/decidim/participatory_documents/admin/documents/edit_pdf.html.erb +1 -0
- data/app/views/decidim/participatory_documents/admin/documents/pdf_viewer.html.erb +1 -1
- data/app/views/decidim/participatory_documents/admin/sections/_form.html.erb +0 -3
- data/app/views/decidim/participatory_documents/admin/suggestions/_add_valuators.html.erb +1 -1
- data/app/views/decidim/participatory_documents/admin/suggestions/_suggestion.html.erb +1 -1
- data/app/views/decidim/participatory_documents/admin/suggestions/_valuators.html.erb +1 -2
- data/app/views/decidim/participatory_documents/documents/_export_modal.html.erb +25 -0
- data/app/views/decidim/participatory_documents/documents/_pdfjs_base.html.erb +5 -2
- data/app/views/decidim/participatory_documents/documents/index.html.erb +2 -2
- data/app/views/decidim/participatory_documents/documents/pdf_viewer.html.erb +40 -33
- data/config/assets.rb +1 -0
- data/config/locales/ca.yml +261 -0
- data/config/locales/de.yml +261 -0
- data/config/locales/en.yml +15 -0
- data/config/locales/es.yml +261 -0
- data/lib/decidim/participatory_documents/component.rb +6 -4
- data/lib/decidim/participatory_documents/engine.rb +5 -1
- data/lib/decidim/participatory_documents/version.rb +2 -2
- data/lib/decidim/participatory_documents.rb +9 -3
- 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
|
-
|
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
|
-
|
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
|
-
|
57
|
-
|
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
|
-
|
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
|
|
@@ -21,8 +21,12 @@ module Decidim
|
|
21
21
|
create_document
|
22
22
|
end
|
23
23
|
broadcast(:ok, document)
|
24
|
-
rescue ActiveRecord::RecordInvalid
|
25
|
-
|
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 =>
|
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).
|
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
|
-
|
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).
|
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,
|
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.
|
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
|
data/app/controllers/decidim/participatory_documents/admin/valuation_assignments_controller.rb
CHANGED
@@ -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
|
-
|
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 @@
|
|
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();
|
@@ -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
|
-
|
8
|
-
|
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/
|
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
|
+
}
|