decidim-participatory_documents 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yml +2 -2
  3. data/.github/workflows/test.yml +8 -5
  4. data/.ruby-version +1 -1
  5. data/.simplecov +15 -9
  6. data/Gemfile.lock +186 -184
  7. data/README.md +32 -28
  8. data/app/commands/decidim/participatory_documents/create_suggestion.rb +3 -1
  9. data/app/controllers/concerns/decidim/participatory_documents/needs_pdf_document.rb +8 -1
  10. data/app/controllers/decidim/participatory_documents/admin/documents_controller.rb +5 -3
  11. data/app/controllers/decidim/participatory_documents/admin/suggestions_controller.rb +21 -1
  12. data/app/controllers/decidim/participatory_documents/admin/valuation_assignments_controller.rb +5 -1
  13. data/app/controllers/decidim/participatory_documents/document_suggestions_controller.rb +14 -0
  14. data/app/controllers/decidim/participatory_documents/section_suggestions_controller.rb +4 -0
  15. data/app/forms/decidim/participatory_documents/admin/document_form.rb +2 -1
  16. data/app/jobs/decidim/participatory_documents/export_my_suggestions_job.rb +16 -0
  17. data/app/models/decidim/participatory_documents/suggestion.rb +12 -0
  18. data/app/packs/entrypoints/decidim_participatory_documents.scss +8 -0
  19. data/app/packs/entrypoints/decidim_participatory_documents_editor.js +1 -3
  20. data/app/packs/entrypoints/decidim_participatory_documents_viewer_off.js +6 -0
  21. data/app/packs/images/export_suggestions.svg +1 -0
  22. data/app/packs/src/decidim/participatory_documents/pdf/pdf_modal_manager.js +4 -1
  23. data/app/packs/src/decidim/participatory_documents/pdf/suggestion_form.js +1 -1
  24. data/app/packs/src/decidim/participatory_documents/pdf.js +46 -0
  25. data/app/packs/src/decidim/participatory_documents/pdf_off.js +39 -0
  26. data/app/packs/stylesheets/decidim/participatory_documents/pdf/admin_modals.scss +9 -5
  27. data/app/packs/stylesheets/decidim/participatory_documents/pdf/admin_tweaks.scss +0 -21
  28. data/app/packs/stylesheets/decidim/participatory_documents/{decidim_admin_classes.scss → pdf/decidim_admin_styles.scss} +0 -3
  29. data/app/packs/stylesheets/decidim/participatory_documents/pdf/modals.scss +49 -0
  30. data/app/packs/stylesheets/decidim/participatory_documents/pdf/tweaks.scss +76 -7
  31. data/app/permissions/decidim/participatory_documents/admin/permissions.rb +30 -15
  32. data/app/permissions/decidim/participatory_documents/permissions.rb +2 -0
  33. data/app/serializers/decidim/participatory_documents/my_suggestion_serializer.rb +22 -0
  34. data/app/serializers/decidim/participatory_documents/suggestion_serializer.rb +1 -1
  35. data/app/uploaders/decidim/participatory_documents/pdf_document_uploader.rb +4 -0
  36. data/app/views/decidim/participatory_documents/admin/documents/_editor_modal.html.erb +1 -1
  37. data/app/views/decidim/participatory_documents/admin/documents/pdf_viewer.html.erb +1 -1
  38. data/app/views/decidim/participatory_documents/admin/sections/_form.html.erb +0 -3
  39. data/app/views/decidim/participatory_documents/admin/suggestions/_suggestion.html.erb +1 -1
  40. data/app/views/decidim/participatory_documents/documents/_export_modal.html.erb +25 -0
  41. data/app/views/decidim/participatory_documents/documents/_pdfjs_base.html.erb +5 -2
  42. data/app/views/decidim/participatory_documents/documents/pdf_viewer.html.erb +40 -33
  43. data/config/assets.rb +1 -0
  44. data/config/locales/ca.yml +259 -0
  45. data/config/locales/de.yml +259 -0
  46. data/config/locales/en.yml +13 -0
  47. data/config/locales/es.yml +259 -0
  48. data/lib/decidim/participatory_documents/component.rb +6 -4
  49. data/lib/decidim/participatory_documents/engine.rb +5 -1
  50. data/lib/decidim/participatory_documents/version.rb +2 -2
  51. data/lib/decidim/participatory_documents.rb +5 -3
  52. metadata +12 -3
@@ -19,6 +19,31 @@
19
19
  margin: 12px;
20
20
  }
