decidim-accountability 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/decidim/accountability/admin/accountability_admin.js.es6 +6 -42
  3. data/app/assets/stylesheets/decidim/accountability/accountability/_categories.scss +8 -1
  4. data/app/assets/stylesheets/decidim/accountability/accountability/_lines_breadcrumb.scss +8 -0
  5. data/app/assets/stylesheets/decidim/accountability/accountability/_results.scss +0 -3
  6. data/app/cells/decidim/accountability/highlighted_results_for_component/show.erb +3 -3
  7. data/app/cells/decidim/accountability/highlighted_results_for_component_cell.rb +1 -1
  8. data/app/commands/decidim/accountability/admin/create_imported_result.rb +106 -0
  9. data/app/commands/decidim/accountability/admin/update_imported_result.rb +110 -0
  10. data/app/controllers/decidim/accountability/admin/import_results_controller.rb +34 -0
  11. data/app/controllers/decidim/accountability/admin/results_controller.rb +2 -20
  12. data/app/controllers/decidim/accountability/versions_controller.rb +7 -8
  13. data/app/jobs/application_job.rb +4 -0
  14. data/app/jobs/decidim/accountability/admin/import_results_csv_job.rb +19 -0
  15. data/app/mailers/decidim/accountability/import_mailer.rb +26 -0
  16. data/app/queries/decidim/accountability/metrics/results_metric_manage.rb +2 -8
  17. data/app/services/decidim/accountability/results_csv_importer.rb +139 -0
  18. data/app/views/decidim/accountability/admin/import_results/new.html.erb +22 -0
  19. data/app/views/decidim/accountability/admin/results/_form.html.erb +1 -5
  20. data/app/views/decidim/accountability/admin/results/index.html.erb +1 -0
  21. data/app/views/decidim/accountability/admin/results/proposals_picker.html.erb +1 -0
  22. data/app/views/decidim/accountability/import_mailer/import.html.erb +25 -0
  23. data/app/views/decidim/accountability/results/_linked_results.html.erb +1 -1
  24. data/app/views/decidim/accountability/results/_results_leaf.html.erb +5 -5
  25. data/app/views/decidim/accountability/results/_results_parent.html.erb +2 -2
  26. data/app/views/decidim/accountability/results/_scope_filters.html.erb +16 -3
  27. data/app/views/decidim/accountability/results/_search.html.erb +3 -3
  28. data/app/views/decidim/accountability/results/_stats_box.html.erb +11 -4
  29. data/app/views/decidim/accountability/results/_timeline.html.erb +2 -2
  30. data/app/views/decidim/accountability/versions/index.html.erb +7 -25
  31. data/app/views/decidim/accountability/versions/show.html.erb +9 -31
  32. data/app/views/decidim/participatory_spaces/_result.html.erb +3 -3
  33. data/config/locales/ar.yml +1 -24
  34. data/config/locales/bg-BG.yml +220 -0
  35. data/config/locales/ca.yml +19 -22
  36. data/config/locales/cs.yml +44 -47
  37. data/config/locales/da-DK.yml +1 -0
  38. data/config/locales/de.yml +19 -22
  39. data/config/locales/el.yml +222 -0
  40. data/config/locales/en.yml +19 -22
  41. data/config/locales/es-MX.yml +19 -22
  42. data/config/locales/es-PY.yml +19 -22
  43. data/config/locales/es.yml +19 -22
  44. data/config/locales/et-EE.yml +1 -0
  45. data/config/locales/eu.yml +1 -24
  46. data/config/locales/fi-plain.yml +19 -22
  47. data/config/locales/fi.yml +32 -35
  48. data/config/locales/fr-CA.yml +222 -0
  49. data/config/locales/fr.yml +18 -22
  50. data/config/locales/ga-IE.yml +1 -0
  51. data/config/locales/gl.yml +1 -24
  52. data/config/locales/hr-HR.yml +1 -0
  53. data/config/locales/hu.yml +18 -22
  54. data/config/locales/id-ID.yml +1 -24
  55. data/config/locales/is-IS.yml +1 -21
  56. data/config/locales/it.yml +37 -40
  57. data/config/locales/ja-JP.yml +221 -0
  58. data/config/locales/lt-LT.yml +1 -0
  59. data/config/locales/lv-LV.yml +218 -0
  60. data/config/locales/mt-MT.yml +1 -0
  61. data/config/locales/nl.yml +18 -22
  62. data/config/locales/no.yml +25 -28
  63. data/config/locales/pl.yml +46 -49
  64. data/config/locales/pt-BR.yml +2 -25
  65. data/config/locales/pt.yml +59 -62
  66. data/config/locales/ro-RO.yml +223 -0
  67. data/config/locales/ru.yml +1 -24
  68. data/config/locales/sk-SK.yml +224 -0
  69. data/config/locales/sk.yml +207 -0
  70. data/config/locales/sl.yml +129 -0
  71. data/config/locales/sr-CS.yml +205 -0
  72. data/config/locales/sv.yml +21 -24
  73. data/config/locales/tr-TR.yml +1 -24
  74. data/config/locales/uk.yml +1 -21
  75. data/db/migrate/20200320105903_index_foreign_keys_in_decidim_accountability_results.rb +7 -0
  76. data/lib/decidim/accountability/admin_engine.rb +4 -3
  77. data/lib/decidim/accountability/version.rb +1 -1
  78. metadata +54 -24
  79. data/app/views/decidim/accountability/admin/results/_proposals.html.erb +0 -12
  80. data/app/views/decidim/accountability/versions/_version.html.erb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23e9e9da0373bafcee830512c9e911e0176767832d826719ccae2a52b441f2f2
