decidim-budgets 0.13.1 → 0.14.1

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/budgets/project_cell.rb +1 -1
  3. data/app/commands/decidim/budgets/admin/import_proposals_to_budgets.rb +76 -0
  4. data/app/controllers/decidim/budgets/admin/attachment_collections_controller.rb +0 -4
  5. data/app/controllers/decidim/budgets/admin/attachments_controller.rb +0 -4
  6. data/app/controllers/decidim/budgets/admin/proposals_imports_controller.rb +32 -0
  7. data/app/controllers/decidim/budgets/line_items_controller.rb +1 -1
  8. data/app/controllers/decidim/budgets/orders_controller.rb +1 -1
  9. data/app/forms/decidim/budgets/admin/project_form.rb +4 -1
  10. data/app/forms/decidim/budgets/admin/project_import_proposals_form.rb +35 -0
  11. data/app/models/decidim/budgets/project.rb +5 -0
  12. data/app/permissions/decidim/budgets/admin/permissions.rb +3 -1
  13. data/app/permissions/decidim/budgets/permissions.rb +18 -0
  14. data/app/views/decidim/budgets/admin/projects/index.html.erb +4 -1
  15. data/app/views/decidim/budgets/admin/proposals_imports/new.html.erb +27 -0
  16. data/app/views/decidim/budgets/line_items/update_budget.js.erb +6 -6
  17. data/app/views/decidim/budgets/projects/_project.html.erb +1 -1
  18. data/app/views/decidim/budgets/projects/show.html.erb +10 -1
  19. data/config/locales/ca.yml +10 -0
  20. data/config/locales/en.yml +10 -0
  21. data/config/locales/es-PY.yml +10 -0
  22. data/config/locales/es.yml +10 -0
  23. data/config/locales/eu.yml +10 -0
  24. data/config/locales/fi.yml +43 -33
  25. data/config/locales/fr.yml +10 -0
  26. data/config/locales/gl.yml +10 -0
  27. data/config/locales/hu.yml +153 -0
  28. data/config/locales/it.yml +10 -0
  29. data/config/locales/nl.yml +10 -0
  30. data/config/locales/pl.yml +10 -0
  31. data/config/locales/pt-BR.yml +15 -5
  32. data/config/locales/pt.yml +10 -0
  33. data/config/locales/ru.yml +13 -3
  34. data/config/locales/sv.yml +35 -25
  35. data/config/locales/uk.yml +54 -44
  36. data/lib/decidim/budgets/admin_engine.rb +4 -0
  37. data/lib/decidim/budgets/component.rb +2 -0
  38. data/lib/decidim/budgets/test/factories.rb +3 -3
  39. data/lib/decidim/budgets/version.rb +1 -1
  40. metadata +18 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d57451a0817b12755de8c494193e41145a4913d9aa19e68c783104f90224897
4
- data.tar.gz: f496dd16ecdd5f904fd63a6360cdb04f0ad08a068d9dae869d8eb993761f2d4a
3
+ metadata.gz: 5c4245371508038885cb48f6ed330288461ea8554a24678fc0a250a928cbff4a
4
+ data.tar.gz: 00d30ac4b1662d12100a97767a53594f7412a15e71ce55d3827f841358c2665b
5
5
  SHA512:
6
- metadata.gz: 162e5bf6ea382161f98d45a43ad66170fd2418e93a568690b01e1ae16faab21c90d3100c80d464ffe1dbdd1e467e344213595c396a5781c6ee9614145b163270
7
- data.tar.gz: 92f793cc2251d01ccd1668b68954a5a56b88de3486e8aae63089ee01aaf8149023dae7f92259323a0f8edff0e72f36a70c3787f5b4001505b5b70612f52e4d98
6
+ metadata.gz: 1c3847ceb6895e9720c993cd147db952e1147b22c6293be831bb486680161edefc188602e4f774d74819b78fae0ab9207c7d67867874bc9d363e1c17c40b8c44
7
+ data.tar.gz: 4b71590a413f024134ceb00827291c1f51acaad882537e249d0e4adeef85e9f856ff2b5bee0563af2234caf1ce9def5f6c5ff87ba11fca1298d36aad68dd15a0
@@ -8,7 +8,7 @@ module Decidim
8
8
  include Cell::ViewModel::Partial
9
9
 
10
10
  def show
