decidim-budgets 0.13.1 → 0.14.1

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