4
- data.tar.gz: 6cd880e63873b91f6a0dbdd79e066bbd9e04e9a235aacc895a2cb0bc9f0a483c
3
+ metadata.gz: 5aa01ab3845ef9d564662b490d22e56aaf88d857de85adfb06f3a58d8bd827a8
4
+ data.tar.gz: 44725f2f514ebc97eb45725e26d067406b24b5d6e71affd3aa5f6978ba92260b
5
5
  SHA512:
6
- metadata.gz: 04a259f346030214c5963d31cfd3240ecc58cd6c4d0f222b52b4b20a215475043faa6a430370e285e48aba86d8582fbcf7c7b1246af885fb88e76782470a26c7
7
- data.tar.gz: cd0f61547ca931bf560d0f1a5d9393c1c5548c8d220a86bad841e1ff64a6d07f0a52987919d137af4236dd40a815a723e2ed39dd84d8a99fff0854497be36178
6
+ metadata.gz: 591653254ddaa34353fd6f010b51bfa122675f9abae01e027ee0d3dfedae70837e4f230c01549e68d181025d71d36f4d5458030f12857d556b63a3363d684513
7
+ data.tar.gz: 66c9eb1f5bbf2b68d4a4ae82d5f49161b83e1cef65a435d4e923d20c7e0dba61060dbae6031b484e6ed1915e9baf834153af075bc259b1ef25ef6cbe3e88722d
@@ -1,47 +1,11 @@
1
1
  // = require_self
2
2
 