11
- cell card_size, model
11
+ cell card_size, model, options
12
12
  end
13
13
 
14
14
  private
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ module Admin
6
+ # A command with all the business logic when an admin imports proposals from
7
+ # one component to budget component.
8
+ class ImportProposalsToBudgets < Rectify::Command
9
+ # Public: Initializes the command.
10
+ #
11
+ # form - A form object with the params.
12
+ def initialize(form)
13
+ @form = form
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid.
19
+ # - :invalid if the form wasn't valid and we couldn't proceed.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ return broadcast(:invalid) unless @form.valid?
24
+
25
+ broadcast(:ok, create_projects_from_accepted_proposals)
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :form
31
+
32
+ def create_projects_from_accepted_proposals
33
+ transaction do
34
+ proposals.map do |original_proposal|
35
+ next if proposal_already_copied?(original_proposal, target_component)
36
+
37
+ project = Decidim::Budgets::Project.new
38
+ project.title = project_localized(original_proposal.title)
39
+ project.description = project_localized(original_proposal.body)
40
+ project.budget = form.default_budget
41
+ project.category = original_proposal.category
42
+ project.component = target_component
43
+ project.save!
44
+
45
+ project.link_resources([original_proposal], "included_proposals")
46
+ end.compact
47
+ end
48
+ end
49
+
50
+ def proposals
51
+ Decidim::Proposals::Proposal.where(component: origin_component).where(state: "accepted")
52
+ end
53
+
54
+ def origin_component
55
+ @form.origin_component
56
+ end
57
+
58
+ def target_component
59
+ @form.current_component
60
+ end
61
+
62
+ def proposal_already_copied?(original_proposal, target_component)
63
+ original_proposal.linked_resources(:projects, "included_proposals").any? do |proposal|
64
+ proposal.component == target_component
65
+ end
66
+ end
67
+
68
+ def project_localized(text)
69
+ Decidim.available_locales.inject({}) do |result, locale|
70
+ result.update(locale => text)
71
+ end.with_indifferent_access
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -19,10 +19,6 @@ module Decidim
19
19
  def project
20
20
  @project ||= projects.find(params[:project_id])
21
21
  end
22
-
23
- def authorization_object
24
- project.component
25
- end
26
22
  end
27
23
  end
28
24
  end
@@ -20,10 +20,6 @@ module Decidim
20
20
  def project
21
21
  @project ||= projects.find(params[:project_id])
22
22
  end
23
-
24
- def authorization_object
25
- project.component
26
- end
27
23
  end
28
24
  end
29
25
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ module Admin
6
+ class ProposalsImportsController < Admin::ApplicationController
7
+ def new
8
+ enforce_permission_to :import_proposals, :projects
9
+
10
+ @form = form(Admin::ProjectImportProposalsForm).instance
11
+ end
12
+
13
+ def create
14
+ enforce_permission_to :import_proposals, :projects
15
+
16
+ @form = form(Admin::ProjectImportProposalsForm).from_params(params)
17
+ Admin::ImportProposalsToBudgets.call(@form) do
18
+ on(:ok) do |projects|
19
+ flash[:notice] = I18n.t("proposals_imports.create.success", scope: "decidim.budgets.admin", number: projects.length)
20
+ redirect_to EngineRouter.admin_proxy(current_component).root_path
21
+ end
22
+
23
+ on(:invalid) do
24
+ flash[:alert] = I18n.t("proposals_imports.create.invalid", scope: "decidim.budgets.admin")
25
+ render action: "new"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -9,7 +9,7 @@ module Decidim
9
9
  helper_method :project
10
10
 
11
11
  def create
12
- authorize_action! "vote"
12
+ enforce_permission_to :vote, :project, project: project
13
13
 
14
14
  respond_to do |format|
15
15
  AddLineItem.call(current_order, project, current_user) do
@@ -7,7 +7,7 @@ module Decidim
7
7
  include NeedsCurrentOrder
8
8
 
9
9
  def checkout
10
- authorize_action! "vote"
10
+ enforce_permission_to :vote, :project, order: current_order
11
11
 
12
12
  Checkout.call(current_order, current_component) do
13
13
  on(:ok) do
@@ -7,6 +7,7 @@ module Decidim
7
7
  class ProjectForm < Decidim::Form
8
8
  include TranslatableAttributes
