decidim-proposals 0.16.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +3 -1
  3. data/app/cells/decidim/proposals/proposal_activity_cell.rb +9 -1
  4. data/app/commands/decidim/proposals/admin/discard_participatory_text.rb +40 -0
  5. data/app/commands/decidim/proposals/admin/import_participatory_text.rb +5 -1
  6. data/app/commands/decidim/proposals/admin/publish_participatory_text.rb +6 -18
  7. data/app/commands/decidim/proposals/admin/update_participatory_text.rb +56 -0
  8. data/app/commands/decidim/proposals/create_proposal.rb +21 -22
  9. data/app/commands/decidim/proposals/publish_collaborative_draft.rb +1 -0
  10. data/app/commands/decidim/proposals/publish_proposal.rb +25 -3
  11. data/app/commands/decidim/proposals/update_proposal.rb +19 -23
  12. data/app/controllers/concerns/decidim/proposals/orderable.rb +1 -1
  13. data/app/controllers/decidim/proposals/admin/participatory_texts_controller.rb +46 -14
  14. data/app/forms/decidim/proposals/admin/import_participatory_text_form.rb +1 -2
  15. data/app/forms/decidim/proposals/admin/proposal_form.rb +1 -1
  16. data/app/helpers/decidim/proposals/application_helper.rb +0 -4
  17. data/app/permissions/decidim/proposals/admin/permissions.rb +2 -4
  18. data/app/views/decidim/proposals/admin/participatory_texts/_article-preview.html.erb +1 -1
  19. data/app/views/decidim/proposals/admin/participatory_texts/_bulk-actions.html.erb +1 -0
  20. data/app/views/decidim/proposals/admin/participatory_texts/index.html.erb +8 -5
  21. data/app/views/decidim/proposals/proposals/show.html.erb +9 -0
  22. data/config/locales/ar-SA.yml +667 -0
  23. data/config/locales/ca.yml +28 -21
  24. data/config/locales/cs-CZ.yml +775 -0
  25. data/config/locales/de.yml +7 -0
  26. data/config/locales/en.yml +7 -0
  27. data/config/locales/es-MX.yml +751 -0
  28. data/config/locales/es-PY.yml +7 -0
  29. data/config/locales/es.yml +21 -14
  30. data/config/locales/eu.yml +10 -3
  31. data/config/locales/fi-pl.yml +8 -1
  32. data/config/locales/fi.yml +11 -4
  33. data/config/locales/fr.yml +7 -0
  34. data/config/locales/gl.yml +7 -0
  35. data/config/locales/hu.yml +21 -14
  36. data/config/locales/id-ID.yml +7 -0
  37. data/config/locales/it.yml +8 -1
  38. data/config/locales/nl.yml +10 -3
  39. data/config/locales/pl.yml +8 -1
  40. data/config/locales/pt-BR.yml +7 -0
  41. data/config/locales/pt.yml +7 -0
  42. data/config/locales/ru.yml +6 -0
  43. data/config/locales/sv.yml +7 -0
  44. data/config/locales/tr-TR.yml +8 -0
  45. data/config/locales/uk.yml +6 -0
  46. data/db/migrate/20190215113158_use_md5_indexes.rb +17 -0
  47. data/lib/decidim/content_parsers/proposal_parser.rb +2 -2
  48. data/lib/decidim/proposals/admin_engine.rb +4 -2
  49. data/lib/decidim/proposals/markdown_to_proposals.rb +49 -4
  50. data/lib/decidim/proposals/proposal_serializer.rb +3 -2
  51. data/lib/decidim/proposals/version.rb +1 -1
  52. metadata +26 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1eb9355480e7b5f78b9eb01ede6e1e51362cf77f991b45d43944fbe3b52eece1
4
- data.tar.gz: c44373543399ee2bd9c95d5d47ba89d209cd9033c899ef7cb614166a531e4b4d
3
+ metadata.gz: f545c292413c69159509938ffa3e0a7a442eaec96770e465b405d709ab0db976
4
+ data.tar.gz: 90b940f6ce3c968772548d40a04a7c47d7491c340c2a407b78a6013ccdf903c8
5
5
  SHA512:
6
- metadata.gz: 5e4582447ccb88226799caaa32bedcf851e81af9ed927c1a7a4adabc9dedd8341438d45ce735cc471998e018119f597c7cb577b7e938d575f3b96de6a34bc1a9
7
- data.tar.gz: 985e3a9309033dbceed04819a85796c8faaf379c6e845420ca9884a2b545464025c5d195dd03659ed6fc571622b751462bda1b9f9de519c99ac887eb8d9844d6
6
+ metadata.gz: e12c387014c567aee0ca73e133357c74621e49dcd49b305ecda4bac2e231e215273d15b30a7d3e0e61772ba6dea2163b3baaf459781aa4a595ba100dcefcf086
7
+ data.tar.gz: 7dde2bc4a1920813acf8fee2bde644f1d7b80b58c056bab12f720ff2df246e376b401e83418d6a51475ba4190e1147f3beadb651a6be12bf90216fc38e5b5c3c
@@ -10,6 +10,7 @@ module Decidim
10
10
  include ProposalCellsHelper
11
11
  include Cell::ViewModel::Partial
12
12
  include Messaging::ConversationHelper
13
+ include Decidim::SanitizeHelper
13
14
 
14
15
  delegate :current_organization, to: :controller
15
16
 
@@ -30,7 +31,8 @@ module Decidim
30
31
 
31
32
  def body
32
33
  return unless model.participatory_text_level == "article"
33
- present(model).body
34
+ formatted = simple_format(present(model).body)
35
+ decidim_sanitize(strip_links(formatted))
34
36
  end
35
37
 
36
38
  def current_user
@@ -12,7 +12,15 @@ module Decidim
12
12
  end
13
13
 
14
14
  def resource_link_text
15
- Decidim::Proposals::ProposalPresenter.new(resource).title
15
+ presenter.title
16
+ end
17
+
18
+ def description
19
+ presenter.body(links: true)
20
+ end
21
+
22
+ def presenter
23
+ @presenter ||= Decidim::Proposals::ProposalPresenter.new(resource)
16
24
  end
17
25
  end
18
26
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ # A command with all the business logic related with an admin discarding participatory text proposals.
7
+ class DiscardParticipatoryText < Rectify::Command
8
+ # Public: Initializes the command.
9
+ #
10
+ # form - A PreviewParticipatoryTextForm form object with the params.
11
+ def initialize(component)
12
+ @component = component
13
+ end
14
+
15
+ # Executes the command. Broadcasts these events:
16
+ #
17
+ # - :ok when everything is valid.
18
+ # - :invalid if the form wasn't valid and we couldn't proceed.
19
+ #
20
+ # Returns nothing.
21
+ def call
22
+ transaction do
23
+ discard_drafts
24
+ end
25
+
26
+ broadcast(:ok)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :form
32
+
33
+ def discard_drafts
34
+ proposals = Decidim::Proposals::Proposal.drafts.where(component: @component)
35
+ proposals.destroy_all
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -39,7 +39,11 @@ module Decidim
39
39
  end
40
40
 
41
41
  def parse_participatory_text_doc(form)
42
- markdown = DocToMarkdown.new(form.document_text, form.document_type).to_md
42
+ return if form.document.blank?
43
+
44
+ document = form.document_text
45
+ document = Decidim::IoEncoder.to_standard_encoding(document)
46
+ markdown = DocToMarkdown.new(document, form.document_type).to_md
43
47
  parser = MarkdownToProposals.new(form.current_component, form.current_user)
44
48
  parser.parse(markdown)
45
49
  end
@@ -5,7 +5,7 @@ module Decidim
5
5
  module Admin
6
6
  # A command with all the business logic when an admin imports proposals from
7
7
  # a participatory text.
8
- class PublishParticipatoryText < Rectify::Command
8
+ class PublishParticipatoryText < UpdateParticipatoryText
9
9
  # Public: Initializes the command.
10
10
  #
11
11
  # form - A PreviewParticipatoryTextForm form object with the params.
@@ -21,13 +21,13 @@ module Decidim
21
21
  # Returns nothing.
22
22
  def call
23
23
  transaction do
24
- @publish_failures = {}
24
+ @failures = {}
25
25
  update_contents_and_resort_proposals(form)
26
26
  publish_drafts
27
27
  end
28
28
 
29
- if @publish_failures.any?
30
- broadcast(:invalid, @publish_failures)
29
+ if @failures.any?
30
+ broadcast(:invalid, @failures)
31
31
  else
32
32
  broadcast(:ok)
33
33
  end
@@ -37,27 +37,15 @@ module Decidim
37
37
 
38
38
  attr_reader :form
39
39
 
40
- def update_contents_and_resort_proposals(form)
41
- form.proposals.each do |prop_form|
42
- proposal = Decidim::Proposals::Proposal.where(component: form.current_component).find(prop_form.id)
43
- proposal.set_list_position(prop_form.position) if proposal.position != prop_form.position
44
- proposal.title = prop_form.title
45
- proposal.body = prop_form.body if proposal.participatory_text_level == Decidim::Proposals::ParticipatoryTextSection::LEVELS[:article]
46
-
47
- add_failure(proposal) unless proposal.save
48
- end
49
- raise ActiveRecord::Rollback if @publish_failures.any?
50
- end
51
-
52
40
  def publish_drafts
53
41
  Decidim::Proposals::Proposal.where(component: form.current_component).drafts.find_each do |proposal|
54
42
  add_failure(proposal) unless proposal.update(published_at: Time.current)
55
43
  end
56
- raise ActiveRecord::Rollback if @publish_failures.any?
44
+ raise ActiveRecord::Rollback if @failures.any?
57
45
  end
58
46
 
59
47
  def add_failure(proposal)
60
- @publish_failures[proposal.id] = proposal.errors.full_messages
48
+ @failures[proposal.id] = proposal.errors.full_messages
61
49
  end
62
50
  end
63
51
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ # A command with all the business logic when an admin updates participatory text proposals.
7
+ class UpdateParticipatoryText < Rectify::Command
8
+ # Public: Initializes the command.
9
+ #
10
+ # form - A PreviewParticipatoryTextForm 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.
18
+ # - :invalid if the form wasn't valid and we couldn't proceed.
19
+ #
20
+ # Returns nothing.
21
+ def call
22
+ transaction do
23
+ @failures = {}
24
+ update_contents_and_resort_proposals(form)
25
+ end
26
+
27
+ if @failures.any?
28
+ broadcast(:invalid, @failures)
29
+ else
30
+ broadcast(:ok)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :form
37
+
38
+ def update_contents_and_resort_proposals(form)
39
+ form.proposals.each do |prop_form|
40
+ proposal = Decidim::Proposals::Proposal.where(component: form.current_component).find(prop_form.id)
41
+ proposal.set_list_position(prop_form.position) if proposal.position != prop_form.position
42
+ proposal.title = prop_form.title
43
+ proposal.body = prop_form.body if proposal.participatory_text_level == Decidim::Proposals::ParticipatoryTextSection::LEVELS[:article]
44
+
45
+ add_failure(proposal) unless proposal.save
46
+ end
47
+ raise ActiveRecord::Rollback if @failures.any?
48
+ end
49
+
50
+ def add_failure(proposal)
51
+ @failures[proposal.id] = proposal.errors.full_messages
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -43,29 +43,28 @@ module Decidim
43
43
 
44
44
  attr_reader :form, :proposal, :attachment
45
45
 
46
- def proposal_attributes
47
- fields = {}
48
-
49
- fields[:title] = title_with_hashtags
50
- fields[:body] = body_with_hashtags
51
- fields[:component] = form.component
52
-
53
- fields
54
- end
55
-
56
- # This will be the PaperTrail version that is
57
- # shown in the version control feature (1 of 1)
46
+ # Prevent PaperTrail from creating an additional version
47
+ # in the proposal multi-step creation process (step 1: create)
48
+ #
49
+ # A first version will be created in step 4: publish
50
+ # for diff rendering in the proposal version control
58
51
  def create_proposal
59
- @proposal = Decidim.traceability.perform_action!(
60
- :create,
61
- Decidim::Proposals::Proposal,
62
- @current_user,
63
- visibility: "public-only"
64
- ) do
65
- proposal = Proposal.new(proposal_attributes)
66
- proposal.add_coauthor(@current_user, user_group: user_group)
67
- proposal.save!
68
- proposal
52
+ PaperTrail.request(enabled: false) do
53
+ @proposal = Decidim.traceability.perform_action!(
54
+ :create,
55
+ Decidim::Proposals::Proposal,
56
+ @current_user,
57
+ visibility: "public-only"
58
+ ) do
59
+ proposal = Proposal.new(
60
+ title: title_with_hashtags,
61
+ body: body_with_hashtags,
62
+ component: form.component
63
+ )
64
+ proposal.add_coauthor(@current_user, user_group: user_group)
65
+ proposal.save!
66
+ proposal
67
+ end
69
68
  end
70
69
  end
71
70
 
@@ -79,6 +79,7 @@ module Decidim
79
79
  new_proposal = Proposal.new(proposal_attributes)
80
80
  new_proposal.coauthorships = @collaborative_draft.coauthorships
81
81
  new_proposal.category = @collaborative_draft.category
82
+ new_proposal.attachments = @collaborative_draft.attachments
82
83
  new_proposal.save!
83
84
  new_proposal
84
85
  end
@@ -34,12 +34,34 @@ module Decidim
34
34
 
35
35
  private
36
36
 
37
- # Prevent PaperTrail from creating an additional version
38
- # in the proposal multi-step creation process (step 4: publish)
37
+ # This will be the PaperTrail version that is
38
+ # shown in the version control feature (1 of 1)
39
+ #
40
+ # For an attribute to appear in the new version it has to be reset
41
+ # and reassigned, as PaperTrail only keeps track of object CHANGES.
39
42
  def publish_proposal
43
+ title = reset(:title)
44
+ body = reset(:body)
45
+
46
+ Decidim.traceability.perform_action!(
47
+ "publish",
48
+ @proposal,
49
+ @current_user,
50
+ visibility: "public-only"
51
+ ) do
52
+ @proposal.update title: title, body: body, published_at: Time.current
53
+ end
54
+ end
55
+
56
+ # Reset the attribute to an empty string and return the old value
57
+ def reset(attribute)
58
+ attribute_value = @proposal[attribute]
40
59
  PaperTrail.request(enabled: false) do
41
- @proposal.update published_at: Time.current
60
+ # rubocop:disable Rails/SkipsModelValidations
61
+ @proposal.update_attribute attribute, ""
62
+ # rubocop:enable Rails/SkipsModelValidations
42
63
  end
64
+ attribute_value
43
65
  end
44
66
 
45
67
  def send_notification
@@ -53,41 +53,37 @@ module Decidim
53
53
 
54
54
  attr_reader :form, :proposal, :current_user, :attachment
55
55
 
56
- def proposal_attributes
57
- fields = {}
58
-
59
- fields[:title] = title_with_hashtags
60
- fields[:body] = body_with_hashtags
61
- fields[:category] = form.category
62
- fields[:scope] = form.scope
63
- fields[:address] = form.address
64
- fields[:latitude] = form.latitude
65
- fields[:longitude] = form.longitude
66
-
67
- fields
68
- end
69
-
70
56
  # Prevent PaperTrail from creating an additional version
71
57
  # in the proposal multi-step creation process (step 3: complete)
58
+ #
59
+ # A first version will be created in step 4: publish
60
+ # for diff rendering in the proposal control version
72
61
  def update_draft
73
62
  PaperTrail.request(enabled: false) do
74
- @proposal.update(proposal_attributes)
63
+ @proposal.update(attributes)
75
64
  @proposal.coauthorships.clear
76
65
  @proposal.add_coauthor(current_user, user_group: user_group)
77
66
  end
78
67
  end
79
68
 
80
69
  def update_proposal
81
- @proposal = Decidim.traceability.update!(
82
- @proposal,
83
- current_user,
84
- proposal_attributes,
85
- visibility: "public-only"
86
- )
70
+ @proposal.update!(attributes)
87
71
  @proposal.coauthorships.clear
88
72
  @proposal.add_coauthor(current_user, user_group: user_group)
89
73
  end
90
74
 
75
+ def attributes
76
+ {
77
+ title: title_with_hashtags,
78
+ body: body_with_hashtags,
79
+ category: form.category,
80
+ scope: form.scope,
81
+ address: form.address,
82
+ latitude: form.latitude,
83
+ longitude: form.longitude
84
+ }
85
+ end
86
+
91
87
  def proposal_limit_reached?
92
88
  proposal_limit = form.current_component.settings.proposal_limit
93
89
 
@@ -109,11 +105,11 @@ module Decidim
109
105
  end
110
106
 
111
107
  def current_user_proposals
112
- Proposal.from_author(current_user).where(component: form.current_component).published.where.not(id: proposal.id)
108
+ Proposal.from_author(current_user).where(component: form.current_component).published.where.not(id: proposal.id).except_withdrawn
113
109
  end
114
110
 
115
111
  def user_group_proposals
116
- Proposal.from_user_group(user_group).where(component: form.current_component).published.where.not(id: proposal.id)
112
+ Proposal.from_user_group(user_group).where(component: form.current_component).published.where.not(id: proposal.id).except_withdrawn
117
113
  end
118
114
  end
119
115
  end
@@ -59,7 +59,7 @@ module Decidim
59
59
  when "most_voted"
60
60
  proposals.order(proposal_votes_count: :desc)
61
61
  when "recent"
62
- proposals.order(created_at: :desc)
62
+ proposals.order(published_at: :desc)
63
63
  end
64
64
  end
65
65
  end
@@ -15,13 +15,13 @@ module Decidim
15
15
  end
16
16
 
17
17
  def new_import
18
- enforce_permission_to :import, :participatory_texts
18
+ enforce_permission_to :manage, :participatory_texts
19
19
  participatory_text = Decidim::Proposals::ParticipatoryText.find_by(component: current_component)
20
20
  @import = form(Admin::ImportParticipatoryTextForm).from_model(participatory_text)
21
21
  end
22
22
 
23
23
  def import
24
- enforce_permission_to :import, :participatory_texts
24
+ enforce_permission_to :manage, :participatory_texts
25
25
  @import = form(Admin::ImportParticipatoryTextForm).from_params(params)
26
26
 
27
27
  Admin::ImportParticipatoryText.call(@import) do
@@ -37,23 +37,55 @@ module Decidim
37
37
  end
38
38
  end
39
39
 
40
- def publish
41
- enforce_permission_to :publish, :participatory_texts
40
+ # When `save_draft` param exists, proposals are only saved.
41
+ # When no `save_draft` param is set, proposals are saved and published.
42
+ def update
43
+ enforce_permission_to :manage, :participatory_texts
44
+
42
45
  form_params = params.require(:preview_participatory_text).permit!
43
46
  @preview_form = form(Admin::PreviewParticipatoryTextForm).from_params(proposals: form_params[:proposals_attributes]&.values)
44
47
 
45
- PublishParticipatoryText.call(@preview_form) do
46
- on(:ok) do
47
- flash[:notice] = I18n.t("participatory_texts.publish.success", scope: "decidim.proposals.admin")
48
- redirect_to proposals_path
48
+ if params.has_key?("save_draft")
49
+ UpdateParticipatoryText.call(@preview_form) do
50
+ on(:ok) do
51
+ flash[:notice] = I18n.t("participatory_texts.update.success", scope: "decidim.proposals.admin")
52
+ redirect_to participatory_texts_path(component_id: current_component.id, initiative_slug: "asdf")
53
+ end
54
+
55
+ on(:invalid) do |failures|
56
+ alert_msg = [I18n.t("participatory_texts.publish.invalid", scope: "decidim.proposals.admin")]
57
+ failures.each_pair { |id, msg| alert_msg << "ID:[#{id}] #{msg}" }
58
+ flash.now[:alert] = alert_msg.join("<br/>").html_safe
59
+ index
60
+ render action: "index"
61
+ end
62
+ end
63
+ else
64
+ PublishParticipatoryText.call(@preview_form) do
65
+ on(:ok) do
66
+ flash[:notice] = I18n.t("participatory_texts.publish.success", scope: "decidim.proposals.admin")
67
+ redirect_to proposals_path
68
+ end
69
+
70
+ on(:invalid) do |failures|
71
+ alert_msg = [I18n.t("participatory_texts.publish.invalid", scope: "decidim.proposals.admin")]
72
+ failures.each_pair { |id, msg| alert_msg << "ID:[#{id}] #{msg}" }
73
+ flash.now[:alert] = alert_msg.join("<br/>").html_safe
74
+ index
75
+ render action: "index"
76
+ end
49
77
  end
78
+ end
79
+ end
80
+
81
+ # Removes all the unpublished proposals (drafts).
82
+ def discard
83
+ enforce_permission_to :manage, :participatory_texts
50
84
 
51
- on(:invalid) do |failures|
52
- alert_msg = [I18n.t("participatory_texts.publish.invalid", scope: "decidim.proposals.admin")]
53
- failures.each_pair { |id, msg| alert_msg << "ID:[#{id}] #{msg}" }
54
- flash.now[:alert] = alert_msg.join("<br/>").html_safe
55
- index
56
- render action: "index"
85
+ DiscardParticipatoryText.call(current_component) do
86
+ on(:ok) do
87
+ flash[:notice] = I18n.t("participatory_texts.discard.success", scope: "decidim.proposals.admin")
88
+ redirect_to participatory_texts_path(component_id: current_component.id, initiative_slug: "asdf")
57
89
  end
58
90
  end
59
91
  end