3
- $("#result_decidim_accountability_status_id").change(function () {
4
- /* eslint-disable no-invalid-this */
5
- const progress = $(this).find(":selected").data("progress")
6
- if (progress || progress === 0) {
7
- $("#result_progress").val(progress);
8
- }
9
- });
10
-
11
3
  $(function() {
12
- $(document).on("open.zf.reveal", "#data_picker-modal", function () {
13
- let xhr = null;
14
-
15
- $("#data_picker-autocomplete").autoComplete({
16
- minChars: 2,
17
- source: function(term, response) {
18
- try {
19
- xhr.abort();
20
- } catch (exception) { xhr = null}
21
-
22
- let url = $("#proposal-picker-choose").attr("href")
23
- xhr = $.getJSON(
24
- url,
25
- { term: term },
26
- function(data) { response(data); }
27
- );
28
- },
29
- renderItem: function (item, search) {
30
- let sanitizedSearch = search.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
31
- let re = new RegExp(`(${sanitizedSearch.split(" ").join("|")})`, "gi");
32
- let title = item[0]
33
- let modelId = item[1]
34
- let val = `#${modelId}- ${title}`;
35
- return `<div class="autocomplete-suggestion" data-model-id="${modelId}" data-val ="${title}">${val.replace(re, "<b>$1</b>")}</div>`;
36
- },
37
- onSelect: function(event, term, item) {
38
- let choose = $("#proposal-picker-choose")
39
- let modelId = item.data("modelId")
40
- let val = `#${modelId}- ${item.data("val")}`;
41
- choose.data("picker-value", modelId)
42
- choose.data("picker-text", val)
43
- choose.data("picker-choose", "")
44
- }
45
- })
4
+ $("#result_decidim_accountability_status_id").change(function () {
5
+ /* eslint-disable no-invalid-this */
6
+ const progress = $(this).find(":selected").data("progress")
7
+ if (progress || progress === 0) {
8
+ $("#result_progress").val(progress);
9
+ }
46
10
  });
47
11
  })
@@ -1,5 +1,13 @@
1
1
  .accountability{
2
2
  .categories{
3
+ a:hover{
4
+ text-decoration: underline;
5
+ }
6
+
7
+ a:focus{
8
+ outline: $anchor-outline-focus;
9
+ }
10
+
3
11
  .categories--header{
4
12
  border-bottom: 1px solid $medium-gray;
5
13
  padding-bottom: 1rem;
@@ -10,7 +18,6 @@
10
18
  }
11
19
 
12
20
  span{
13
- text-transform: uppercase;
14
21
  font-size: 1.15rem;
15
22
  color: lighten($dark-gray, 50);
16
23
  }
@@ -13,6 +13,14 @@
13
13
  a{
14
14
  font-weight: 600;
15
15
  color: $dark-gray;
16
+
17
+ &:hover{
18
+ text-decoration: underline;
19
+ }
20
+
21
+ &:focus{
22
+ outline: 2px solid $secondary;
23
+ }
16
24
  }
17
25
 
18
26
  ul{
@@ -10,7 +10,6 @@
10
10
 
11
11
  p{
12
12
  font-weight: 600;
13
- text-transform: uppercase;
14
13
  margin-bottom: .25rem;
15
14
  display: inline-block;
16
15
  margin-right: .5rem;
@@ -44,7 +43,6 @@
44
43
  .scope-filters{
45
44
  div{
46
45
  color: lighten($dark-gray, 50);
47
- text-transform: uppercase;
48
46
  margin-bottom: .15rem;
49
47
  }
50
48
 
@@ -107,7 +105,6 @@
107
105
  margin-bottom: .5rem;
108
106
 
109
107
  .progress-text{
110
- text-transform: uppercase;
111
108
  color: lighten($dark-gray, 30);
112
109
  }
113
110
 
@@ -1,7 +1,7 @@
1
1
  <section class="section row collapse highlighted_results">
2
- <h4 class="section-heading">
2
+ <h3 class="section-heading">
3
3
  <%= translated_attribute(model.name) %> <a href="<%= main_component_path(model) %>" class="text-small"><%= t("decidim.participatory_spaces.highlighted_results.see_all", count: results_count) %></a>
4
- </h4>
4
+ </h3>
5
5
 
6
6
  <div class="row">
7
7
  <div class="columns accountability">
@@ -16,6 +16,6 @@
16
16
  <%= link_to(
17
17
  t("decidim.participatory_spaces.highlighted_results.see_all", count: results_count),
18
18
  main_component_path(model),
19
- class: "button button--sc light secondary button--right"
19
+ class: "button button--sc hollow button--right"
20
20
  ) %>
21
21
  </section>
@@ -25,7 +25,7 @@ module Decidim
25
25
  end
26
26
 
27
27
  def results_to_render
28
- @results_to_render ||= results.limit(4)
28
+ @results_to_render ||= results.includes(:component, :status).limit(4)
29
29
  end
30
30
 
31
31
  def results_count
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ module Admin
6
+ # This command is executed when the user creates a Result from the admin
7
+ # panel.
8
+ class CreateImportedResult < Rectify::Command
9
+ def initialize(form, parent_id = nil)
10
+ @form = form
11
+ @parent_id = parent_id
12
+ end
13
+
14
+ # Creates the result if valid.
15
+ #
16
+ # Broadcasts :ok if successful, :invalid otherwise.
17
+ def call
18
+ return broadcast(:invalid) if @form.invalid?
19
+
20
+ transaction do
21
+ create_result
22
+ link_meetings
23
+ link_proposals
24
+ link_projects
25
+ notify_proposal_followers
26
+ end
27
+
28
+ broadcast(:ok)
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :result
34
+
35
+ def create_result
36
+ params = {
37
+ component: @form.current_component,
38
+ scope: @form.scope,
39
+ category: @form.category,
40
+ parent_id: @parent_id,
41
+ title: @form.title,
42
+ description: @form.description,
43
+ start_date: @form.start_date,
44
+ end_date: @form.end_date,
45
+ progress: @form.progress,
46
+ decidim_accountability_status_id: @form.decidim_accountability_status_id,
47
+ external_id: @form.external_id.presence,
48
+ weight: @form.weight
49
+ }
50
+
51
+ @result = Decidim.traceability.create!(
52
+ Result,
53
+ @form.current_user,
54
+ params,
55
+ visibility: "all"
56
+ )
57
+ end
58
+
59
+ def proposals
60
+ @proposals ||= result.sibling_scope(:proposals).where(id: @form.proposal_ids)
61
+ end
62
+
63
+ def projects
64
+ @projects ||= result.sibling_scope(:projects).where(id: @form.project_ids)
65
+ end
66
+
67
+ def meeting_ids
68
+ @meeting_ids ||= proposals.flat_map do |proposal|
69
+ proposal.linked_resources(:meetings, "proposals_from_meeting").pluck(:id)
70
+ end.uniq
71
+ end
72
+
73
+ def meetings
74
+ @meetings ||= result.sibling_scope(:meetings).where(id: meeting_ids)
75
+ end
76
+
77
+ def link_proposals
78
+ result.link_resources(proposals, "included_proposals")
79
+ end
80
+
81
+ def link_projects
82
+ result.link_resources(projects, "included_projects")
83
+ end
84
+
85
+ def link_meetings
86
+ result.link_resources(meetings, "meetings_through_proposals")
87
+ end
88
+
89
+ def notify_proposal_followers
90
+ proposals.each do |proposal|
91
+ Decidim::EventsManager.publish(
92
+ event: "decidim.events.accountability.proposal_linked",
93
+ event_class: Decidim::Accountability::ProposalLinkedEvent,
94
+ resource: result,
95
+ affected_users: proposal.notifiable_identities,
96
+ followers: proposal.followers - proposal.notifiable_identities,
97
+ extra: {
98
+ proposal_id: proposal.id
99
+ }
100
+ )
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ module Admin
6
+ # This command is executed when the user changes a Result from the admin
7
+ # panel.
8
+ class UpdateImportedResult < Rectify::Command
9
+ # Initializes an UpdateResult Command.
10
+ #
11
+ # form - The form from which to get the data.
12
+ # result - The current instance of the result to be updated.
13
+ def initialize(form, result, parent_id = nil)
14
+ @form = form
15
+ @result = result
16
+ @parent_id = parent_id
17
+ end
18
+
19
+ # Updates the result if valid.
20
+ #
21
+ # Broadcasts :ok if successful, :invalid otherwise.
22
+ def call
23
+ return broadcast(:invalid) if form.invalid?
24
+
25
+ transaction do
26
+ update_result
27
+ link_proposals
28
+ link_meetings
29
+ link_projects
30
+ send_notifications if should_notify_followers?
31
+ end
32
+
33
+ broadcast(:ok)
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :result, :form
39
+
40
+ def update_result
41
+ Decidim.traceability.update!(
42
+ result,
43
+ form.current_user,
44
+ scope: @form.scope,
45
+ category: @form.category,
46
+ parent_id: @parent_id,
47
+ title: @form.title,
48
+ description: @form.description,
49
+ start_date: @form.start_date,
50
+ end_date: @form.end_date,
51
+ progress: @form.progress,
52
+ decidim_accountability_status_id: @form.decidim_accountability_status_id,
53
+ external_id: @form.external_id.presence,
54
+ weight: @form.weight
55
+ )
56
+ end
57
+
58
+ def proposals
59
+ @proposals ||= result.sibling_scope(:proposals).where(id: form.proposal_ids)
60
+ end
61
+
62
+ def projects
63
+ @projects ||= result.sibling_scope(:projects).where(id: form.project_ids)
64
+ end
65
+
66
+ def meeting_ids
67
+ @meeting_ids ||= proposals.flat_map do |proposal|
68
+ proposal.linked_resources(:meetings, "proposals_from_meeting").pluck(:id)
69
+ end.uniq
70
+ end
71
+
72
+ def meetings
73
+ @meetings ||= result.sibling_scope(:meetings).where(id: meeting_ids)
74
+ end
75
+
76
+ def link_proposals
77
+ result.link_resources(proposals, "included_proposals")
78
+ end
79
+
80
+ def link_projects
81
+ result.link_resources(projects, "included_projects")
82
+ end
83
+
84
+ def link_meetings
85
+ result.link_resources(meetings, "meetings_through_proposals")
86
+ end
87
+
88
+ def send_notifications
89
+ result.linked_resources(:proposals, "included_proposals").each do |proposal|
90
+ Decidim::EventsManager.publish(
91
+ event: "decidim.events.accountability.result_progress_updated",
92
+ event_class: Decidim::Accountability::ResultProgressUpdatedEvent,
93
+ resource: result,
94
+ affected_users: proposal.notifiable_identities,
95
+ followers: proposal.followers - proposal.notifiable_identities,
96
+ extra: {
97
+ progress: result.progress,
98
+ proposal_id: proposal.id
99
+ }
100
+ )
101
+ end
102
+ end
103
+
104
+ def should_notify_followers?
105
+ result.previous_changes["progress"].present?
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ module Admin
6
+ # This controller allows an admin to import results from a csv file for the Accountability component
7
+ class ImportResultsController < Admin::ApplicationController
8
+ def new
9
+ @errors = []
10
+ end
11
+
12
+ def create
13
+ @csv_file = params[:csv_file]
14
+ redirect_to(new_import_path) && return if @csv_file.blank?
15
+
16
+ Decidim::Accountability::Admin::ImportResultsCSVJob.perform_later(current_user, current_component, @csv_file.read.force_encoding("utf-8").encode("utf-8"))
17
+
18
+ flash[:notice] = I18n.t("imports.create.success", scope: "decidim.accountability.admin")
19
+ redirect_to import_results_path(current_participatory_process, current_component)
20
+ end
21
+
22
+ private
23
+
24
+ def current_component
25
+ @current_component ||= current_participatory_process.components.find(params[:component_id])
26
+ end
27
+
28
+ def current_participatory_process
29
+ @current_participatory_process ||= ParticipatoryProcess.find_by(slug: params[:participatory_process_slug])
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -7,6 +7,8 @@ module Decidim
7
7
  class ResultsController < Admin::ApplicationController
8
8
  include Decidim::ApplicationHelper
9
9
  include Decidim::SanitizeHelper
10
+ include Decidim::Proposals::Admin::Picker
11
+
10
12
  helper_method :results, :parent_result, :parent_results, :statuses, :present
11
13
 
12
14
  def new
@@ -70,26 +72,6 @@ module Decidim
70
72
  end
71
73
  end
72
74
 
73
- def proposals
74
- respond_to do |format|
75
- format.html do
76
- render partial: "proposals"
77
- end
78
- format.json do
79
- query = Decidim.find_resource_manifest(:proposals)
80
- .try(:resource_scope, current_component)&.order(title: :asc)
81
- term = params[:term]
82
- if term&.start_with?("#")
83
- term.delete!("#")
84
- query = query.where("CAST(id AS TEXT) LIKE ?", "#{term}%")
85
- else
86
- query = query.where("title ilike ?", "%#{params[:term]}%")
87
- end
88
- render json: query.all.collect { |p| [decidim_html_escape(present(p).title), p.id] }
89
- end
90
- end
91
- end
92
-
93
75
  private
94
76
 
95
77
  def results
@@ -5,20 +5,19 @@ module Decidim
5
5
  # Exposes result versions so users can see how a result
6
6
  # has been updated through time.
7
7
  class VersionsController < Decidim::Accountability::ApplicationController
8
- helper Decidim::TraceabilityHelper
9
8
  helper Decidim::Accountability::BreadcrumbHelper
10
- helper_method :current_version, :result
9
+ helper_method :result
11
10
 
12
- private
11
+ include Decidim::ResourceVersionsConcern
13
12
 
14
- def result
15
- @result ||= Result.includes(:timeline_entries).where(component: current_component).find(params[:result_id])
13
+ def versioned_resource
14
+ result
16
15
  end
17
16
 
18
- def current_version
19
- return nil if params[:id].to_i < 1
17
+ private
20
18
 
21
- @current_version ||= result.versions[params[:id].to_i - 1]
19
+ def result
20
+ @result ||= Result.includes(:timeline_entries).where(component: current_component).find(params[:result_id])
22
21
  end
23
22
  end
24
23
  end