9
9
  include TranslationsHelper
10
+ include Decidim::ApplicationHelper
10
11
 
11
12
  translatable_attribute :title, String
12
13
  translatable_attribute :description, String
@@ -36,7 +37,9 @@ module Decidim
36
37
  end
37
38
 
38
39
  def proposals
39
- @proposals ||= Decidim.find_resource_manifest(:proposals).try(:resource_scope, current_component)&.order(title: :asc)&.pluck(:title, :id)
40
+ @proposals ||= Decidim.find_resource_manifest(:proposals).try(:resource_scope, current_component)
41
+ &.order(title: :asc)
42
+ &.map { |proposal| [present(proposal).title, proposal.id] }
40
43
  end
41
44
 
42
45
  # Finds the Category from the decidim_category_id.
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ module Admin
6
+ # A form object to be used when admin users want to import a collection of proposals
7
+ # from another component into projects component.
8
+ class ProjectImportProposalsForm < Decidim::Form
9
+ mimic :proposals_import
10
+
11
+ attribute :origin_component_id, Integer
12
+ attribute :default_budget, Integer
13
+ attribute :import_all_accepted_proposals, Boolean
14
+
15
+ validates :origin_component_id, :origin_component, :current_component, presence: true
16
+ validates :import_all_accepted_proposals, allow_nil: false, acceptance: true
17
+ validates :default_budget, presence: true, numericality: { greater_than: 0 }
18
+
19
+ def origin_component
20
+ @origin_component ||= origin_components.find_by(id: origin_component_id)
21
+ end
22
+
23
+ def origin_components
24
+ @origin_components ||= current_participatory_space.components.where(manifest_name: :proposals)
25
+ end
26
+
27
+ def origin_components_collection
28
+ origin_components.map do |component|
29
+ [component.name[I18n.locale.to_s], component.id]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -49,6 +49,11 @@ module Decidim
49
49
  def confirmed_orders_count
50
50
  orders.finished.count
51
51
  end
52
+
53
+ # Public: Overrides the `allow_resource_permissions?` Resourceable concern method.
54
+ def allow_resource_permissions?
55
+ component.settings.resources_permissions_enabled
56
+ end
52
57
  end
53
58
  end
54
59
  end
@@ -8,11 +8,13 @@ module Decidim
8
8
  # The public part needs to be implemented yet
9
9
  return permission_action if permission_action.scope != :admin
10
10
 
11
- return permission_action if permission_action.subject != :project
11
+ return permission_action unless [:project, :projects].include?(permission_action.subject)
12
12
 
13
13
  case permission_action.action
14
14
  when :create
15
15
  permission_action.allow!
16
+ when :import_proposals
17
+ permission_action.allow!
16
18
  when :update, :destroy
17
19
  permission_action.allow! if project.present?
18
20
  end
@@ -13,12 +13,30 @@ module Decidim
13
13
  return permission_action if permission_action.subject != :project
14
14
 
15
15
  case permission_action.action
16
+ when :vote
17
+ can_vote_project?(project || order&.projects&.first)
16
18
  when :report
17
19
  permission_action.allow!
18
20
  end
19
21
 
20
22
  permission_action
21
23
  end
24
+
25
+ private
26
+
27
+ def project
28
+ @project ||= context.fetch(:project, nil)
29
+ end
30
+
31
+ def order
32
+ @order ||= context.fetch(:order, nil)
33
+ end
34
+
35
+ def can_vote_project?(a_project)
36
+ is_allowed = a_project && authorized?(:vote, resource: project)
37
+
38
+ toggle_allow(is_allowed)
39
+ end
22
40
  end
23
41
  end
24
42
  end
@@ -2,7 +2,8 @@
2
2
  <div class="card-divider">
3
3
  <h2 class="card-title">
4
4
  <%= t(".title") %>
5
- <%= link_to t("actions.new", scope: "decidim.budgets"), new_project_path, class: "button tiny button--title" if allowed_to? :create, :project %>
5
+ <%= link_to t("actions.new", scope: "decidim.budgets"), new_project_path, class: "button tiny button--title new" if allowed_to? :create, :project %>
6
+ <%= link_to t("actions.import", scope: "decidim.budgets", name: t("models.project.name", scope: "decidim.budgets.admin")), new_proposals_import_path, class: "button tiny button--title" if allowed_to? :import_proposals, :project %>
6
7
  </h2>
