decidim-plans 0.16.6 → 0.16.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/app/assets/javascripts/decidim/plans/admin/plans.js.es6 +93 -1
  4. data/app/assets/javascripts/decidim/plans/multifield/dynamic_fields.component.js.es6 +39 -0
  5. data/app/assets/javascripts/decidim/plans/multifield.js.es6 +1 -0
  6. data/app/cells/decidim/plans/plan_m/tags.erb +1 -0
  7. data/app/cells/decidim/plans/plan_m_cell.rb +1 -1
  8. data/app/cells/decidim/plans/tags/show.erb +7 -0
  9. data/app/cells/decidim/plans/tags/taggings.erb +3 -0
  10. data/app/cells/decidim/plans/tags_cell.rb +52 -0
  11. data/app/commands/decidim/plans/admin/answer_plan.rb +3 -0
  12. data/app/commands/decidim/plans/admin/create_tag.rb +44 -0
  13. data/app/commands/decidim/plans/admin/destroy_tag.rb +44 -0
  14. data/app/commands/decidim/plans/admin/update_plan_taggings.rb +53 -0
  15. data/app/commands/decidim/plans/admin/update_tag.rb +47 -0
  16. data/app/commands/decidim/plans/close_plan.rb +9 -1
  17. data/app/commands/decidim/plans/reopen_plan.rb +9 -1
  18. data/app/controllers/decidim/plans/admin/plans_controller.rb +32 -2
  19. data/app/controllers/decidim/plans/admin/tags_controller.rb +126 -0
  20. data/app/controllers/decidim/plans/plans_controller.rb +7 -2
  21. data/app/forms/decidim/plans/admin/tag_form.rb +22 -0
  22. data/app/forms/decidim/plans/admin/taggings_form.rb +20 -0
  23. data/app/helpers/decidim/plans/attached_proposals_helper.rb +18 -2
  24. data/app/helpers/decidim/plans/plan_cells_helper.rb +1 -1
  25. data/app/models/decidim/plans/plan.rb +14 -0
  26. data/app/models/decidim/plans/plan_tagging.rb +13 -0
  27. data/app/models/decidim/plans/tag.rb +20 -0
  28. data/app/permissions/decidim/plans/admin/permissions.rb +14 -4
  29. data/app/permissions/decidim/plans/permissions.rb +3 -1
  30. data/app/presenters/decidim/plans/plan_presenter.rb +4 -0
  31. data/app/queries/decidim/plans/component_plan_tags.rb +35 -0
  32. data/app/queries/decidim/plans/organization_tags.rb +25 -0
  33. data/app/services/decidim/plans/plan_search.rb +6 -0
  34. data/app/views/decidim/plans/admin/plans/_plan-tr.html.erb +13 -2
  35. data/app/views/decidim/plans/admin/plans/index.html.erb +9 -0
  36. data/app/views/decidim/plans/admin/plans/taggings.html.erb +67 -0
  37. data/app/views/decidim/plans/admin/tags/_form.html.erb +21 -0
  38. data/app/views/decidim/plans/admin/tags/edit.html.erb +7 -0
  39. data/app/views/decidim/plans/admin/tags/index.html.erb +52 -0
  40. data/app/views/decidim/plans/admin/tags/new.html.erb +7 -0
  41. data/app/views/decidim/plans/plans/_evaluation_modal.html.erb +22 -0
  42. data/app/views/decidim/plans/plans/_filters.html.erb +4 -0
  43. data/app/views/decidim/plans/plans/show.html.erb +9 -1
  44. data/app/views/decidim/plans/shared/_attachments.html.erb +2 -2
  45. data/app/views/decidim/plans/shared/_tags.html.erb +1 -0
  46. data/config/locales/en.yml +57 -2
  47. data/config/locales/fi.yml +57 -2
  48. data/config/locales/sv.yml +57 -2
  49. data/db/migrate/20190329161710_fix_plan_closing_workflow_states.rb +13 -0
  50. data/db/migrate/20190331141058_create_decidim_plans_tags_and_plan_taggings.rb +17 -0
  51. data/lib/decidim/content_parsers/plan_parser.rb +82 -0
  52. data/lib/decidim/content_renderers/plan_renderer.rb +33 -0
  53. data/lib/decidim/plans/admin_engine.rb +3 -0
  54. data/lib/decidim/plans/component.rb +11 -2
  55. data/lib/decidim/plans/engine.rb +6 -0
  56. data/lib/decidim/plans/test/factories.rb +17 -0
  57. data/lib/decidim/plans/version.rb +1 -1
  58. data/lib/decidim/plans.rb +8 -0
  59. metadata +29 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 870133d81fb0fd7608a2087fbf2fdb7a434a37ebe3437334c9cdb604c2afd3f8
4
- data.tar.gz: 4e28e6926fbdfe8339eb19ef66c1c455cef50ad9cf42a1940f2c2ec9df0a041f
3
+ metadata.gz: 210bbe22dc50de4ec472503edc4ec7cc7944ccf4623cbd0602ba2f56ac73c59c
4
+ data.tar.gz: 4b6ba7db25df3fc75fcd770511918ad71aeb38247769f0df384b2090f7d7d389
5
5
  SHA512:
6
- metadata.gz: 1cd3865a386f58d321ff9e077e7c8728b011dfe85cffcc52b0c6854759ee91592398eab98506fdb31d162f52022a532a0bc5e18d21191e88798d9fa29fc84858
7
- data.tar.gz: 55e53122d62b560a70532c9ce0d3b2b8466b4ca2187cc54664df4f3710a8697b8f5112c200d597d39e19006271ae4402d6421d5a8dac83e8b5dd254201bfb56f
6
+ metadata.gz: a2c04c5480bad9093b12345eedb2ae702867fa770b3a76eb7b548349620492db1997091c453424f1e5b01f7640a7a404e13cb80d4d1790921bafce4faa4fd79e
7
+ data.tar.gz: 374f57e0e4ce29c5ea888c631b3b6623251a24dbd58890292977d9f9dec60ff7631fb5ea57d781ec4e95b7a30ee20f59481e944826234b571d95a5c8bd2a2147
data/README.md CHANGED
@@ -59,7 +59,8 @@ can start writing the plans when plan creation is enabled.
59
59
 
60
60
  ## Contributing
61
61
 
62
- For instructions how to setup your development environment for Decidim, see [Decidim](https://github.com/decidim/decidim). Also follow Decidim's general
62
+ For instructions how to setup your development environment for Decidim, see
63
+ [Decidim](https://github.com/decidim/decidim). Also follow Decidim's general
63
64
  instructions for development for this project as well.
64
65
 
65
66
  ### Developing
@@ -1,3 +1,95 @@
1
1
  $(() => {
2
- // Add the needed JS initialization code here
2
+ const $search = $("#data_picker-autocomplete");
3
+ const $results = $("#plan-tags-results");
4
+ const $template = $(".decidim-template", $results);
5
+ const $form = $search.parents("form");
6
+ const addRowItem = function(id, title) {
7
+ let template = $template.html();
8
+ template = template.replace(new RegExp("{{tag_id}}", "g"), id);
9
+ template = template.replace(new RegExp("{{tag_name}}", "g"), title);
10
+ const $newRow = $(template);
11
+ $("table tbody", $results).append($newRow);
12
+ $results.removeClass("hide");
13
+
14
+ // Add it to the autocomplete form
15
+ const $field = $(`<input type="hidden" name="tags[]" value="${id}">`);
16
+ $form.append($field);
17
+
18
+ // Listen to the click event on the remove button
19
+ $(".remove-tagging", $newRow).on("click", function(ev) {
20
+ ev.preventDefault();
21
+ $newRow.remove();
22
+ $field.remove();
23
+
24
+ if ($("table tbody tr", $results).length < 1) {
25
+ $results.addClass("hide");
26
+ }
27
+ });
28
+ };
29
+ let xhr = null;
30
+ let currentSearch = "";
31
+
32
+ $search.on("keyup", function() {
33
+ currentSearch = $search.val();
34
+ });
35
+
36
+ $search.autoComplete({
37
+ minChars: 2,
38
+ cache: 0,
39
+ source: function(term, response) {
40
+ try {
41
+ xhr.abort();
42
+ } catch (exception) { xhr = null; }
43
+
44
+ const url = $form.attr("action");
45
+ xhr = $.getJSON(
46
+ url,
47
+ $form.serializeArray(),
48
+ function(data) {
49
+ if (data.length > 0) {
50
+ response(data);
51
+ } else {
52
+ response([
53
+ [null, $search.data("no-results-text"), term]
54
+ ]);
55
+ }
56
+ }
57
+ );
58
+ },
59
+ renderItem: function (item, search) {
60
+ const sanitizedSearch = search.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
61
+ const re = new RegExp(`(${sanitizedSearch.split(" ").join("|")})`, "gi");
62
+ const modelId = item[0];
63
+ const title = item[1];
64
+ const val = `${title}`;
65
+
66
+ if (modelId === null) {
67
+ // Empty result
68
+ const term = item[2];
69
+ const url = $search.data("no-results-url").replace("{{term}}", encodeURIComponent(term));
70
+ return `<div><a href="${url}">${val.replace("{{term}}", term)}</a></div>`;
71
+ }
72
+ return `<div class="autocomplete-suggestion" data-model-id="${modelId}" data-val="${title}">${val.replace(re, "<b>$1</b>")}</div>`;
73
+ },
74
+ onSelect: function(event, term, item) {
75
+ const $suggestions = $search.data("sc");
76
+ const modelId = item.data("modelId");
77
+ const title = item.data("val");
78
+
79
+ addRowItem(modelId, title);
80
+
81
+ $search.val(currentSearch);
82
+ setTimeout(function() {
83
+ $(`[data-model-id="${modelId}"]`, $suggestions).remove();
84
+ $suggestions.show();
85
+ }, 20);
86
+ }
87
+ });
88
+
89
+ const resultsArray = $results.data("results");
90
+ if (Array.isArray(resultsArray)) {
91
+ resultsArray.forEach((value) => {
92
+ addRowItem(value[0], value[1]);
93
+ });
94
+ }
3
95
  });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Extends / overrides:
3
+ * decidim/admin/dynamic_fields.component
4
+ *
5
+ * Fixes broken attachments in IE11 by overriding the `_addField` method in the
6
+ *`DynamicFieldsComponent` coming from the `decidim-admin` component.
7
+ *
8
+ * For further details, see:
9
+ * https://github.com/mainio/decidim-module-plans/issues/13
10
+ **/
11
+
12
+ ((exports) => {
13
+ const { DynamicFieldsComponent } = exports.DecidimAdmin;
14
+
15
+ class DynamicFieldsComponentExtended extends DynamicFieldsComponent {
16
+ _addField() {
17
+ const $container = $(this.wrapperSelector).find(this.containerSelector);
18
+ // START OVERRIDE
19
+ const $template = $(this.wrapperSelector).children(".decidim-template");
20
+ // END OVERRIDE
21
+ const $newField = $($template.html()).template(this.placeholderId, this._getUID());
22
+
23
+ $newField.find("ul.tabs").attr("data-tabs", true);
24
+
25
+ $newField.appendTo($container);
26
+ $newField.foundation();
27
+
28
+ if (this.onAddField) {
29
+ this.onAddField($newField);
30
+ }
31
+ }
32
+ }
33
+
34
+ exports.DecidimAdmin = exports.DecidimAdmin || {};
35
+ exports.DecidimAdmin.DynamicFieldsComponent = DynamicFieldsComponentExtended;
36
+ exports.DecidimAdmin.createDynamicFields = (options) => {
37
+ return new DynamicFieldsComponentExtended(options);
38
+ };
39
+ })(window);
@@ -1,5 +1,6 @@
1
1
  // = require decidim/admin/auto_buttons_by_position.component
2
2
  // = require decidim/admin/auto_label_by_position.component
3
3
  // = require decidim/admin/dynamic_fields.component
4
+ // = require decidim/plans/multifield/dynamic_fields.component
4
5
  // = require decidim/admin/sort_list.component
5
6
  // = require decidim/plans/multifield/component
@@ -0,0 +1 @@
1
+ <%= render partial: "decidim/plans/shared/tags.html", locals: { resource: model, tags_class_extra: "tags--proposal" } %>
@@ -31,7 +31,7 @@ module Decidim
31
31
  end
32
32
 
33
33
  def has_badge?
34
- answered? || withdrawn?
34
+ closed? || answered? || withdrawn?
35
35
  end
36
36
 
37
37
  def has_link_to_resource?
@@ -0,0 +1,7 @@
1
+ <%= content_tag :ul, class: tags_classes do %>
2
+ <%= cell("decidim/plans/tags", model).category %>
3
+
4
+ <%= cell("decidim/plans/tags", model).scope %>
5
+
6
+ <%= cell("decidim/plans/tags", model).taggings %>
7
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <% model.tags.each do |tag| %>
2
+ <%= content_tag :li, link_to_tag(tag) %>
3
+ <% end %>
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Plans
5
+ # This cell renders:
6
+ # - The category of a resource shown with the translated name (parent)
7
+ # - The scope of a resource shown with the translated name (parent)
8
+ # - The assigned tags from the plans
9
+ #
10
+ # The context `resource` must be present example use inside another `cell`:
11
+ # <%= cell("decidim/category", model.category, context: {resource: model}) %>
12
+ #
13
+ class TagsCell < Decidim::TagsCell
14
+ def show
15
+ render if category? || scope? || taggings?
16
+ end
17
+
18
+ def taggings
19
+ render if taggings?
20
+ end
21
+
22
+ private
23
+
24
+ def category?
25
+ model.category.present?
26
+ end
27
+
28
+ def scope?
29
+ has_visible_scopes?(model)
30
+ end
31
+
32
+ def taggings?
33
+ model.tags.any?
34
+ end
35
+
36
+ def link_to_tag(tag)
37
+ end
38
+
39
+ def link_to_tag(tag)
40
+ link_to tag_name(tag), tag_path(tag)
41
+ end
42
+
43
+ def tag_name(tag)
44
+ translated_attribute(tag.name)
45
+ end
46
+
47
+ def tag_path(tag)
48
+ resource_locator(model).index(filter: { tag_id: [tag.id] })
49
+ end
50
+ end
51
+ end
52
+ end
@@ -39,7 +39,10 @@ module Decidim
39
39
  plan,
40
40
  form.current_user
41
41
  ) do
42
+ closed_at = plan.closed_at || Time.current
43
+
42
44
  plan.update!(
45
+ closed_at: closed_at,
43
46
  state: @form.state,
44
47
  answer: @form.answer,
45
48
  answered_at: Time.current
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Plans
5
+ module Admin
6
+ # A command with all the business logic when a user creates a new tag.
7
+ class CreateTag < Rectify::Command
8
+ # Public: Initializes the command.
9
+ #
10
+ # form - A form object with the params.
11
+ def initialize(form)
12
+ @form = form
13
+ end
14
+
15
+ # Executes the command. Broadcasts these events:
16
+ #
17
+ # - :ok when everything is valid, together with the tag.
18
+ # - :invalid if the form wasn't valid and we couldn't proceed.
19
+ #
20
+ # Returns nothing.
21
+ def call
22
+ return broadcast(:invalid) if form.invalid?
23
+
24
+ create_tag
25
+
26
+ broadcast(:ok, tag)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :form, :tag
32
+
33
+ def create_tag
34
+ @tag = Decidim.traceability.create(
35
+ Tag,
36
+ form.current_user,
37
+ name: form.name,
38
+ organization: form.organization
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Plans
5
+ module Admin
6
+ # A command with all the business logic when a user destroys a tag.
7
+ class DestroyTag < Rectify::Command
8
+ # Public: Initializes the command.
9
+ #
10
+ # tag - The target object to be destroyed.
11
+ # current_user - the user performing the action.
12
+ def initialize(tag, current_user)
13
+ @tag = tag
14
+ @current_user = current_user
15
+ end
16
+
17
+ # Destroys the tag if valid.
18
+ #
19
+ # Broadcasts :ok if successful, :invalid otherwise.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ destroy_tag
24
+
25
+ broadcast(:ok)
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :tag, :current_user
31
+
32
+ def destroy_tag
33
+ Decidim.traceability.perform_action!(
34
+ :delete,
35
+ tag,
36
+ current_user
37
+ ) do
38
+ tag.destroy!
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Plans
5
+ module Admin
6
+ # A command with all the business logic when a user updates a tag.
7
+ class UpdatePlanTaggings < Rectify::Command
8
+ # Public: Initializes the command.
9
+ #
10
+ # form - A form object with the params.
11
+ # plan - The target object to be updated.
12
+ def initialize(form, plan)
13
+ @form = form
14
+ @plan = plan
15
+ end
16
+
17
+ # Executes the command. Broadcasts these events:
18
+ #
19
+ # - :ok when everything is valid, together with the plan.
20
+ # - :invalid if the form wasn't valid and we couldn't proceed.
21
+ #
22
+ # Returns nothing.
23
+ def call
24
+ return broadcast(:invalid) if form.invalid?
25
+
26
+ update_plan_taggings
27
+
28
+ broadcast(:ok, plan)
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :form, :plan
34
+
35
+ def update_plan_taggings
36
+ Decidim.traceability.perform_action!(
37
+ :update,
38
+ plan,
39
+ form.current_user
40
+ ) do
41
+ plan.taggings.destroy_all
42
+ plan.update!(
43
+ tags: Tag.where(
44
+ id: form.tags,
45
+ organization: plan.component.organization
46
+ )
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Plans
5
+ module Admin
6
+ # A command with all the business logic when a user updates a tag.
7
+ class UpdateTag < Rectify::Command
8
+ # Public: Initializes the command.
9
+ #
10
+ # form - A form object with the params.
11
+ # tag - The target object to be updated.
12
+ def initialize(form, tag)
13
+ @form = form
14
+ @tag = tag
15
+ end
16
+
17
+ # Executes the command. Broadcasts these events:
18
+ #
19
+ # - :ok when everything is valid, together with the plan.
20
+ # - :invalid if the form wasn't valid and we couldn't proceed.
21
+ #
22
+ # Returns nothing.
23
+ def call
24
+ return broadcast(:invalid) if form.invalid?
25
+
26
+ update_tag
27
+
28
+ broadcast(:ok, tag)
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :form, :tag
34
+
35
+ def update_tag
36
+ Decidim.traceability.perform_action!(
37
+ :update,
38
+ tag,
39
+ form.current_user
40
+ ) do
41
+ tag.update!(name: form.name)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -36,7 +36,15 @@ module Decidim
36
36
  @current_user,
37
37
  visibility: "public-only"
38
38
  ) do
39
- @plan.update closed_at: Time.current
39
+ # Unless the plan has already been answered, change the state to
40
+ # "evaluating".
41
+ state = @plan.state
42
+ state = "evaluating" unless @plan.answered?
43
+
44
+ @plan.update!(
45
+ state: state,
46
+ closed_at: Time.current
47
+ )
40
48
  end
41
49
  end
42
50
  end
@@ -36,7 +36,15 @@ module Decidim
36
36
  @current_user,
37
37
  visibility: "public-only"
38
38
  ) do
39
- @plan.update closed_at: nil
39
+ # Unless the plan has already been answered, change the state back to
40
+ # "open".
41
+ state = @plan.state
42
+ state = "open" unless @plan.answered?
43
+
44
+ @plan.update!(
45
+ state: state,
46
+ closed_at: nil
47
+ )
40
48
  end
41
49
  end
42
50
  end
@@ -11,7 +11,7 @@ module Decidim
11
11
  helper Plans::ApplicationHelper
12
12
  helper Plans::AttachmentsHelper
13
13
  helper Plans::RemainingCharactersHelper
14
- helper_method :plans, :query, :form_presenter, :attached_proposals_picker_field
14
+ helper_method :plans, :plan, :query, :counts, :form_presenter, :attached_proposals_picker_field
15
15
 
16
16
  def new
17
17
  enforce_permission_to :create, :plans
@@ -89,16 +89,46 @@ module Decidim
89
89
  end
90
90
  end
91
91
 
92
+ def taggings
93
+ enforce_permission_to :edit_taggings, :plan, plan: plan
94
+
95
+ @form = form(Admin::TaggingsForm).from_model(plan)
96
+ end
97
+
98
+ def update_taggings
99
+ enforce_permission_to :edit_taggings, :plan, plan: plan
100
+
101
+ @form = form(Admin::TaggingsForm).from_params(params)
102
+ Admin::UpdatePlanTaggings.call(@form, plan) do
103
+ on(:ok) do
104
+ flash[:notice] = I18n.t("plans.update_taggings.success", scope: "decidim.plans.admin")
105
+ redirect_to plans_path
106
+ end
107
+
108
+ on(:invalid) do
109
+ flash.now[:alert] = I18n.t("plans.update_taggings.invalid", scope: "decidim.plans.admin")
110
+ render :taggings
111
+ end
112
+ end
113
+ end
114
+
92
115
  private
93
116
 
94
117
  def query
95
- @query ||= Plan.where(component: current_component).ransack(params[:q])
118
+ @query ||= Plan.published.where(component: current_component).ransack(params[:q])
96
119
  end
97
120
 
98
121
  def plans
99
122
  @plans ||= query.result.page(params[:page]).per(15)
100
123
  end
101
124
 
125
+ def counts
126
+ @counts ||= {
127
+ published: Plan.published.where(component: current_component).count,
128
+ drafts: Plan.drafts.where(component: current_component).count
129
+ }
130
+ end
131
+
102
132
  def plan
103
133
  @plan ||= Plan.where(component: current_component).find(params[:id])
104
134
  end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Plans
5
+ module Admin
6
+ # This controller allows admins to the tags related to plans.
7
+ class TagsController < Admin::ApplicationController
8
+ include TranslatableAttributes
9
+
10
+ helper_method :plan, :tags
11
+
12
+ def index
13
+ enforce_permission_to :read, :plan_tag
14
+
15
+ respond_to do |format|
16
+ format.html do
17
+ render :index
18
+ end
19
+ format.json do
20
+ return render json: [] unless params.has_key?(:term)
21
+
22
+ tags = OrganizationTags.new(current_organization).query.where(
23
+ "name ->> '#{current_locale}' ILIKE ?",
24
+ "%#{params[:term]}%"
25
+ ).where.not(
26
+ id: plan.tags.pluck(:id)
27
+ )
28
+ render json: tags.map { |t| [t.id, translated_attribute(t.name)] }
29
+ end
30
+ end
31
+ end
32
+
33
+ def new
34
+ enforce_permission_to :create, :plan_tags
35
+
36
+ dummy_tag = Tag.new(organization: current_organization)
37
+ dummy_tag.name = {}
38
+ current_organization.available_locales.map do |locale|
39
+ dummy_tag.name[locale] = params[:name]
40
+ end
41
+
42
+ @form = form(Admin::TagForm).from_model(dummy_tag)
43
+ @form.back_to_plan = true if params[:back_to_plan].to_i == 1
44
+ end
45
+
46
+ def create
47
+ enforce_permission_to :create, :plan_tags
48
+ @form = form(Admin::TagForm).from_params(params)
49
+
50
+ Admin::CreateTag.call(@form) do
51
+ on(:ok) do
52
+ flash[:notice] = I18n.t("create.success", scope: i18n_flashes_scope)
53
+
54
+ return redirect_to taggings_plan_path(plan) if @form.back_to_plan
55
+
56
+ redirect_to plan_tags_path(plan)
57
+ end
58
+
59
+ on(:invalid) do
60
+ flash.now[:alert] = I18n.t("create.invalid", scope: i18n_flashes_scope)
61
+ render action: "new"
62
+ end
63
+ end
64
+ end
65
+
66
+ def edit
67
+ enforce_permission_to :edit, :plan_tags, tag: tag
68
+ @form = form(Admin::TagForm).from_model(tag)
69
+ end
70
+
71
+ def update
72
+ enforce_permission_to :edit, :plan_tags, tag: tag
73
+
74
+ @form = form(Admin::TagForm).from_params(params)
75
+ Admin::UpdateTag.call(@form, @tag) do
76
+ on(:ok) do
77
+ flash[:notice] = I18n.t("update.success", scope: i18n_flashes_scope)
78
+ redirect_to plan_tags_path(plan)
79
+ end
80
+
81
+ on(:invalid) do
82
+ flash.now[:alert] = I18n.t("update.invalid", scope: i18n_flashes_scope)
83
+ render :edit
84
+ end
85
+ end
86
+ end
87
+
88
+ def destroy
89
+ enforce_permission_to :destroy, :plan_tags, tag: tag
90
+
91
+ Admin::DestroyTag.call(@tag, current_user) do
92
+ on(:ok) do
93
+ flash[:notice] = I18n.t("destroy.success", scope: i18n_flashes_scope)
94
+ redirect_to plan_tags_path(plan)
95
+ end
96
+
97
+ on(:invalid) do
98
+ flash.now[:alert] = I18n.t("destroy.error", scope: i18n_flashes_scope)
99
+ redirect_to plan_tags_path(plan)
100
+ end
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def i18n_flashes_scope
107
+ "decidim.plans.admin.tags"
108
+ end
109
+
110
+ def tag
111
+ @tag ||= Tag.where(organization: current_organization).find(params[:id])
112
+ end
113
+
114
+ def plan
115
+ @plan ||= Plan.where(component: current_component).find(params[:plan_id])
116
+ end
117
+
118
+ def tags
119
+ @tags ||= OrganizationTags.new(
120
+ current_organization
121
+ ).query.page(params[:page]).per(30)
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end