decidim-proposals 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 (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