7
8
  </div>
8
9
 
@@ -40,6 +41,8 @@
40
41
  <%= icon_link_to "paperclip", project_attachments_path(project), t("actions.attachments", scope: "decidim.budgets"), class: "action-icon--attachments" %>
41
42
  <% end %>
42
43
 
44
+ <%= resource_permissions_link(project) %>
45
+
43
46
  <% if allowed_to? :destroy, :project, project: project %>
44
47
  <%= icon_link_to "circle-x", project_path(project), t("actions.destroy", scope: "decidim.budgets"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.budgets") } %>
45
48
  <% end %>
@@ -0,0 +1,27 @@
1
+ <%= decidim_form_for(@form, url: proposals_import_path, html: { class: "form import_proposals" }) do |f| %>
2
+ <% if @form.origin_components.any? %>
3
+ <div class="card">
4
+ <div class="card-divider">
5
+ <h2 class="card-title"><%= title %></h2>
6
+ </div>
7
+
8
+ <div class="card-section">
9
+ <div class="row column">
10
+ <%= f.select :origin_component_id, @form.origin_components_collection, prompt: t(".select_component") %>
11
+ </div>
12
+ <div class="row column">
13
+ <%= f.number_field :default_budget %>
14
+ </div>
15
+ <div class="row column">
16
+ <%= f.check_box :import_all_accepted_proposals %>
17
+ </div>
18
+ </div>
19
+ </div>
20
+
21
+ <div class="button--double form-general-submit">
22
+ <%= f.submit t(".create") %>
23
+ </div>
24
+ <% else %>
25
+ <p><%= t(".no_components") %></p>
26
+ <% end %>
27
+ <% end %>
@@ -5,20 +5,20 @@ var $projectItem = $('#project-<%= project.id %>-item');
5
5
  var $projectBudgetButton = $('#project-<%= project.id %>-budget-button');
6
6
  var $budgetConfirm = $('#budget-confirm');
7
7
 
8
- morphdom($orderTotalBudget[0], '<%= j(render partial: 'decidim/budgets/projects/order_total_budget').strip.html_safe %>');
9
- morphdom($orderSelectedProjects[0], '<%= j(render partial: 'decidim/budgets/projects/order_selected_projects').strip.html_safe %>');
10
- morphdom($orderProgress[0], '<%= j(render partial: 'decidim/budgets/projects/order_progress').strip.html_safe %>');
11
- morphdom($budgetConfirm[0], '<%= j(render partial: 'decidim/budgets/projects/budget_confirm').strip.html_safe %>')
8
+ morphdom($orderTotalBudget[0], '<%= j(render partial: "decidim/budgets/projects/order_total_budget").strip.html_safe %>');
9
+ morphdom($orderSelectedProjects[0], '<%= j(render partial: "decidim/budgets/projects/order_selected_projects").strip.html_safe %>');
10
+ morphdom($orderProgress[0], '<%= j(render partial: "decidim/budgets/projects/order_progress").strip.html_safe %>');
11
+ morphdom($budgetConfirm[0], '<%= j(render partial: "decidim/budgets/projects/budget_confirm").strip.html_safe %>')
12
12
 
13
13
  $("#order-progress").foundation();
14
14
  $(".budget-summary__selected").foundation();
15
15
 
16
16
  if ($projectItem.length > 0) {
17
- morphdom($projectItem[0], '<%= j(render partial: 'decidim/budgets/projects/project', locals: { project: project }).strip.html_safe %>');
17
+ morphdom($projectItem[0], '<%= j(render partial: "decidim/budgets/projects/project", locals: { project: project }).strip.html_safe %>');
18
18
  }
19
19
 
20
20
  if ($projectBudgetButton.length > 0) {
21
- morphdom($projectBudgetButton[0], '<%= j(render partial: 'decidim/budgets/projects/project_budget_button', locals: { project: project }).strip.html_safe %>');
21
+ morphdom($projectBudgetButton[0], '<%= j(render partial: "decidim/budgets/projects/project_budget_button", locals: { project: project }).strip.html_safe %>');
22
22
  }
23
23
 
24
24
  window.DecidimBudgets.checkProgressPosition();
@@ -6,7 +6,7 @@
6
6
  <%= translated_attribute project.title %>
7
7
  </h5>
8
8
  <% end %>
9
- <%= cell "decidim/tags", project, context: {extra_classes: ["tags--project"]} %>
9
+ <%= cell "decidim/tags", project, context: { extra_classes: ["tags--project"] } %>
10
10
  </div>
11
11
  </div>
12
12
  <% if current_user.present? %>
@@ -3,6 +3,15 @@
3
3
  description: translated_attribute(project.description)
4
4
  ) %>
