decidim-accountability 0.9.3 → 0.10.0

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/decidim/accountability/admin/accountability_admin.js.es6 +46 -0
  3. data/app/assets/javascripts/decidim/accountability/version_diff.js.es6 +2 -6
  4. data/app/commands/decidim/accountability/admin/create_result.rb +15 -0
  5. data/app/commands/decidim/accountability/admin/update_status.rb +1 -1
  6. data/app/commands/decidim/accountability/admin/update_timeline_entry.rb +1 -1
  7. data/app/controllers/decidim/accountability/admin/results_controller.rb +20 -0
  8. data/app/events/decidim/accountability/proposal_linked_event.rb +21 -0
  9. data/app/forms/decidim/accountability/admin/result_form.rb +13 -3
  10. data/app/models/decidim/accountability/result.rb +8 -1
  11. data/app/presenters/decidim/accountability/admin_log/result_presenter.rb +44 -0
  12. data/app/views/decidim/accountability/admin/results/_form.html.erb +3 -5
  13. data/app/views/decidim/accountability/admin/results/_proposals.html.erb +12 -0
  14. data/app/views/decidim/accountability/results/_home_categories.html.erb +3 -3
  15. data/app/views/decidim/accountability/results/_home_header.html.erb +1 -1
  16. data/app/views/decidim/accountability/results/_nav_breadcrumb.html.erb +12 -4
  17. data/app/views/decidim/accountability/results/_results_leaf.html.erb +1 -1
  18. data/app/views/decidim/accountability/results/_results_parent.html.erb +1 -1
  19. data/app/views/decidim/accountability/results/_show_leaf.html.erb +1 -1
  20. data/app/views/decidim/accountability/results/_show_parent.html.erb +1 -1
  21. data/app/views/decidim/accountability/results/_stats_box.html.erb +20 -2
  22. data/app/views/decidim/participatory_processes/participatory_process_groups/_highlighted_results.html.erb +12 -0
  23. data/app/views/decidim/participatory_processes/participatory_process_groups/_result.html.erb +32 -0
  24. data/app/views/decidim/participatory_spaces/_highlighted_results.html.erb +14 -0
  25. data/app/views/decidim/participatory_spaces/_result.html.erb +32 -0
  26. data/config/locales/ca.yml +28 -1
  27. data/config/locales/en.yml +28 -1
  28. data/config/locales/es.yml +28 -1
  29. data/config/locales/eu.yml +28 -1
  30. data/config/locales/fi.yml +28 -1
  31. data/config/locales/fr.yml +28 -1
  32. data/config/locales/gl.yml +28 -1
  33. data/config/locales/it.yml +28 -1
  34. data/config/locales/nl.yml +28 -1
  35. data/config/locales/pl.yml +28 -6
  36. data/config/locales/pt-BR.yml +28 -1
  37. data/config/locales/pt.yml +28 -1
  38. data/config/locales/ru.yml +0 -1
  39. data/config/locales/sv.yml +28 -1
  40. data/config/locales/uk.yml +3 -3
  41. data/lib/decidim/accountability/admin_engine.rb +3 -0
  42. data/lib/decidim/accountability/engine.rb +34 -0
  43. data/lib/decidim/accountability/feature.rb +1 -0
  44. data/lib/decidim/accountability/version.rb +1 -1
  45. metadata +29 -22
  46. data/app/assets/javascripts/decidim/accountability/admin/accountability_admin.js +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d108eb3f6f905e0323481b1446dce266b5df791c956d13d24964a820634a5ff
4
- data.tar.gz: d0e60451edae95e5bab4b399170539c1e9b8b5a3a61c7eb4d2e3e1db72bc6e24
3
+ metadata.gz: fb8f10169d6b6d32051013abfb88a1db9a7d2f35907d1a41cab8aa2f13b8c90f
4
+ data.tar.gz: fa3789548be4955c7c6d9347358f09272369f83bd5172d1d93580a3498776cb0
5
5
  SHA512:
6
- metadata.gz: fe17bf52cd5eb3670c1f16c5d19f878f91dc20d4f8454be88dacdf438b4dd7d7f48a4f6dee90931724cb5fe30a1d22d10f92fcba87362873ef22854664f2f11d
7
- data.tar.gz: 6eec909ef42a7bc40196104ab854e19884833578e531829d9e741bede000121badfb031c47df0c516a6b943e275c5251995e9fd4ce3c0edb04f76b33a948854d
6
+ metadata.gz: d548d50caa0918336a747895c6e7c4927100e2055b4b951297566444948224aa4f4afcc2069cd3aeb5b635b4a6bf5303ce17189ae4210f60c8699e42833ee981
7
+ data.tar.gz: e74c3c0d2767da9680e676b63541ca458b40a4bfeca325b0324d184b434163f9ca53ec5e136f1dc849c3f160dce0436cb0706226fbf3054572b10ec22ad3addf
@@ -0,0 +1,46 @@
1
+ // = require_self
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
+ $(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
+ xhr = $.getJSON(
23
+ 'proposals.json',
24
+ { term: term },
25
+ function(data) { response(data); }
26
+ );
27
+ },
28
+ renderItem: function (item, search) {
29
+ let sanitizedSearch = search.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
30
+ let re = new RegExp(`(${sanitizedSearch.split(' ').join('|')})`, "gi");
31
+ let title = item[0]
32
+ let modelId = item[1]
33
+ let val = `#${modelId}- ${title}`;
34
+ return `<div class="autocomplete-suggestion" data-model-id="${modelId}" data-val ="${title}">${val.replace(re, "<b>$1</b>")}</div>`;
35
+ },
36
+ onSelect: function(event, term, item) {
37
+ let choose = $('#proposal-picker-choose')
38
+ let modelId = item.data('modelId')
39
+ let val = `#${modelId}- ${item.data('val')}`;
40
+ choose.data('picker-value', modelId)
41
+ choose.data('picker-text', val)
42
+ choose.data('picker-choose', '')
43
+ }
44
+ })
45
+ });
46
+ })
@@ -4,12 +4,8 @@ $(() => {
4
4
  $(".diff-i18n_html, .diff-i18n").each(function(_index, element) {
5
5
  const diffElement = $(element);
6
6
  const valueElement = diffElement.find(".diff__value");
7
- const oldValue = valueElement.data("old-value").
8
- replace(/</g, "&lt;").
9
- replace(/>/g, "&gt;");
10
- const newValue = valueElement.data("new-value").
11
- replace(/</g, "&lt;").
12
- replace(/>/g, "&gt;");
7
+ const oldValue = valueElement.data("old-value").replace(/</g, "&lt;").replace(/>/g, "&gt;");
8
+ const newValue = valueElement.data("new-value").replace(/</g, "&lt;").replace(/>/g, "&gt;");
13
9
 
14
10
  const diff = JsDiff.diffChars(oldValue, newValue);
15
11
  let outputHTML = "";
@@ -21,6 +21,7 @@ module Decidim
21
21
  link_meetings
22
22
  link_proposals
23
23
  link_projects
24
+ notify_proposal_followers
24
25
  end
25
26
 
26
27
  broadcast(:ok)
@@ -76,6 +77,20 @@ module Decidim
76
77
  def link_meetings
77
78
  result.link_resources(meetings, "meetings_through_proposals")
78
79
  end
80
+
81
+ def notify_proposal_followers
82
+ proposals.includes(:author).each do |proposal|
83
+ Decidim::EventsManager.publish(
84
+ event: "decidim.events.accountability.proposal_linked",
85
+ event_class: Decidim::Accountability::ProposalLinkedEvent,
86
+ resource: result,
87
+ recipient_ids: Array(proposal&.author&.id) + proposal.followers.pluck(:id),
88
+ extra: {
89
+ proposal_id: proposal.id
90
+ }
91
+ )
92
+ end
93
+ end
79
94
  end
80
95
  end
81
96
  end
@@ -33,7 +33,7 @@ module Decidim
33
33
  attr_reader :status, :form
34
34
 
35
35
  def update_status
36
- status.update_attributes!(
36
+ status.update!(
37
37
  key: @form.key,
38
38
  name: @form.name,
39
39
  description: @form.description,
@@ -33,7 +33,7 @@ module Decidim
33
33
  attr_reader :timeline_entry, :form
34
34
 
35
35
  def update_timeline_entry
36
- timeline_entry.update_attributes!(
36
+ timeline_entry.update!(
37
37
  entry_date: @form.entry_date,
38
38
  description: @form.description
39
39
  )
@@ -56,6 +56,26 @@ module Decidim
56
56
  redirect_to results_path(parent_id: result.parent_id)
57
57
  end
58
58
 
59
+ def proposals
60
+ respond_to do |format|
61
+ format.html do
62
+ render partial: "proposals"
63
+ end
64
+ format.json do
65
+ query = Decidim.find_resource_manifest(:proposals)
66
+ .try(:resource_scope, current_feature)&.order(title: :asc)
67
+ term = params[:term]
68
+ if term&.start_with?("#")
69
+ term.delete!("#")
70
+ query = query.where("CAST(id AS TEXT) LIKE ?", "#{term}%")
71
+ else
72
+ query = query.where("title ilike ?", "%#{params[:term]}%")
73
+ end
74
+ render json: query.all.collect { |p| [p.title, p.id] }
75
+ end
76
+ end
77
+ end
78
+
59
79
  private
60
80
 
61
81
  def results
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ class ProposalLinkedEvent < Decidim::Events::SimpleEvent
6
+ i18n_attributes :proposal_title, :proposal_path
7
+
8
+ def proposal_path
9
+ @proposal_path ||= Decidim::ResourceLocatorPresenter.new(proposal).path
10
+ end
11
+
12
+ def proposal_title
13
+ @proposal_title ||= proposal.title
14
+ end
15
+
16
+ def proposal
17
+ @proposal ||= resource.linked_resources(:proposals, "included_proposals").where(id: extra[:proposal_id]).first
18
+ end
19
+ end
20
+ end
21
+ end
@@ -26,11 +26,12 @@ module Decidim
26
26
 
27
27
  validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
28
28
  validates :category, presence: true, if: ->(form) { form.decidim_category_id.present? }
29
- validate { errors.add(:decidim_scope_id, :invalid) if current_participatory_space&.scope && !current_participatory_space&.scope&.ancestor_of?(scope) }
30
29
 
31
30
  validates :parent, presence: true, if: ->(form) { form.parent_id.present? }
32
31
  validates :status, presence: true, if: ->(form) { form.decidim_accountability_status_id.present? }
33
32
 
33
+ validate :scope_belongs_to_participatory_space_scope
34
+
34
35
  delegate :categories, to: :current_feature
35
36
 
36
37
  def map_model(model)
@@ -40,7 +41,10 @@ module Decidim
40
41
  end
41
42
 
42
43
  def proposals
43
- @proposals ||= Decidim.find_resource_manifest(:proposals).try(:resource_scope, current_feature)&.order(title: :asc)&.pluck(:title, :id)
44
+ @proposals ||= Decidim.find_resource_manifest(:proposals)
45
+ .try(:resource_scope, current_feature)
46
+ &.where(id: proposal_ids)
47
+ &.order(title: :asc)
44
48
  end
45
49
 
46
50
  def projects
@@ -52,7 +56,7 @@ module Decidim
52
56
  #
53
57
  # Returns a Decidim::Scope
54
58
  def scope
55
- @scope ||= @decidim_scope_id ? current_feature.scopes.find_by(id: @decidim_scope_id) : current_participatory_space&.scope
59
+ @scope ||= @decidim_scope_id ? current_participatory_space.scopes.find_by(id: @decidim_scope_id) : current_participatory_space.scope
56
60
  end
57
61
 
58
62
  # Scope identifier
@@ -73,6 +77,12 @@ module Decidim
73
77
  def status
74
78
  @status ||= Decidim::Accountability::Status.find_by(feature: current_feature, id: decidim_accountability_status_id)
75
79
  end
80
+
81
+ private
82
+
83
+ def scope_belongs_to_participatory_space_scope
84
+ errors.add(:decidim_scope_id, :invalid) if current_participatory_space.out_of_scope?(scope)
85
+ end
76
86
  end
77
87
  end
78
88
  end
@@ -7,7 +7,7 @@ module Decidim
7
7
  class Result < Accountability::ApplicationRecord
8
8
  include Decidim::Resourceable
9
9
  include Decidim::HasFeature
10
- include Decidim::HasScope
10
+ include Decidim::ScopableFeature
11
11
  include Decidim::HasCategory
12
12
  include Decidim::HasReference
13
13
  include Decidim::Comments::Commentable
@@ -25,6 +25,13 @@ module Decidim
25
25
 
26
26
  after_save :update_parent_progress, if: -> { parent_id.present? }
27
27
 
28
+ def self.order_randomly(seed)
29
+ transaction do
30
+ connection.execute("SELECT setseed(#{connection.quote(seed)})")
31
+ order("RANDOM()").load
32
+ end
33
+ end
34
+
28
35
  def update_parent_progress
29
36
  return if parent.blank?
30
37
 
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ module AdminLog
6
+ # This class holds the logic to present a `Decidim::Accountability::Result`
7
+ # for the `AdminLog` log.
8
+ #
9
+ # Usage should be automatic and you shouldn't need to call this class
10
+ # directly, but here's an example:
11
+ #
12
+ # action_log = Decidim::ActionLog.last
13
+ # view_helpers # => this comes from the views
14
+ # ResultPresenter.new(action_log, view_helpers).present
15
+ class ResultPresenter < Decidim::Log::BasePresenter
16
+ private
17
+
18
+ def action_string
19
+ case action
20
+ when "create"
21
+ "decidim.accountability.admin_log.result.create"
22
+ when "update"
23
+ "decidim.accountability.admin_log.result.update"
24
+ end
25
+ end
26
+
27
+ def diff_fields_mapping
28
+ {
29
+ start_date: :date,
30
+ end_date: :date,
31
+ description: :i18n,
32
+ title: :i18n,
33
+ decidim_scope_id: :scope,
34
+ progress: :percentage
35
+ }
36
+ end
37
+
38
+ def i18n_labels_scope
39
+ "activemodel.attributes.result"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -54,11 +54,9 @@
54
54
 
55
55
  <div class="row column">
56
56
  <% if @form.proposals %>
57
- <%= form.select :proposal_ids,
58
- @form.proposals,
59
- {},
60
- { multiple: true, class: "chosen-select" }
61
- %>
57
+ <% picker_options = { id: "decidim_accountability_proposals", class: "picker-multiple", name: 'result[proposal_ids]', multiple: true }
58
+ prompt_params= { url: 'proposals', text: t(".add_proposal") } %>
59
+ <%= form.data_picker(:proposals, picker_options, prompt_params) {|item| { url: 'proposals', text: "##{item.id}- #{item.title}" }} %>
62
60
  <% end %>
63
61
  </div>
64
62
 
@@ -0,0 +1,12 @@
1
+ <div class="picker-header">
2
+ <h1><%= t(".current_selection") %></h1>
3
+ </div>
4
+ <div class="picker-content">
5
+ <input type="text" name="proposal-search" id="data_picker-autocomplete"/>
6
+ </div>
7
+ <div class="picker-footer">
8
+ <div class="buttons button--double">
9
+ <a id="proposal-picker-choose" class="button mute" href="proposals" data-picker-value="" data-picker-text="" ><%= t(".select") %></a>
10
+ <a class="button clear" data-close=""><%= t(".close") %></a>
11
+ </div>
12
+ </div>
@@ -28,14 +28,14 @@
28
28
  <p class="heading3">
29
29
  <%= link_to translated_attribute(category.name),results_path(filter: { category_id: category, scope_id: current_scope }) %></p>
30
30
 
31
- <% if progress_calculator(current_scope, category.id).present? %>
31
+ <% if feature_settings.display_progress_enabled? && progress_calculator(current_scope, category.id).present? %>
32
32
  <div class="progress">
33
33
  <div class="progress-meter" style="width:<%= progress_calculator(current_scope, category.id) %>%"></div>
34
34
  </div>
35
35
  <% end %>
36
36
 
37
37
  <div class="progress-info">
38
- <% if progress_calculator(current_scope, category.id).present? %>
38
+ <% if feature_settings.display_progress_enabled? && progress_calculator(current_scope, category.id).present? %>
39
39
  <div class="progress-figure heading3">
40
40
  <%= display_percentage progress_calculator(current_scope, category.id) %>
41
41
  </div>
@@ -55,7 +55,7 @@
55
55
  <div class="category--line">
56
56
  <strong><%= translated_attribute(subcategory.name) %></strong>
57
57
 
58
- <% if progress_calculator(current_scope, subcategory.id).present? %>
58
+ <% if feature_settings.display_progress_enabled? && progress_calculator(current_scope, subcategory.id).present? %>
59
59
  <div class="progress-figure heading3">
60
60
  <%= display_percentage progress_calculator(current_scope, subcategory.id) %>
61
61
  </div>
@@ -4,7 +4,7 @@
4
4
  <%== translated_attribute feature_settings.intro %>
5
5
  </div>
6
6
 
7
- <% if progress_calculator(current_scope, nil).present? %>
7
+ <% if feature_settings.display_progress_enabled? && progress_calculator(current_scope, nil).present? %>
8
8
  <div class="small-12 medium-5 columns">
9
9
  <div class="progress-level">
10
10
  <p><%= t(".global_status") %></p>
@@ -1,12 +1,16 @@
1
1
  <div class="lines-breadcrumb">
2
2
  <%= link_to t(".global"), root_path %>
3
- <span class="percentage"><%= display_percentage progress_calculator(current_scope, nil) %></span>
3
+ <% if feature_settings.display_progress_enabled? %>
4
+ <span class="percentage"><%= display_percentage progress_calculator(current_scope, nil) %></span>
5
+ <% end %>
4
6
 
5
7
  <% if category.present? && category.parent.present? %>
6
8
  <span class="breadcrumb--separator">></span>
7
9
  <div>
8
10
  <%= link_to translated_attribute(category.parent.name), results_path(filter: { category_id: category.parent_id, scope_id: current_scope }) %>
9
- <span class="percentage"><%= display_percentage progress_calculator(current_scope, category.parent_id) %></span>
11
+ <% if feature_settings.display_progress_enabled? %>
12
+ <span class="percentage"><%= display_percentage progress_calculator(current_scope, category.parent_id) %></span>
13
+ <% end %>
10
14
  </div>
11
15
  <% end %>
12
16
 
@@ -14,7 +18,9 @@
14
18
  <span class="breadcrumb--separator">></span>
15
19
  <div>
16
20
  <%= link_to translated_attribute(category.name), results_path(filter: { category_id: category.id, scope_id: current_scope }) %>
17
- <span class="percentage"><%= display_percentage progress_calculator(current_scope, category.id) %></span>
21
+ <% if feature_settings.display_progress_enabled? %>
22
+ <span class="percentage"><%= display_percentage progress_calculator(current_scope, category.id) %></span>
23
+ <% end %>
18
24
  </div>
19
25
  <% end %>
20
26
 
@@ -22,7 +28,9 @@
22
28
  <span class="breadcrumb--separator">></span>
23
29
  <div>
24
30
  <%= link_to translated_attribute(result.parent.title), result_path(result.parent, filter: { scope_id: current_scope }) %>
25
- <span class="percentage"><%= display_percentage result.parent.progress %></span>
31
+ <% if feature_settings.display_progress_enabled? %>
32
+ <span class="percentage"><%= display_percentage result.parent.progress %></span>
33
+ <% end %>
26
34
  </div>
27
35
  <% end %>
28
36
 
@@ -34,7 +34,7 @@
34
34
  </div>
35
35
  <% end %>
36
36
 
37
- <% if result.progress.present? %>
37
+ <% if feature_settings.display_progress_enabled? && result.progress.present? %>
38
38
  <div class="card--list__data">
39
39
  <span class="card--list__data__number"><%= display_percentage result.progress %></span>
40
40
  </div>
@@ -19,7 +19,7 @@
19
19
  <span class="text-small"><%= heading_leaf_level_results(result.children.count) %></span>
20
20
  <% end %>
21
21
 
22
- <% if result.progress.present? %>
22
+ <% if feature_settings.display_progress_enabled? && result.progress.present? %>
23
23
  <div class="card--list__data">
24
24
  <span class="card--list__data__number"><%= display_percentage result.progress %></span>
25
25
  </div>
@@ -9,7 +9,7 @@
9
9
  </div>
10
10
 
11
11
  <div class="row">
12
- <% if result.progress.present? %>
12
+ <% if feature_settings.display_progress_enabled? && result.progress.present? %>
13
13
  <div class="columns section mediumlarge-3 mediumlarge-push-9">
14
14
  <div class="progress-level">
15
15
  <div class="progress-label">
@@ -1,4 +1,4 @@
1
- <div class="row accountability">
1
+ <div class="row accountability result-view">
2
2
  <div class="small-12 columns">
3
3
  <%= render partial: "decidim/accountability/results/nav_breadcrumb", locals: { category: result.try(:category) } %>
4
4
  </div>
@@ -1,4 +1,20 @@
1
1
  <div class="columns section mediumlarge-4 large-3">
2
+
3
+ <% if result.children.any? && feature_settings.display_progress_enabled? && result.progress.present? %>
4
+ <div class="progress-level section">
5
+ <div class="progress-label">
6
+ <span class="progress-text"><%= t("models.result.fields.progress", scope: "decidim.accountability") %>:</span>
7
+ <span class="progress-figure">
8
+ <%= display_percentage result.progress %>
9
+ </span>
10
+ </div>
11
+
12
+ <div class="progress">
13
+ <div class="progress-meter" style="width:<%= result.progress %>%"></div>
14
+ </div>
15
+ </div>
16
+ <% end %>
17
+
2
18
  <% if result.versions.any? || result.linked_resources(:proposals, "included_proposals").any? %>
3
19
  <div class="card extra line-stats">
4
20
  <div class="definition-data">
@@ -6,7 +22,9 @@
6
22
  <div class="definition-data__item versions_count">
7
23
  <span class="definition-data__title"><%= t("results.show.stats.version_number", scope: "decidim.accountability") %></span>
8
24
  <%= result.versions.count %>
9
- <%= link_to t("results.show.stats.show_all_versions", scope: "decidim.accountability"), result_versions_path(result), class: "button button--sc hollow secondary small expanded" %>
25
+ <div class="pr-s pl-s">
26
+ <%= link_to t("results.show.stats.show_all_versions", scope: "decidim.accountability"), result_versions_path(result), class: "button button--sc hollow secondary small expanded" %>
27
+ </div>
10
28
  </div>
11
29
  <% if result.last_editor.present? %>
12
30
  <div class="definition-data__item last_revision_by">
@@ -49,7 +67,7 @@
49
67
  </div>
50
68
  <% end %>
51
69
 
52
- <%= feature_reference(result) %>
70
+ <%= resource_reference(result) %>
53
71
  <%= render partial: "decidim/shared/share_modal" %>
54
72
  <%= embed_modal_for result_result_widget_url(result, format: :js) %>
55
73
  </div>