decidim-accountability 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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>