5
5
 
6
+ <%
7
+ edit_link(
8
+ resource_locator(project).edit,
9
+ :update,
10
+ :project,
11
+ project: project
12
+ )
13
+ %>
14
+
6
15
  <div class="row column view-header">
7
16
  <% if current_user.present? && current_settings.votes_enabled? && current_participatory_space.can_participate?(current_user) %>
8
17
  <%= render partial: "budget_summary", locals: { include_heading: false } %>
@@ -38,7 +47,7 @@
38
47
  <div class="columns mediumlarge-8 mediumlarge-pull-4">
39
48
  <div class="section">
40
49
  <%= decidim_sanitize translated_attribute project.description %>
41
- <%= cell "decidim/tags", project, context: {extra_classes: ["tags--project"]} %>
50
+ <%= cell "decidim/tags", project, context: { extra_classes: ["tags--project"] } %>
42
51
  </div>
43
52
  <%= linked_resources_for project, :proposals, "included_proposals" %>
44
53
  <%= linked_resources_for project, :results, "included_projects" %>
@@ -21,6 +21,7 @@ ca:
21
21
  confirm_destroy: Segur que vols eliminar aquest projecte?
22
22
  destroy: Esborrar
23
23
  edit: Editar
24
+ import: Importa propostes a projectes
24
25
  new: Nou projecte
25
26
  preview: Previsualitzar
26
27
  title: Accions
@@ -47,6 +48,14 @@ ca:
47
48
  update:
48
49
  invalid: Hi ha hagut un problema actualitzant aquest projecte
49
50
  success: Projecte actualitzat correctament
51
+ proposals_imports:
52
+ create:
53
+ invalid: Hi ha hagut un problema en importar les propostes en projectes
54
+ success: "S'ha importat amb èxit %{number} propostes en projectes"
55
+ new:
56
+ create: Importa propostes a projectes
57
+ no_components: No hi ha cap component de propostes en aquest espai participatiu per importar les propostes a projectes.
58
+ select_component: Selecciona un component
50
59
  admin_log:
51
60
  project:
52
61
  create: "%{user_name} ha creat el projecte %{resource_name} a l'espai %{space_name}"
@@ -121,6 +130,7 @@ ca:
121
130
  announcement: Anunci
122
131
  comments_enabled: Comentaris habilitats
123
132
  projects_per_page: Projectes per pàgina
133
+ resources_permissions_enabled: Es poden establir permisos d'accions per a cada trobada
124
134
  total_budget: Pressupost total
125
135
  vote_threshold_percent: Percentatge del pressupost mínim per fer el vot
126
136
  step:
@@ -22,6 +22,7 @@ en:
22
22
  confirm_destroy: Are you sure you want to delete this project?
23
23
  destroy: Delete
24
24
  edit: Edit
25
+ import: Import proposals to projects
25
26
  new: New project
26
27
  preview: Preview
27
28
  title: Actions
@@ -48,6 +49,14 @@ en:
48
49
  update:
49
50
  invalid: There's been a problem updating this project
50
51
  success: Project successfully updated
52
+ proposals_imports:
53
+ create:
54
+ invalid: There's been a problem importing the proposals into projects
55
+ success: "%{number} proposals successfully imported into projects"
56
+ new:
57
+ create: Import proposals to projects
58
+ no_components: There are no other proposal components in this participatory space to import the proposals into projects.
59
+ select_component: Please select a component
51
60
  admin_log:
52
61
  project:
53
62
  create: "%{user_name} created the %{resource_name} project in the %{space_name} space"
@@ -122,6 +131,7 @@ en:
122
131
  announcement: Announcement
123
132
  comments_enabled: Comments enabled
124
133
  projects_per_page: Projects per page
134
+ resources_permissions_enabled: Actions permissions can be set for each meeting
125
135
  total_budget: Total budget
126
136
  vote_threshold_percent: Vote threshold percent
127
137
  step: