decidim-plans 0.16.6 → 0.16.7
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.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/app/assets/javascripts/decidim/plans/admin/plans.js.es6 +93 -1
- data/app/assets/javascripts/decidim/plans/multifield/dynamic_fields.component.js.es6 +39 -0
- data/app/assets/javascripts/decidim/plans/multifield.js.es6 +1 -0
- data/app/cells/decidim/plans/plan_m/tags.erb +1 -0
- data/app/cells/decidim/plans/plan_m_cell.rb +1 -1
- data/app/cells/decidim/plans/tags/show.erb +7 -0
- data/app/cells/decidim/plans/tags/taggings.erb +3 -0
- data/app/cells/decidim/plans/tags_cell.rb +52 -0
- data/app/commands/decidim/plans/admin/answer_plan.rb +3 -0
- data/app/commands/decidim/plans/admin/create_tag.rb +44 -0
- data/app/commands/decidim/plans/admin/destroy_tag.rb +44 -0
- data/app/commands/decidim/plans/admin/update_plan_taggings.rb +53 -0
- data/app/commands/decidim/plans/admin/update_tag.rb +47 -0
- data/app/commands/decidim/plans/close_plan.rb +9 -1
- data/app/commands/decidim/plans/reopen_plan.rb +9 -1
- data/app/controllers/decidim/plans/admin/plans_controller.rb +32 -2
- data/app/controllers/decidim/plans/admin/tags_controller.rb +126 -0
- data/app/controllers/decidim/plans/plans_controller.rb +7 -2
- data/app/forms/decidim/plans/admin/tag_form.rb +22 -0
- data/app/forms/decidim/plans/admin/taggings_form.rb +20 -0
- data/app/helpers/decidim/plans/attached_proposals_helper.rb +18 -2
- data/app/helpers/decidim/plans/plan_cells_helper.rb +1 -1
- data/app/models/decidim/plans/plan.rb +14 -0
- data/app/models/decidim/plans/plan_tagging.rb +13 -0
- data/app/models/decidim/plans/tag.rb +20 -0
- data/app/permissions/decidim/plans/admin/permissions.rb +14 -4
- data/app/permissions/decidim/plans/permissions.rb +3 -1
- data/app/presenters/decidim/plans/plan_presenter.rb +4 -0
- data/app/queries/decidim/plans/component_plan_tags.rb +35 -0
- data/app/queries/decidim/plans/organization_tags.rb +25 -0
- data/app/services/decidim/plans/plan_search.rb +6 -0
- data/app/views/decidim/plans/admin/plans/_plan-tr.html.erb +13 -2
- data/app/views/decidim/plans/admin/plans/index.html.erb +9 -0
- data/app/views/decidim/plans/admin/plans/taggings.html.erb +67 -0
- data/app/views/decidim/plans/admin/tags/_form.html.erb +21 -0
- data/app/views/decidim/plans/admin/tags/edit.html.erb +7 -0
- data/app/views/decidim/plans/admin/tags/index.html.erb +52 -0
- data/app/views/decidim/plans/admin/tags/new.html.erb +7 -0
- data/app/views/decidim/plans/plans/_evaluation_modal.html.erb +22 -0
- data/app/views/decidim/plans/plans/_filters.html.erb +4 -0
- data/app/views/decidim/plans/plans/show.html.erb +9 -1
- data/app/views/decidim/plans/shared/_attachments.html.erb +2 -2
- data/app/views/decidim/plans/shared/_tags.html.erb +1 -0
- data/config/locales/en.yml +57 -2
- data/config/locales/fi.yml +57 -2
- data/config/locales/sv.yml +57 -2
- data/db/migrate/20190329161710_fix_plan_closing_workflow_states.rb +13 -0
- data/db/migrate/20190331141058_create_decidim_plans_tags_and_plan_taggings.rb +17 -0
- data/lib/decidim/content_parsers/plan_parser.rb +82 -0
- data/lib/decidim/content_renderers/plan_renderer.rb +33 -0
- data/lib/decidim/plans/admin_engine.rb +3 -0
- data/lib/decidim/plans/component.rb +11 -2
- data/lib/decidim/plans/engine.rb +6 -0
- data/lib/decidim/plans/test/factories.rb +17 -0
- data/lib/decidim/plans/version.rb +1 -1
- data/lib/decidim/plans.rb +8 -0
- metadata +29 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 210bbe22dc50de4ec472503edc4ec7cc7944ccf4623cbd0602ba2f56ac73c59c
|
|
4
|
+
data.tar.gz: 4b6ba7db25df3fc75fcd770511918ad71aeb38247769f0df384b2090f7d7d389
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
-
|
|
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" } %>
|
|
@@ -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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|