decidim-plans 0.16.0 → 0.16.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/app/assets/javascripts/decidim/plans/proposal_picker.js.es6 +52 -32
  4. data/app/commands/decidim/plans/close_plan.rb +44 -0
  5. data/app/commands/decidim/plans/reopen_plan.rb +44 -0
  6. data/app/controllers/decidim/plans/admin/plans_controller.rb +32 -0
  7. data/app/controllers/decidim/plans/admin/sections_controller.rb +1 -6
  8. data/app/controllers/decidim/plans/plan_collaborator_requests_controller.rb +6 -0
  9. data/app/controllers/decidim/plans/plans_controller.rb +40 -4
  10. data/app/controllers/decidim/plans/versions_controller.rb +6 -0
  11. data/app/helpers/decidim/plans/attached_proposals_helper.rb +2 -0
  12. data/app/helpers/decidim/plans/traceability_helper.rb +8 -12
  13. data/app/models/decidim/plans/plan.rb +15 -0
  14. data/app/permissions/decidim/plans/admin/permissions.rb +2 -0
  15. data/app/permissions/decidim/plans/permissions.rb +7 -1
  16. data/app/services/decidim/plans/diff_renderer/base.rb +1 -1
  17. data/app/views/decidim/plans/admin/plans/_bulk-actions.html.erb +2 -0
  18. data/app/views/decidim/plans/admin/plans/_plan-tr.html.erb +8 -0
  19. data/app/views/decidim/plans/plans/show.html.erb +7 -0
  20. data/config/locales/en.yml +23 -0
  21. data/config/locales/fi.yml +23 -0
  22. data/config/locales/sv.yml +24 -0
  23. data/db/migrate/20190214124014_add_closed_at_to_decidim_plans.rb +8 -0
  24. data/lib/decidim/plans/admin_engine.rb +4 -0
  25. data/lib/decidim/plans/component.rb +16 -3
  26. data/lib/decidim/plans/engine.rb +2 -0
  27. data/lib/decidim/plans/plan_serializer.rb +82 -0
  28. data/lib/decidim/plans/test/factories.rb +9 -2
  29. data/lib/decidim/plans/version.rb +1 -1
  30. data/lib/decidim/plans.rb +4 -2
  31. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f496c0cd1c6ee3f73085fc296508701d44434eb5a1c9dc3375c33ac99ea3a45
4
- data.tar.gz: 2893280cdc00ae08eb9096e94da371496e43a2d993ea49ef6345def50a767176
3
+ metadata.gz: a0beab872d687c4002db352e84e5de788545493a03a5c9e06d22af045d014a10
4
+ data.tar.gz: b422c03351c34cb8919b22bef233b85451c808edaf90b638e213d027493f2484
5
5
  SHA512:
6
- metadata.gz: 2751ff4b10252cf2b3bbf59deccb36692b7866dcd9ec90d649b3ca0cb640812601c2fa4ab938228a5425b8a4c96adfd9cd6ca71b147bcaa9834a2df4a815ccd3
7
- data.tar.gz: f2db56458ab76aade2d06b91e67061b6bdd857e33a848ae720579fe851e18b3007e4f89847cb51aa71daf8c50947ba0d926b33aac09b4315f7d35788869a4ebf
6
+ metadata.gz: e3a45ded830363d9e5c1bffe7f9f22b52fff04f62ba8f5d2dea1cb44cf22a716466ce945c67b5bf9f8230becc3102add69f7196fc90f36cb4fb3207fb0b3e9ec
7
+ data.tar.gz: 5fc28c2575d607f354eb8fe677f9d33886239b948614c939b04f40ddfc095a816c61d0cfaa272af340922184d3c32fe418a956c92163626c83f0989dc5fb1ee8
data/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Decidim::Plans
2
2
 
3
+ [![Build Status](https://travis-ci.com/mainio/decidim-module-plans.svg?branch=master)](https://travis-ci.com/mainio/decidim-module-plans)
4
+ [![codecov](https://codecov.io/gh/mainio/decidim-module-plans/branch/master/graph/badge.svg)](https://codecov.io/gh/mainio/decidim-module-plans)
5
+
6
+ The gem has been developed by [Mainio Tech](https://www.mainiotech.fi/).
7
+
3
8
  A [Decidim](https://github.com/decidim/decidim) module that provides a new
4
9
  component that can be added to any participatory space in Decidim. The component
5
10
  allows users to write plans together that link to specific proposals. Further on
@@ -26,6 +31,9 @@ following differences:
26
31
  converted to sub-headings in the budgeting project's description and the
27
32
  budget needs to be specified by the user doing the conversion.
28
33
 
34
+ Development of this gem has been sponsored by the
35
+ [City of Helsinki](https://www.hel.fi/).
36
+
29
37
  ## Installation
30
38
 
31
39
  Add this line to your application's Gemfile:
@@ -4,38 +4,58 @@
4
4
  // = require_self
5
5
 
6
6
  $(function() {
7
- $(document).on("open.zf.reveal", "#data_picker-modal", function () {
8
- let xhr = null;
7
+ $(document).on(
8
+ "open.zf.reveal", "#data_picker-modal",
9
9
 
10
- $("#data_picker-autocomplete").autoComplete({
11
- minChars: 2,
12
- source: function(term, response) {
13
- try {
14
- xhr.abort();
15
- } catch (exception) { xhr = null; }
10
+ /* @this HTMLElement */
11
+ function () {
12
+ let xhr = null;
16
13
 
17
- let url = $("#proposal-picker-choose").attr("href");
18
- xhr = $.getJSON(
19
- url,
20
- { term: term },
21
- function(data) { response(data); }
22
- );
23
- },
24
- renderItem: function (item, search) {
25
- let sanitizedSearch = search.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
26
- let re = new RegExp(`(${sanitizedSearch.split(" ").join("|")})`, "gi");
27
- let title = item[0];
28
- let modelId = item[1];
29
- return `<div class="autocomplete-suggestion" data-model-id="${modelId}" data-val ="${title}">${title.replace(re, "<b>$1</b>")}</div>`;
30
- },
31
- onSelect: function(event, term, item) {
32
- let choose = $("#proposal-picker-choose");
33
- let modelId = item.data("modelId");
34
- let val = item.data("val");
35
- choose.data("picker-value", modelId);
36
- choose.data("picker-text", val);
37
- choose.data("picker-choose", "");
38
- }
39
- });
40
- });
14
+ $("#data_picker-autocomplete").autoComplete({
15
+ minChars: 2,
16
+ source: function(term, response) {
17
+ try {
18
+ xhr.abort();
19
+ } catch (exception) { xhr = null; }
20
+
21
+ let url = $("#proposal-picker-choose").attr("href");
22
+ xhr = $.getJSON(
23
+ url,
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
+ return `<div class="autocomplete-suggestion" data-model-id="${modelId}" data-val ="${title}">${title.replace(re, "<b>$1</b>")}</div>`;
34
+ },
35
+ onSelect: function(event, term, item) {
36
+ let choose = $("#proposal-picker-choose");
37
+ let modelId = item.data("modelId");
38
+ let val = item.data("val");
39
+ choose.data("picker-value", modelId);
40
+ choose.data("picker-text", val);
41
+ choose.data("picker-choose", "");
42
+ }
43
+ });
44
+
45
+ // Remove all the empty values after the selection is made to prevent
46
+ // these empty values appearing in the list.
47
+ // This is needed until the following is merged to the core:
48
+ // https://github.com/decidim/decidim/pull/4842
49
+ const $choose = $("#proposal-picker-choose", this);
50
+ $choose.on("click", function() {
51
+ const $values = $("#plan_proposals .picker-values");
52
+ $("input[type='checkbox']", $values).each(function(_ev, input) {
53
+ const $input = $(input);
54
+ if ($input.val().length < 1) {
55
+ $input.parent().remove();
56
+ }
57
+ });
58
+ });
59
+ }
60
+ );
41
61
  });
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Plans
5
+ # A command with all the business logic when a user closes a plan.
6
+ class ClosePlan < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # plan - The plan to publish.
10
+ # current_user - The current user.
11
+ def initialize(plan, current_user)
12
+ @plan = plan
13
+ @current_user = current_user
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid and the plan is published.
19
+ # - :invalid if the plan's author is not the current user.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ return broadcast(:invalid) if @current_user.nil?
24
+
25
+ close_plan
26
+
27
+ broadcast(:ok, @plan)
28
+ end
29
+
30
+ private
31
+
32
+ def close_plan
33
+ Decidim.traceability.perform_action!(
34
+ "close",
35
+ @plan,
36
+ @current_user,
37
+ visibility: "public-only"
38
+ ) do
39
+ @plan.update closed_at: Time.current
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
+ # A command with all the business logic when a user reopens a plan.
6
+ class ReopenPlan < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # plan - The plan to publish.
10
+ # current_user - The current user.
11
+ def initialize(plan, current_user)
12
+ @plan = plan
13
+ @current_user = current_user
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid and the plan is published.
19
+ # - :invalid if the plan's author is not the current user.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ return broadcast(:invalid) if @current_user.nil?
24
+
25
+ close_plan
26
+
27
+ broadcast(:ok, @plan)
28
+ end
29
+
30
+ private
31
+
32
+ def close_plan
33
+ Decidim.traceability.perform_action!(
34
+ "reopen",
35
+ @plan,
36
+ @current_user,
37
+ visibility: "public-only"
38
+ ) do
39
+ @plan.update closed_at: nil
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -57,6 +57,38 @@ module Decidim
57
57
  end
58
58
  end
59
59
 
60
+ def close
61
+ enforce_permission_to :close, :plan, plan: plan
62
+
63
+ ClosePlan.call(plan, current_user) do
64
+ on(:ok) do
65
+ flash[:notice] = I18n.t("close.success", scope: "decidim.plans.plans.plan")
66
+ redirect_to plans_path
67
+ end
68
+
69
+ on(:invalid) do
70
+ flash.now[:alert] = t("close.error", scope: "decidim.plans.plans.plan")
71
+ redirect_to plans_path
72
+ end
73
+ end
74
+ end
75
+
76
+ def reopen
77
+ enforce_permission_to :close, :plan, plan: plan
78
+
79
+ ReopenPlan.call(plan, current_user) do
80
+ on(:ok) do
81
+ flash[:notice] = I18n.t("reopen.success", scope: "decidim.plans.plans.plan")
82
+ redirect_to plans_path
83
+ end
84
+
85
+ on(:invalid) do
86
+ flash.now[:alert] = t("reopen.error", scope: "decidim.plans.plans.plan")
87
+ redirect_to plans_path
88
+ end
89
+ end
90
+ end
91
+
60
92
  private
61
93
 
62
94
  def query
@@ -16,11 +16,6 @@ module Decidim
16
16
  @form = form(Admin::PlanSectionsForm).from_model(sections)
17
17
  end
18
18
 
19
- def new
20
- enforce_permission_to :create, :sections
21
- # TODO
22
- end
23
-
24
19
  def create
25
20
  enforce_permission_to :create, :section
26
21
  @form = form(Admin::PlanSectionsForm).from_params(params)
@@ -33,7 +28,7 @@ module Decidim
33
28
 
34
29
  on(:invalid) do
35
30
  flash.now[:alert] = I18n.t("update.invalid", scope: i18n_flashes_scope)
36
- render template: "index"
31
+ render action: :index
37
32
  end
38
33
  end
39
34
  end
@@ -8,6 +8,8 @@ module Decidim
8
8
  before_action :retrieve_plan, only: [:request_access, :request_accept, :request_reject]
9
9
 
10
10
  def request_access
11
+ enforce_permission_to :request_access, :plan, plan: @plan
12
+
11
13
  @request_access_form = form(RequestAccessToPlanForm).from_params(params)
12
14
  RequestAccessToPlan.call(@request_access_form, current_user) do
13
15
  on(:ok) do |_plan|
@@ -22,6 +24,8 @@ module Decidim
22
24
  end
23
25
 
24
26
  def request_accept
27
+ enforce_permission_to :edit, :plan, plan: @plan
28
+
25
29
  @accept_request_form = form(AcceptAccessToPlanForm).from_params(params)
26
30
  AcceptAccessToPlan.call(@accept_request_form, current_user) do
27
31
  on(:ok) do |requester_user|
@@ -36,6 +40,8 @@ module Decidim
36
40
  end
37
41
 
38
42
  def request_reject
43
+ enforce_permission_to :edit, :plan, plan: @plan
44
+
39
45
  @reject_request_form = form(RejectAccessToPlanForm).from_params(params)
40
46
  RejectAccessToPlan.call(@reject_request_form, current_user) do
41
47
  on(:ok) do |requester_user|
@@ -17,9 +17,9 @@ module Decidim
17
17
 
18
18
  helper_method :attached_proposals_picker_field
19
19
 
20
- before_action :authenticate_user!, only: [:new, :create, :edit, :update, :withdraw, :preview, :publish, :destroy]
20
+ before_action :authenticate_user!, only: [:new, :create, :edit, :update, :withdraw, :preview, :publish, :close, :reopen, :destroy]
21
21
  before_action :check_draft, only: [:new]
22
- before_action :retrieve_plan, only: [:show, :edit, :update, :withdraw, :preview, :publish, :destroy]
22
+ before_action :retrieve_plan, only: [:show, :edit, :update, :withdraw, :preview, :publish, :close, :reopen, :destroy]
23
23
  before_action :ensure_published!, only: [:show, :withdraw]
24
24
 
25
25
  def index
@@ -129,6 +129,8 @@ module Decidim
129
129
  def preview; end
130
130
 
131
131
  def publish
132
+ enforce_permission_to :publish, :plan, plan: @plan
133
+
132
134
  PublishPlan.call(@plan, current_user) do
133
135
  on(:ok) do |plan|
134
136
  flash[:notice] = I18n.t("publish.success", scope: "decidim.plans.plans.plan")
@@ -142,6 +144,38 @@ module Decidim
142
144
  end
143
145
  end
144
146
 
147
+ def close
148
+ enforce_permission_to :close, :plan, plan: @plan
149
+
150
+ ClosePlan.call(@plan, current_user) do
151
+ on(:ok) do |plan|
152
+ flash[:notice] = I18n.t("close.success", scope: "decidim.plans.plans.plan")
153
+ redirect_to Decidim::ResourceLocatorPresenter.new(plan).path
154
+ end
155
+
156
+ on(:invalid) do
157
+ flash.now[:alert] = t("close.error", scope: "decidim.plans.plans.plan")
158
+ redirect_to Decidim::ResourceLocatorPresenter.new(@plan).path
159
+ end
160
+ end
161
+ end
162
+
163
+ def reopen
164
+ enforce_permission_to :close, :plan, plan: @plan
165
+
166
+ ReopenPlan.call(@plan, current_user) do
167
+ on(:ok) do |plan|
168
+ flash[:notice] = I18n.t("reopen.success", scope: "decidim.plans.plans.plan")
169
+ redirect_to Decidim::ResourceLocatorPresenter.new(plan).path
170
+ end
171
+
172
+ on(:invalid) do
173
+ flash.now[:alert] = t("reopen.error", scope: "decidim.plans.plans.plan")
174
+ redirect_to Decidim::ResourceLocatorPresenter.new(@plan).path
175
+ end
176
+ end
177
+ end
178
+
145
179
  private
146
180
 
147
181
  def check_draft
@@ -149,12 +183,14 @@ module Decidim
149
183
  end
150
184
 
151
185
  def plan_draft
152
- Plan.drafts.from_all_author_identities(current_user).not_hidden.where(component: current_component)
186
+ @plan_draft ||= Plan.drafts.from_all_author_identities(current_user).not_hidden.find_by(component: current_component)
153
187
  end
154
188
 
189
+ # rubocop:disable Naming/MemoizedInstanceVariableName
155
190
  def retrieve_plan
156
- @plan = Plan.where(component: current_component).find(params[:id])
191
+ @plan ||= Plan.where(component: current_component).find(params[:id])
157
192
  end
193
+ # rubocop:enable Naming/MemoizedInstanceVariableName
158
194
 
159
195
  def ensure_published!
160
196
  return unless @plan
@@ -21,6 +21,8 @@ module Decidim
21
21
  end
22
22
 
23
23
  def item_versions
24
+ return [] if current_version.transaction_id.nil?
25
+
24
26
  # There may be multiple updates on the item during the same transaction
25
27
  Decidim::Plans::PaperTrail::Version.where(
26
28
  transaction_id: current_version.transaction_id,
@@ -29,6 +31,8 @@ module Decidim
29
31
  end
30
32
 
31
33
  def associated_versions
34
+ return [] if current_version.transaction_id.nil?
35
+
32
36
  @associated_versions ||= Decidim::Plans::PaperTrail::Version.where(
33
37
  transaction_id: current_version.transaction_id
34
38
  ).where.not(
@@ -37,6 +41,8 @@ module Decidim
37
41
  end
38
42
 
39
43
  def content_versions
44
+ return [] if current_version.transaction_id.nil?
45
+
40
46
  @content_versions ||= item.sections.map do |section|
41
47
  content = item.contents.find_by(section: section)
42
48
  next unless content
@@ -35,6 +35,8 @@ module Decidim
35
35
  .try(:resource_scope, current_component)
36
36
  &.order(title: :asc)
37
37
  &.where("title ilike ?", "%#{params[:term]}%")
38
+ &.where&.not(state: "rejected")
39
+ &.where&.not(published_at: nil)
38
40
  render json: query.all.collect { |p| [present(p).title, p.id] }
39
41
  end
40
42
  end
@@ -32,21 +32,17 @@ module Decidim
32
32
  private
33
33
 
34
34
  def renderer_for(version)
35
+ return nil if version.item.nil?
36
+
35
37
  locale = current_locale unless component_settings.multilingual_answers?
36
38
 
37
- renderer_klass =
38
- case version.item.class.name
39
- when "Decidim::Plans::Plan"
40
- Decidim::Plans::DiffRenderer::Plan
41
- when "Decidim::Plans::Content"
42
- Decidim::Plans::DiffRenderer::Content
43
- when "Decidim::Categorization"
44
- Decidim::Plans::DiffRenderer::Categorization
45
- when "Decidim::Component"
46
- Decidim::Plans::DiffRenderer::Component
47
- end
39
+ item_klass = version.item.class.name
40
+ lastpart = item_klass.split("::").last
41
+ renderer_klass = "Decidim::Plans::DiffRenderer::#{lastpart}"
42
+
43
+ return nil unless defined?(renderer_klass)
48
44
 
49
- renderer_klass.new(version, locale)
45
+ renderer_klass.constantize.new(version, locale)
50
46
  end
51
47
 
52
48
  # Renders the given value in a user-friendly way based on the value class.
@@ -99,6 +99,13 @@ module Decidim
99
99
  published_at.present?
100
100
  end
101
101
 
102
+ # Public: Checks if the plan has been closed or not.
103
+ #
104
+ # Returns Boolean.
105
+ def closed?
106
+ closed_at.present?
107
+ end
108
+
102
109
  # Public: Checks if the organization has given an answer for the plan.
103
110
  #
104
111
  # Returns Boolean.
@@ -191,6 +198,14 @@ module Decidim
191
198
  Arel.sql(query)
192
199
  end
193
200
 
201
+ def self.export_serializer
202
+ Decidim::Plans::PlanSerializer
203
+ end
204
+
205
+ def self.data_portability_images(user)
206
+ user_collection(user).map { |p| p.attachments.collect(&:file) }
207
+ end
208
+
194
209
  # Checks whether the plan is inside the time window to be editable or not once published.
195
210
  def within_edit_time_limit?
196
211
  true
@@ -23,6 +23,8 @@ module Decidim
23
23
  permission_action.allow!
24
24
  when :edit, :update, :destroy
25
25
  permission_action.allow! if plan.present? || section.present?
26
+ when :close
27
+ permission_action.allow! if plan.present?
26
28
  end
27
29
 
28
30
  permission_action
@@ -35,6 +35,8 @@ module Decidim
35
35
  can_withdraw_plan?
36
36
  when :publish
37
37
  can_publish_plan?
38
+ when :close
39
+ can_close_plan?
38
40
  when :request_access
39
41
  can_request_access_plan?
40
42
  end
@@ -45,7 +47,7 @@ module Decidim
45
47
  end
46
48
 
47
49
  def can_edit_plan?
48
- toggle_allow(plan.open? && plan.editable_by?(user))
50
+ toggle_allow(plan.open? && !plan.closed? && plan.editable_by?(user))
49
51
  end
50
52
 
51
53
  def can_withdraw_plan?
@@ -56,6 +58,10 @@ module Decidim
56
58
  toggle_allow(plan.open? && plan.editable_by?(user))
57
59
  end
58
60
 
61
+ def can_close_plan?
62
+ toggle_allow(plan && plan.created_by?(user))
63
+ end
64
+
59
65
  def can_request_access_plan?
60
66
  return toggle_allow(false) unless plan.open?
61
67
  return toggle_allow(false) if plan.editable_by?(user)
@@ -82,7 +82,7 @@ module Decidim
82
82
  )
83
83
  end
84
84
 
85
- def parse_i18n_changeset(attribute, values, type, diff)
85
+ def parse_i18n_changeset(attribute, values, _type, diff)
86
86
  values.last.each_key do |locale, _value|
87
87
  next if display_locale && display_locale != locale
88
88
 
@@ -5,5 +5,7 @@
5
5
  <% if allowed_to? :create, :plan %>
6
6
  <%= link_to t("actions.new", scope: "decidim.plans"), new_plan_path, class: "button tiny button--simple" %>
7
7
  <% end %>
8
+
9
+ <%= export_dropdown %>
8
10
  </div>
9
11
  </div>
@@ -40,6 +40,14 @@
40
40
  <%= icon_link_to "pencil", edit_plan_path(plan), t("actions.edit_plan", scope: "decidim.plans"), class: "action-icon--edit-plan" %>
41
41
  <% end %>
42
42
 
43
+ <% if allowed_to? :close, :plan, plan: plan %>
44
+ <% if plan.closed? %>
45
+ <%= icon_link_to "action-undo", reopen_plan_path(plan), t("actions.reopen_plan", scope: "decidim.plans"), method: :post, class: "action-icon--reopen-plan" %>
46
+ <% else %>
47
+ <%= icon_link_to "ban", close_plan_path(plan), t("actions.close_plan", scope: "decidim.plans"), method: :post, class: "action-icon--close-plan" %>
48
+ <% end %>
49
+ <% end %>
50
+
43
51
  <% if allowed_to? :create, :plan_answer %>
44
52
  <%= icon_link_to "comment-square", edit_plan_plan_answer_path(plan_id: plan.id, id: plan.id), t("actions.answer", scope: "decidim.plans"), class: " icon--small" %>
45
53
  <% else %>
@@ -45,6 +45,13 @@
45
45
  <% if allowed_to?(:edit, :plan, plan: @plan) %>
46
46
  <%= link_to t("edit", scope:"decidim.plans.plans.show"), edit_plan_path(@plan), class: "button secondary hollow expanded button--sc mt-s", id: "plan_edit" %>
47
47
  <% end %>
48
+ <% if allowed_to?(:close, :plan, plan: @plan) %>
49
+ <% if @plan.closed? %>
50
+ <%= link_to t("reopen", scope:"decidim.plans.plans.show"), reopen_plan_path(@plan), method: :post, class: "button secondary hollow expanded button--sc mt-s", id: "plan_reopen", data: { confirm: t("reopen_confirm", scope:"decidim.plans.plans.show") } %>
51
+ <% else %>
52
+ <%= link_to t("close", scope:"decidim.plans.plans.show"), close_plan_path(@plan), method: :post, class: "button secondary hollow expanded button--sc mt-s", id: "plan_close", data: { confirm: t("close_confirm", scope:"decidim.plans.plans.show") } %>
53
+ <% end %>
54
+ <% end %>
48
55
 
49
56
  <%= render "request_access_form" %>
50
57
 
@@ -1,11 +1,15 @@
1
1
  ---
2
2
  en:
3
+ activerecord:
4
+ models:
5
+ decidim/plans/plan: Plan
3
6
  activemodel:
4
7
  attributes:
5
8
  plan:
6
9
  proposals: Proposals
7
10
  category_id: Category
8
11
  decidim_category_id: Category
12
+ decidim_component_id: Component
9
13
  decidim_scope_id: Scope
10
14
  state: State
11
15
  title: Title
@@ -39,19 +43,27 @@ en:
39
43
  comments_blocked: Comments blocked
40
44
  creation_enabled: Creation enabled
41
45
  plan_answering_enabled: Answering enabled
46
+ pages:
47
+ home:
48
+ statistics:
49
+ plans_count: Plans
42
50
  participatory_processes:
43
51
  statistics:
44
52
  plans_count: Plans
45
53
  plans:
46
54
  actions:
47
55
  answer: Answer
56
+ close_plan: Mark as done
48
57
  edit_plan: Edit
49
58
  new: Submit new plan
59
+ reopen_plan: Open for editing
50
60
  sections: Sections
51
61
  title: Actions
52
62
  admin:
53
63
  actions:
54
64
  preview: Preview
65
+ exports:
66
+ plans: Plans
55
67
  plan_answers:
56
68
  edit:
57
69
  accepted: Accepted
@@ -207,15 +219,24 @@ en:
207
219
  random: Random
208
220
  recent: Recent
209
221
  plan:
222
+ close:
223
+ error: Error marking the item as done.
224
+ success: Item marked done successfully.
210
225
  publish:
211
226
  error: Error publishing new content.
212
227
  success: Published successfully.
228
+ reopen:
229
+ error: Error opening item for editing.
230
+ success: Item opened for editing successfully.
213
231
  view_plan: View plan
214
232
  withdraw:
215
233
  error: Error withdrawing.
216
234
  success: Withdrawn successfully.
217
235
  show:
218
236
  back: Back
237
+ close: Mark as done
238
+ close_confirm: Are you sure you want to mark this item done? After
239
+ marking done, the item can no longer be edited.
219
240
  edit: Edit
220
241
  hidden_authors_count:
221
242
  one: and %{count} more person
@@ -229,6 +250,8 @@ en:
229
250
  publish: Publish
230
251
  publish_info: Publish this version of the draft or
231
252
  published_proposal: published plan
253
+ reopen: Open for editing
254
+ reopen_confirm: Are you sure you want to open this item for editing?
232
255
  report: Report
233
256
  request_access: Request editing permissions
234
257
  requested_access: Editing permissions requested
@@ -1,11 +1,15 @@
1
1
  ---
2
2
  fi:
3
+ activerecord:
4
+ models:
5
+ decidim/plans/plan: Suunnitelma
3
6
  activemodel:
4
7
  attributes:
5
8
  plan:
6
9
  proposals: Ehdotukset
7
10
  category_id: Kategoria
8
11
  decidim_category_id: Kategoria
12
+ decidim_component_id: Komponentti
9
13
  decidim_scope_id: Teema
10
14
  state: Tila
11
15
  title: Otsikko
@@ -39,19 +43,27 @@ fi:
39
43
  comments_blocked: Kommentointi estetty
40
44
  creation_enabled: Luonti sallittu
41
45
  plan_answering_enabled: Vastaaminen sallittu
46
+ pages:
47
+ home:
48
+ statistics:
49
+ plans_count: Suunnitelmia
42
50
  participatory_processes:
43
51
  statistics:
44
52
  plans_count: Suunnitelmaa
45
53
  plans:
46
54
  actions:
47
55
  answer: Vastaa
56
+ close_plan: Merkitse valmiiksi
48
57
  edit_plan: Muokkaa
49
58
  new: Tee uusi suunnitelma
59
+ reopen_plan: Avaa muokattavaksi
50
60
  sections: Osiot
51
61
  title: Toiminnot
52
62
  admin:
53
63
  actions:
54
64
  preview: Esikatsele
65
+ exports:
66
+ plans: Suunnitelmat
55
67
  plan_answers:
56
68
  edit:
57
69
  accepted: Hyväksytty
@@ -207,15 +219,24 @@ fi:
207
219
  random: Satunnainen
208
220
  recent: Uusimmat
209
221
  plan:
222
+ close:
223
+ error: Valmiiksi merkitseminen epäonnistui.
224
+ success: Valmiiksi merkitseminen onnistui.
210
225
  publish:
211
226
  error: Uuden sisällön julkaiseminen epäonnistui.
212
227
  success: Julkaisu onnistui.
228
+ reopen:
229
+ error: Muokattavaksi avaaminen epäonnistui.
230
+ success: Muokattavaksi avaaminen onnistui.
213
231
  view_plan: Näytä suunnitelma
214
232
  withdraw:
215
233
  error: Peruuttaminen epäonnistui.
216
234
  success: Peruuttaminen onnistui.
217
235
  show:
218
236
  back: Takaisin
237
+ close: Merkitse valmiiksi
238
+ close_confirm: Haluatko varmasti merkitä tämän kohteen valmiiksi? Kun
239
+ kohde on merkitty valmiiksi, sitä ei voi enää muokata.
219
240
  edit: Muokkaa
220
241
  hidden_authors_count:
221
242
  one: ja %{count} muu henkilö
@@ -229,6 +250,8 @@ fi:
229
250
  publish: Julkaise
230
251
  publish_info: Julkaise tämä luonnosversio tai
231
252
  published_proposal: julkaistu suunnitelma
253
+ reopen: Avaa muokattavaksi
254
+ reopen_confirm: Haluatko varmasti avata tämän kohteen muokattavaksi?
232
255
  report: Ilmoita
233
256
  request_access: Pyydä muokkausoikeudet
234
257
  requested_access: Muokkausoikeudet pyydetty
@@ -1,11 +1,16 @@
1
1
  ---
2
2
  sv:
3
+ activerecord:
4
+ models:
5
+ decidim/plans/plan: Plan
3
6
  activemodel:
4
7
  attributes:
5
8
  plan:
6
9
  proposals: Proposals
7
10
  category_id: Category
8
11
  decidim_category_id: Category
12
+ decidim_component_id: Component
13
+ decidim_proposal_id: Proposal
9
14
  decidim_scope_id: Scope
10
15
  state: State
11
16
  title: Title
@@ -39,19 +44,27 @@ sv:
39
44
  comments_blocked: Comments blocked
40
45
  creation_enabled: Creation enabled
41
46
  plan_answering_enabled: Answering enabled
47
+ pages:
48
+ home:
49
+ statistics:
50
+ plans_count: Plans
42
51
  participatory_processes:
43
52
  statistics:
44
53
  plans_count: Plans
45
54
  plans:
46
55
  actions:
47
56
  answer: Answer
57
+ close_plan: Mark as done
48
58
  edit_plan: Edit
49
59
  new: Submit new plan
60
+ reopen_plan: Open for editing
50
61
  sections: Sections
51
62
  title: Actions
52
63
  admin:
53
64
  actions:
54
65
  preview: Preview
66
+ exports:
67
+ plans: Plans
55
68
  plan_answers:
56
69
  edit:
57
70
  accepted: Accepted
@@ -207,15 +220,24 @@ sv:
207
220
  random: Random
208
221
  recent: Recent
209
222
  plan:
223
+ close:
224
+ error: Error marking the item as done.
225
+ success: Item marked done successfully.
210
226
  publish:
211
227
  error: Error publishing new content.
212
228
  success: Published successfully.
229
+ reopen:
230
+ error: Error opening item for editing.
231
+ success: Item opened for editing successfully.
213
232
  view_plan: View plan
214
233
  withdraw:
215
234
  error: Error withdrawing.
216
235
  success: Withdrawn successfully.
217
236
  show:
218
237
  back: Back
238
+ close: Mark as done
239
+ close_confirm: Are you sure you want to mark this item done? After
240
+ marking done, the item can no longer be edited.
219
241
  edit: Edit
220
242
  hidden_authors_count:
221
243
  one: and %{count} more person
@@ -229,6 +251,8 @@ sv:
229
251
  publish: Publish
230
252
  publish_info: Publish this version of the draft or
231
253
  published_proposal: published plan
254
+ reopen: Open for editing
255
+ reopen_confirm: Are you sure you want to open this item for editing?
232
256
  report: Report
233
257
  request_access: Request editing permissions
234
258
  requested_access: Editing permissions requested
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddClosedAtToDecidimPlans < ActiveRecord::Migration[5.2]
4
+ def change
5
+ add_column :decidim_plans_plans, :closed_at, :datetime
6
+ add_index :decidim_plans_plans, :closed_at
7
+ end
8
+ end
@@ -13,6 +13,10 @@ module Decidim
13
13
  resources :plans, only: [:index, :new, :create, :edit, :update] do
14
14
  get :search_proposals
15
15
  resources :plan_answers, only: [:edit, :update]
16
+ member do
17
+ post :close
18
+ post :reopen
19
+ end
16
20
  end
17
21
  resources :sections, only: [:index, :new, :create, :edit, :update]
18
22
 
@@ -13,7 +13,7 @@ Decidim.register_component(:plans) do |component|
13
13
 
14
14
  component.data_portable_entities = ["Decidim::Plans::Plan"]
15
15
 
16
- component.actions = %w(create withdraw)
16
+ component.actions = %w(create withdraw close reopen)
17
17
 
18
18
  component.permissions_class_name = "Decidim::Plans::Permissions"
19
19
 
@@ -23,7 +23,7 @@ Decidim.register_component(:plans) do |component|
23
23
  settings.attribute :comments_enabled, type: :boolean, default: true
24
24
  settings.attribute :announcement, type: :text, translated: true, editor: true
25
25
  settings.attribute :attachments_allowed, type: :boolean, default: false
26
- settings.attribute :multilingual_answers, type: :boolean, default: true
26
+ settings.attribute :multilingual_answers, type: :boolean
27
27
  end
28
28
 
29
29
  component.settings(:step) do |settings|
@@ -51,6 +51,19 @@ Decidim.register_component(:plans) do |component|
51
51
  Decidim::Comments::Comment.where(root_commentable: plans).count
52
52
  end
53
53
 
54
+ component.exports :plans do |exports|
55
+ exports.collection do |component_instance|
56
+ Decidim::Plans::Plan
57
+ .published
58
+ .where(component: component_instance)
59
+ .includes(:category, component: { participatory_space: :organization })
60
+ end
61
+
62
+ exports.include_in_open_data = true
63
+
64
+ exports.serializer Decidim::Plans::PlanSerializer
65
+ end
66
+
54
67
  component.seeds do |participatory_space|
55
68
  admin_user = Decidim::User.find_by(
56
69
  organization: participatory_space.organization,
@@ -69,7 +82,7 @@ Decidim.register_component(:plans) do |component|
69
82
  published_at: Time.current,
70
83
  participatory_space: participatory_space,
71
84
  settings: {
72
- multilingual_answers: true
85
+ multilingual_answers: false
73
86
  },
74
87
  step_settings: step_settings
75
88
  }
@@ -13,6 +13,8 @@ module Decidim
13
13
  member do
14
14
  get :preview
15
15
  post :publish
16
+ post :close
17
+ post :reopen
16
18
  put :withdraw
17
19
  post :request_access, controller: "plan_collaborator_requests"
18
20
  post :request_accept, controller: "plan_collaborator_requests"
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Plans
5
+ # This class serializes a Proposal so can be exported to CSV, JSON or other
6
+ # formats.
7
+ class PlanSerializer < Decidim::Exporters::Serializer
8
+ include Decidim::ApplicationHelper
9
+ include Decidim::ResourceHelper
10
+
11
+ # Public: Initializes the serializer with a plan.
12
+ def initialize(plan)
13
+ @plan = plan
14
+ end
15
+
16
+ # Public: Exports a hash with the serialized data for this proposal.
17
+ def serialize
18
+ values = {
19
+ id: plan.id,
20
+ category: {
21
+ id: plan.category.try(:id),
22
+ name: plan.category.try(:name)
23
+ },
24
+ scope: {
25
+ id: plan.scope.try(:id),
26
+ name: plan.scope.try(:name)
27
+ },
28
+ participatory_space: {
29
+ id: plan.participatory_space.id,
30
+ url: Decidim::ResourceLocatorPresenter.new(plan.participatory_space).url
31
+ },
32
+ component: { id: component.id },
33
+ state: plan.state.to_s,
34
+ comments: plan.comments.count,
35
+ attachments: plan.attachments.count,
36
+ followers: plan.followers.count,
37
+ published_at: plan.published_at,
38
+ closed_at: plan.closed_at,
39
+ url: url,
40
+ related_proposals: {
41
+ ids: related_proposal_ids,
42
+ urls: related_proposal_urls
43
+ },
44
+ title: plan.title
45
+ }
46
+
47
+ # Add section content
48
+ plan.sections.each do |sect|
49
+ content = plan.contents.find_by(section: sect)
50
+ values["section_#{sect.id}".to_sym] = {
51
+ title: sect.body,
52
+ value: content.try(:body)
53
+ }
54
+ end
55
+
56
+ values
57
+ end
58
+
59
+ private
60
+
61
+ attr_reader :plan
62
+
63
+ def component
64
+ plan.component
65
+ end
66
+
67
+ def related_proposal_ids
68
+ plan.proposals.map(&:id)
69
+ end
70
+
71
+ def related_proposal_urls
72
+ plan.proposals.map do |proposal|
73
+ Decidim::ResourceLocatorPresenter.new(proposal).url
74
+ end
75
+ end
76
+
77
+ def url
78
+ Decidim::ResourceLocatorPresenter.new(plan).url
79
+ end
80
+ end
81
+ end
82
+ end
@@ -41,10 +41,10 @@ FactoryBot.define do
41
41
  end
42
42
  end
43
43
 
44
- trait :with_single_language_answers do
44
+ trait :with_multilingual_answers do
45
45
  settings do
46
46
  {
47
- multilingual_answers: false
47
+ multilingual_answers: true
48
48
  }
49
49
  end
50
50
  end
@@ -89,6 +89,13 @@ FactoryBot.define do
89
89
  published_at { nil }
90
90
  end
91
91
 
92
+ trait :official do
93
+ after :build do |plan|
94
+ plan.coauthorships.clear
95
+ plan.coauthorships.build(author: plan.organization)
96
+ end
97
+ end
98
+
92
99
  trait :evaluating do
93
100
  state { "evaluating" }
94
101
  answered_at { Time.current }
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module Plans
5
- VERSION = "0.16.0"
5
+ VERSION = "0.16.1"
6
6
  DECIDIM_VERSION = "~> 0.16.0"
7
7
  end
8
8
  end
data/lib/decidim/plans.rb CHANGED
@@ -6,11 +6,13 @@ require_relative "plans/engine"
6
6
  require_relative "plans/admin_engine"
7
7
  require_relative "plans/paper_trail"
8
8
  require_relative "plans/component"
9
- require_relative "plans/locale_aware"
10
- require_relative "plans/optionally_translatable_attributes"
11
9
 
12
10
  module Decidim
13
11
  module Plans
12
+ autoload :LocaleAware, "decidim/plans/locale_aware"
13
+ autoload :OptionallyTranslatableAttributes, "decidim/plans/optionally_translatable_attributes"
14
+ autoload :PlanSerializer, "decidim/plans/plan_serializer"
15
+
14
16
  # Public: Stores an instance of Loggability
15
17
  def self.loggability
16
18
  @loggability ||= Loggability.new
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decidim-plans
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.16.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Antti Hukkanen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-06 00:00:00.000000000 Z
11
+ date: 2019-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: decidim-core
@@ -151,11 +151,13 @@ files:
151
151
  - app/commands/decidim/plans/admin/update_plan.rb
152
152
  - app/commands/decidim/plans/admin/update_sections.rb
153
153
  - app/commands/decidim/plans/attachment_methods.rb
154
+ - app/commands/decidim/plans/close_plan.rb
154
155
  - app/commands/decidim/plans/create_plan.rb
155
156
  - app/commands/decidim/plans/destroy_plan.rb
156
157
  - app/commands/decidim/plans/nested_updater.rb
157
158
  - app/commands/decidim/plans/publish_plan.rb
158
159
  - app/commands/decidim/plans/reject_access_to_plan.rb
160
+ - app/commands/decidim/plans/reopen_plan.rb
159
161
  - app/commands/decidim/plans/request_access_to_plan.rb
160
162
  - app/commands/decidim/plans/respond_to_access_request.rb
161
163
  - app/commands/decidim/plans/update_plan.rb
@@ -286,6 +288,7 @@ files:
286
288
  - db/migrate/20190202123044_create_version_associations.rb
287
289
  - db/migrate/20190202123045_add_transaction_id_column_to_versions.rb
288
290
  - db/migrate/20190202200716_add_update_token_to_decidim_plans.rb
291
+ - db/migrate/20190214124014_add_closed_at_to_decidim_plans.rb
289
292
  - lib/decidim/plans.rb
290
293
  - lib/decidim/plans/admin.rb
291
294
  - lib/decidim/plans/admin_engine.rb
@@ -295,6 +298,7 @@ files:
295
298
  - lib/decidim/plans/optionally_translatable_attributes.rb
296
299
  - lib/decidim/plans/paper_trail.rb
297
300
  - lib/decidim/plans/paper_trail/record_trail.rb
301
+ - lib/decidim/plans/plan_serializer.rb
298
302
  - lib/decidim/plans/test/factories.rb
299
303
  - lib/decidim/plans/version.rb
300
304
  homepage: https://github.com/mainio/decidim-module-plans