21
21
 
22
+ #toolbarViewerMiddle {
23
+ padding-left: 0;
24
+ padding-right: 0;
25
+
26
+ @media(min-width: 940px) {
27
+ transform: translateX(-75%);
28
+ }
29
+
30
+ @media(max-width: 680px) {
31
+ padding-left: 1em;
32
+ }
33
+ }
34
+
35
+ #toolbarViewerLeft {
36
+ @media(max-width: 680px) {
37
+ display: none;
38
+ }
39
+ }
40
+
41
+ #toolbarViewerRight {
42
+ @media(max-width: 380px) {
43
+ display: none;
44
+ }
45
+ }
46
+
22
47
  .secondaryToolbar,
23
48
  .findbar {
24
49
  top: 53px;
@@ -49,6 +74,18 @@
49
74
  margin-top: 8px;
50
75
  }
51
76
 
77
+ .hiddenSmallView {
78
+ @media(max-width: 800px) {
79
+ display: none;
80
+ }
81
+ }
82
+
83
+ .toolbarButtonSpacer {
84
+ @media(max-width: 980px) {
85
+ width: 0;
86
+ }
87
+ }
88
+
52
89
  .toolbarField {
53
90
  margin-top: 9px;
54
91
  }
@@ -64,13 +101,19 @@
64
101
  background: #fff;
65
102
  border: 1px solid #000;
66
103
  }
104
+
105
+ @media(max-width: 940px) {
106
+ display: none;
107
+ }
108
+
109
+ @media(min-width: 941px) {
110
+ display: block;
111
+ }
67
112
  }
68
113
 
69
114
  .decidimButton {
70
- display: inline-block;
71
- vertical-align: middle;
72
- margin: 0 0 1rem;
73
- padding: .85em 1em;
115
+ margin: 0 0 0 1rem;
116
+ padding: .65em;
74
117
  border-radius: 4px;
75
118
  transition: background-color .25s ease-out, color .25s ease-out;
76
119
  font-family: inherit;
@@ -83,16 +126,42 @@
83
126
  border: transparent solid 1px;
84
127
  color: var(--box-text-color);
85
128
  text-shadow: 0 0 0 #fff;
129
+
130
+ &:first-child {
131
+ margin-left: 0;
132
+ }
133
+
134
+ &.muted {
135
+ background: #ddd;
136
+ color: #333;
137
+ }
138
+
139
+ @media(max-width: 900px) {
140
+ margin-top: 12px;
141
+ }
86
142
  }
87
143
 
88
144
  #globalSuggestionTrigger {
89
- min-width: 160px;
90
-
91
145
  &::before {
92
146
  display: none;
93
147
  }
94
148
  }
95
149
 
150
+ #exportSuggestionsTrigger {
151
+ background-image: url(../images/export_suggestions.svg);
152
+ background-repeat: no-repeat;
153
+ background-size: 20px 20px;
154
+ background-position: 8px 9px;
155
+ padding-left: 34px;
156
+
157
+ @media(max-width: 900px) {
158
+ width: 28px;
159
+ height: 36px;
160
+ overflow: hidden;
161
+ font-size: 0;
162
+ }
163
+ }
164
+
96
165
  #fullscreenButton {
97
166
  background-image: url(../images/fullscreen.svg);
98
167
  background-repeat: no-repeat;
@@ -115,7 +184,7 @@
115
184
  background-image: url(../images/fullscreen_exit.svg);
116
185
  }
117
186
 
118
- @media(max-width: 560px) {
187
+ @media(max-width: 940px) {
119
188
  width: 28px;
120
189
  height: 28px;
121
190
  margin-right: 0;
@@ -7,38 +7,53 @@ module Decidim
7
7
  def permissions
8
8
  return permission_action if permission_action.scope != :admin
9
9
 
10
- # Valuators can only perform these actions
11
- if user_is_valuator?
12
- if valuator_assigned_to_suggestion?
13
- can_create_suggestion_note?
14
- can_create_suggestion_answer?
15
- end
16
- valuator_can_unassign_valuator_from_suggestions?
17
-
18
- return permission_action
10
+ handle_valuator_permissions if user_is_valuator?
11
+ handle_general_permissions unless user_is_valuator?
12
+
13
+ permission_action
14
+ end
15
+
16
+ private
17
+
18
+ def handle_valuator_permissions
19
+ if valuator_assigned_to_suggestion?
20
+ can_create_suggestion_note?
21
+ can_create_suggestion_answer?
22
+ allow! if action_is_show_on_suggestion?
23
+ elsif action_is_show_on_suggestion?
24
+ disallow!
19
25
  end
26
+ valuator_can_unassign_valuator_from_suggestions?
27
+ end
20
28
 
29
+ def handle_general_permissions
30
+ allow! if action_is_show_on_suggestion?
21
31
  if create_permission_action?
22
32
  can_create_suggestion_note?
23
33
  can_create_suggestion_answer?
24
34
  end
25
-
26
35
  edit_suggestion_note?
36
+ can_edit_document_or_sections? if action_is_update_on_document?
37
+ allow_default_admin_actions
38
+ end
27
39
 
28
- can_edit_document_or_sections? if permission_action.subject == :participatory_document && permission_action.action == :update
40
+ def action_is_show_on_suggestion?
41
+ permission_action.subject == :suggestion && permission_action.action == :show
42
+ end
43
+
44
+ def action_is_update_on_document?
45
+ permission_action.subject == :participatory_document && permission_action.action == :update
46
+ end
29
47
 
48
+ def allow_default_admin_actions
30
49
  allow! if permission_action.subject == :suggestion_note && permission_action.action == :create
31
50
  allow! if permission_action.subject == :suggestion_answer
32
51
  allow! if permission_action.subject == :document_section
33
52
  allow! if permission_action.subject == :document_annotations
34
53
  allow! if permission_action.subject == :participatory_document && permission_action.action == :create
35
54
  allow! if permission_action.subject == :suggestions
36
-
37
- permission_action
38
55
  end
39
56
 
40
- private
41
-
42
57
  def suggestion
43
58
  @suggestion ||= context.fetch(:suggestion, nil)
44
59
  end
@@ -6,6 +6,8 @@ module Decidim
6
6
  def permissions
7
7
  return permission_action unless user
8
8
 
9
+ allow! if permission_action.subject == :suggestion && permission_action.action == :create
10
+
9
11
  # Delegate the admin permission checks to the admin permissions class
10
12
  return Decidim::ParticipatoryDocuments::Admin::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :admin
11
13
 
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ParticipatoryDocuments
5
+ # This class serializes a Suggestion so can be exported to CSV, JSON or other
6
+ # formats.
7
+ class MySuggestionSerializer < SuggestionSerializer
8
+ # Public: Exports a hash with the serialized data for this suggestion.
9
+ def serialize
10
+ {
11
+ id: suggestion.id,
12
+ body: suggestion_body(suggestion),
13
+ author: suggestion.try(:normalized_author).try(:name),
14
+ state: humanize_suggestion_state(suggestion.state),
15
+ answer: answer_text(suggestion),
16
+ section: section(suggestion),
17
+ submitted_on: submitted_on(suggestion)
18
+ }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -24,7 +24,7 @@ module Decidim
24
24
  state: humanize_suggestion_state(suggestion.state),
25
25
  answer: answer_text(suggestion),
26
26
  section: section(suggestion),
27
- valuators: suggestion.valuation_assignments.count,
27
+ valuators: suggestion.valuation_assignments.map(&:valuator).map(&:name).join(", "),
28
28
  submitted_on: submitted_on(suggestion)
29
29
  }
30
30
  end
@@ -6,6 +6,10 @@ module Decidim
6
6
  def content_type_allowlist
7
7
  %w(application/pdf)
8
8
  end
9
+
10
+ def extension_allowlist
11
+ %w(pdf)
12
+ end
9
13
  end
10
14
  end
11
15
  end
@@ -1,3 +1,3 @@
1
1
  <div id="decidim">
2
- <%= render partial: "decidim/participatory_documents/admin/sections/form" %>
2
+ <%= render "decidim/participatory_documents/admin/sections/form" %>
3
3
  </div>
@@ -21,7 +21,7 @@
21
21
  </head>
22
22
 
23
23
  <body tabindex="1">
24
- <%= render "decidim/participatory_documents/documents/pdfjs_base", displaySave: true, globalSuggestions: false %>
24
+ <%= render "decidim/participatory_documents/documents/pdfjs_base", display_save: true %>
25
25
 
26
26
  <%= render "decidim/participatory_documents/admin/documents/editor_modal" %>
27
27
 
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  <div class="reveal-overlay">
4
2
  <div class="reveal" id="editor-modal">
5
3
  <%= decidim_form_for([document, @form], html: { class: "form" }) do |form| %>
@@ -21,4 +19,3 @@
21
19
  <span aria-hidden="true">&times;</span>
22
20
  </button>
23
21
  </div>
24
- </div>
@@ -4,7 +4,7 @@
4
4
  </td>
5
5
  <td><%= suggestion.id %></td>
6
6
  <td>
7
- <%= truncate(suggestion_content(suggestion)[:text], length: 50) %>
7
+ <%= link_to truncate(suggestion_content(suggestion)[:text], length: 50), document_suggestion_path(document, suggestion) %>
8
8
  <% if suggestion_content(suggestion)[:file_link] %>
9
9
  <%= suggestion_content(suggestion)[:file_link] %>
10
10
  <% end %>
@@ -0,0 +1,25 @@
1
+ <div id="decidim">
2
+ <div class="reveal-overlay">
3
+ <div class="reveal" id="export-modal" data-reveal>
4
+ <div class="reveal__header">
5
+ <h2 class="reveal__title"><%= t(".title") %></h2>
6
+ <button class="close-button" data-close aria-label="close"
7
+ type="button">
8
+ <span aria-hidden="true">&times;</span>
9
+ </button>
10
+ </div>
11
+
12
+ <div class="content">
13
+ <div class="row">
14
+ <p><%= t(".description_html", email: current_user&.email, count: all_suggestions&.count) %></p>
15
+ </div>
16
+
17
+ <div class="row">
18
+ <div class="column flex-center">
19
+ <button type="button" class="export-button button primary expanded" data-url="<%= export_document_suggestions_path(document) %>"><%= t(".send") %></button>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </div>
@@ -305,15 +305,18 @@ See https://github.com/adobe-type-tools/cmap-resources
305
305
  </button>
306
306
  </div>
307
307
  <div id="toolbarViewerMiddle">
308
- <% if defined?(displaySave) && displaySave == true %>
308
+ <% if defined?(display_save) && display_save == true %>
309
309
  <button id="DecidimPDSaveButton" class="decidimButton" title="<%= t "save_changes", scope: "decidim.participatory_documents.document" %>">
310
310
  <%= t "save_changes", scope: "decidim.participatory_documents.document" %>
311
311
  </button>
312
312
  <% end %>
313
- <% if defined?(globalSuggestions) && globalSuggestions == true %>
313
+ <% if defined?(display_suggestions) && display_suggestions == true %>
314
314
  <button id="globalSuggestionTrigger" class="decidimButton" title="<%= t "global_suggestions", scope: "decidim.participatory_documents.document" %>">
315
315
  <%= t "global_suggestions", scope: "decidim.participatory_documents.document" %>
316
316
  </button>
317
+ <button id="exportSuggestionsTrigger" class="decidimButton muted" title="<%= t "export_my_suggestions", scope: "decidim.participatory_documents.document" %>">
318
+ <%= t "export_my_suggestions", scope: "decidim.participatory_documents.document" %>
319
+ </button>
317
320
  <% end %>
318
321
  </div>
319
322
  </div>
@@ -13,51 +13,58 @@
13
13
  <!-- This snippet is used in production (included from viewer.html) -->
14
14
  <link rel="resource" type="application/l10n" href="/pdfjs/web/locale/locale.properties">
15
15
  <script src="/pdfjs/web/viewer.js"></script>
16
- <%= javascript_pack_tag "decidim_participatory_documents_viewer" %>
17
16
  <%= stylesheet_pack_tag "decidim_participatory_documents_viewer" %>
17
+ <%= javascript_pack_tag "decidim_participatory_documents_viewer" if current_user %>
18
+ <%= javascript_pack_tag "decidim_participatory_documents_viewer_off" unless current_user %>
18
19
  <%= organization_colors %>
19
20
  <%= pdf_custom_style %>
20
21
  <%= csrf_meta_tags %>
21
22
  </head>
22
23
 
23
24
  <body tabindex="1">
24
- <%= render "decidim/participatory_documents/documents/pdfjs_base", displaySave: false, globalSuggestions: true %>
25
+ <%= render "decidim/participatory_documents/documents/pdfjs_base", display_suggestions: true %>
25
26
 
26
27
  <div id="notifications"></div>
27
28
 
28
29
  <div id="participation-modal"></div>
29
30
 
30
- <script>
31
-
32
- var I18n = <%= I18n.t("decidim.participatory_documents.ui_messages").to_json.html_safe %>;
33
- var DocumentPath = '<%= document_path(document).split("?")[0] %>';
34
- var CurrentBoxes = <%= document.annotations.collect(&:serialize).to_json.html_safe %>;
35
- var pages = {};
36
-
37
- PDFViewerApplication.initializedPromise.then(function() {
38
- var options = {
39
- i18n: I18n,
40
- documentPath: DocumentPath,
41
- globalSuggestionsButton: document.getElementById("globalSuggestionTrigger"),
42
- participationLayout: document.getElementById("participation-modal")
43
- };
44
-
45
- window.InitDocumentManagers(options);
46
-
47
- window.showInfo(I18n.startSuggesting, {delay: 5000});
48
-
49
- PDFViewerApplication.eventBus.on('annotationeditorlayerrendered', function (doc) {
50
- var page = PDFViewerApplication.pdfViewer._pages[doc.pageNumber - 1];
51
- var annotationsLayer = page.annotationEditorLayer && page.annotationEditorLayer.div;
52
- var savedPage = pages[doc.pageNumber];
53
- // Let's render the object only if not already existing.
54
- // Note that the PDF library might redraw the annotation layer, if this happens we need to re-render the areas.
55
- if(annotationsLayer && (!savedPage || savedPage.boxEditor.div !== annotationsLayer)) {
56
- page.boxEditor = window.InitPolygonViewer(annotationsLayer, CurrentBoxes.filter(box => box.page_number === doc.pageNumber), options);
57
- pages[doc.pageNumber] = page;
58
- }
31
+ <%= render "export_modal" %>
32
+
33
+ <script>
34
+
35
+ var I18n = <%= I18n.t("decidim.participatory_documents.ui_messages").to_json.html_safe %>;
36
+ var DocumentPath = '<%= document_path(document).split("?")[0] %>';
37
+ var CurrentBoxes = <%= document.annotations.collect(&:serialize).to_json.html_safe %>;
38
+ var pages = {};
39
+
40
+ PDFViewerApplication.initializedPromise.then(function() {
41
+ var options = {
42
+ i18n: I18n,
43
+ documentPath: DocumentPath,
44
+ globalSuggestionsButton: document.getElementById("globalSuggestionTrigger"),
45
+ participationLayout: document.getElementById("participation-modal"),
46
+ exportButton: document.getElementById("exportSuggestionsTrigger"),
47
+ exportModal: document.getElementById("export-modal")
48
+ };
49
+
50
+ window.InitDocumentManagers(options);
51
+
52
+ window.showInfo(I18n.startSuggesting, {delay: 5000});
53
+
54
+ PDFViewerApplication.eventBus.on('annotationeditorlayerrendered', function (doc) {
55
+ var page = PDFViewerApplication.pdfViewer._pages[doc.pageNumber - 1];
56
+ var annotationsLayer = page.annotationEditorLayer && page.annotationEditorLayer.div;
57
+ var savedPage = pages[doc.pageNumber];
58
+ // Let's render the object only if not already existing.
59
+ // Note that the PDF library might redraw the annotation layer, if this happens we need to re-render the areas.
60
+ if(annotationsLayer && (!savedPage || savedPage.boxEditor.div !== annotationsLayer)) {
61
+ page.boxEditor = window.InitPolygonViewer(annotationsLayer, CurrentBoxes.filter(box => box.page_number === doc.pageNumber), options);
62
+ pages[doc.pageNumber] = page;
63
+ }
64
+ // make all links with target blank to prevent open them inside the iframe
65
+ page.div.querySelectorAll("a[href]").forEach(anchor => anchor.target = "_blank")
66
+ });
59
67
  });
60
- });
61
- </script>
68
+ </script>
62
69
  </body>
63
70
  </html>
data/config/assets.rb CHANGED
@@ -6,6 +6,7 @@ Decidim::Webpacker.register_path("#{base_path}/app/packs")
6
6
  Decidim::Webpacker.register_entrypoints(
7
7
  decidim_participatory_documents_admin: "#{base_path}/app/packs/entrypoints/decidim_participatory_documents_admin.js",
8
8
  decidim_participatory_documents_viewer: "#{base_path}/app/packs/entrypoints/decidim_participatory_documents_viewer.js",
9
+ decidim_participatory_documents_viewer_off: "#{base_path}/app/packs/entrypoints/decidim_participatory_documents_viewer_off.js",
9
10
  decidim_participatory_documents_editor: "#{base_path}/app/packs/entrypoints/decidim_participatory_documents_editor.js",
10
11
  decidim_participatory_documents: "#{base_path}/app/packs/entrypoints/decidim_participatory_documents.js"
11
12
  )