decidim-proposals 0.14.4 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -0
  3. data/app/assets/images/decidim/gamification/badges/accepted_proposals.svg +194 -104
  4. data/app/assets/images/decidim/gamification/badges/proposals.svg +192 -108
  5. data/app/assets/javascripts/decidim/proposals/admin/proposals.es6 +25 -20
  6. data/app/cells/decidim/proposals/proposal_activity_cell.rb +19 -0
  7. data/app/commands/decidim/proposals/admin/answer_proposal.rb +6 -2
  8. data/app/commands/decidim/proposals/admin/create_proposal.rb +4 -4
  9. data/app/commands/decidim/proposals/admin/import_participatory_text.rb +49 -0
  10. data/app/commands/decidim/proposals/admin/import_proposals.rb +7 -21
  11. data/app/commands/decidim/proposals/admin/merge_proposals.rb +68 -0
  12. data/app/commands/decidim/proposals/admin/publish_participatory_text.rb +65 -0
  13. data/app/commands/decidim/proposals/admin/split_proposals.rb +67 -0
  14. data/app/commands/decidim/proposals/admin/update_proposal.rb +67 -0
  15. data/app/commands/decidim/proposals/create_collaborative_draft.rb +19 -14
  16. data/app/commands/decidim/proposals/create_proposal.rb +4 -2
  17. data/app/commands/decidim/proposals/endorse_proposal.rb +5 -1
  18. data/app/commands/decidim/proposals/publish_proposal.rb +15 -3
  19. data/app/commands/decidim/proposals/unvote_proposal.rb +34 -3
  20. data/app/commands/decidim/proposals/vote_proposal.rb +32 -2
  21. data/app/controllers/decidim/proposals/admin/participatory_texts_controller.rb +62 -0
  22. data/app/controllers/decidim/proposals/admin/proposals_controller.rb +28 -1
  23. data/app/controllers/decidim/proposals/admin/proposals_merges_controller.rb +27 -0
  24. data/app/controllers/decidim/proposals/admin/proposals_splits_controller.rb +27 -0
  25. data/app/controllers/decidim/proposals/collaborative_drafts_controller.rb +0 -1
  26. data/app/controllers/decidim/proposals/proposal_endorsements_controller.rb +6 -2
  27. data/app/controllers/decidim/proposals/proposal_votes_controller.rb +15 -0
  28. data/app/controllers/decidim/proposals/proposals_controller.rb +1 -1
  29. data/app/forms/decidim/proposals/admin/import_participatory_text_form.rb +28 -0
  30. data/app/forms/decidim/proposals/admin/preview_participatory_text_form.rb +21 -0
  31. data/app/forms/decidim/proposals/admin/proposal_form.rb +4 -1
  32. data/app/forms/decidim/proposals/admin/proposals_fork_form.rb +57 -0
  33. data/app/forms/decidim/proposals/admin/proposals_merge_form.rb +13 -0
  34. data/app/forms/decidim/proposals/admin/proposals_split_form.rb +12 -0
  35. data/app/helpers/decidim/proposals/application_helper.rb +23 -0
  36. data/app/helpers/decidim/proposals/map_helper.rb +1 -1
  37. data/app/helpers/decidim/proposals/participatory_texts_helper.rb +18 -0
  38. data/app/helpers/decidim/proposals/proposal_endorsements_helper.rb +1 -1
  39. data/app/helpers/decidim/proposals/proposal_wizard_helper.rb +2 -1
  40. data/app/jobs/decidim/proposals/notify_proposals_mentioned_job.rb +3 -4
  41. data/app/models/decidim/proposals/participatory_text.rb +13 -0
  42. data/app/models/decidim/proposals/proposal.rb +31 -9
  43. data/app/models/decidim/proposals/proposal_vote.rb +21 -1
  44. data/app/permissions/decidim/proposals/admin/permissions.rb +30 -0
  45. data/app/presenters/decidim/proposals/admin_log/proposal_presenter.rb +5 -1
  46. data/app/presenters/decidim/proposals/log/resource_presenter.rb +18 -0
  47. data/app/presenters/decidim/proposals/official_author_presenter.rb +4 -0
  48. data/app/queries/decidim/proposals/metrics/accepted_proposals_metric_manage.rb +29 -0
  49. data/app/queries/decidim/proposals/metrics/proposals_metric_manage.rb +53 -0
  50. data/app/queries/decidim/proposals/metrics/votes_metric_manage.rb +57 -0
  51. data/app/services/decidim/proposals/proposal_builder.rb +72 -0
  52. data/app/services/decidim/proposals/proposal_search.rb +2 -2
  53. data/app/views/decidim/proposals/admin/participatory_texts/_article-preview.html.erb +13 -0
  54. data/app/views/decidim/proposals/admin/participatory_texts/_bulk-actions.html.erb +1 -0
  55. data/app/views/decidim/proposals/admin/participatory_texts/index.html.erb +43 -0
  56. data/app/views/decidim/proposals/admin/participatory_texts/new_import.html.erb +39 -0
  57. data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +7 -2
  58. data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +4 -0
  59. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_dropdown.html.erb +36 -0
  60. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_merge.html.erb +15 -0
  61. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_recategorize.html.erb +15 -0
  62. data/app/views/decidim/proposals/admin/proposals/bulk_actions/_split.html.erb +15 -0
  63. data/app/views/decidim/proposals/admin/proposals/edit.html.erb +7 -0
  64. data/app/views/decidim/proposals/admin/proposals/index.html.erb +2 -2
  65. data/app/views/decidim/proposals/collaborative_drafts/compare.html.erb +2 -2
  66. data/app/views/decidim/proposals/collaborative_drafts/complete.html.erb +1 -1
  67. data/app/views/decidim/proposals/collaborative_drafts/show.html.erb +1 -1
  68. data/app/views/decidim/proposals/proposal_endorsements/_identity.html.erb +5 -1
  69. data/app/views/decidim/proposals/proposal_endorsements/update_buttons_and_counters.js.erb +1 -1
  70. data/app/views/decidim/proposals/proposal_votes/update_buttons_and_counters.js.erb +13 -4
  71. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +2 -2
  72. data/app/views/decidim/proposals/proposals/_endorsement_identities_cabin.html.erb +1 -1
  73. data/app/views/decidim/proposals/proposals/_proposal_similar.html.erb +1 -1
  74. data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +15 -4
  75. data/app/views/decidim/proposals/proposals/new.html.erb +1 -1
  76. data/config/locales/ca.yml +72 -4
  77. data/config/locales/de.yml +71 -3
  78. data/config/locales/en.yml +71 -3
  79. data/config/locales/es-PY.yml +71 -3
  80. data/config/locales/es.yml +73 -5
  81. data/config/locales/eu.yml +71 -3
  82. data/config/locales/fi.yml +81 -13
  83. data/config/locales/fr.yml +71 -3
  84. data/config/locales/gl.yml +71 -3
  85. data/config/locales/hu.yml +71 -3
  86. data/config/locales/it.yml +71 -3
  87. data/config/locales/nl.yml +71 -3
  88. data/config/locales/pl.yml +71 -3
  89. data/config/locales/pt-BR.yml +72 -4
  90. data/config/locales/pt.yml +71 -3
  91. data/config/locales/sv.yml +71 -3
  92. data/db/migrate/20180927111721_create_participatory_texts.rb +13 -0
  93. data/db/migrate/20180930125321_add_participatory_text_level_to_proposals.rb +7 -0
  94. data/db/migrate/20180930125321_add_position_to_proposals.rb +7 -0
  95. data/db/migrate/20181003074440_fix_user_groups_ids_in_proposals_endorsements.rb +16 -0
  96. data/db/migrate/20181010114622_add_temporary_votes.rb +9 -0
  97. data/db/migrate/20181016132225_add_organization_as_author.rb +13 -0
  98. data/db/migrate/20181017084221_make_author_polymorhpic_for_proposal_endorsements.rb +31 -0
  99. data/lib/decidim/content_parsers/proposal_parser.rb +9 -3
  100. data/lib/decidim/proposals.rb +3 -1
  101. data/lib/decidim/proposals/admin_engine.rb +12 -1
  102. data/lib/decidim/proposals/component.rb +60 -23
  103. data/lib/decidim/proposals/engine.rb +55 -12
  104. data/lib/decidim/proposals/markdown_to_proposals.rb +90 -0
  105. data/lib/decidim/proposals/participatory_text_section.rb +21 -0
  106. data/lib/decidim/proposals/test/factories.rb +35 -7
  107. data/lib/decidim/proposals/version.rb +1 -1
  108. metadata +86 -19
@@ -6,9 +6,15 @@ $(document).ready(function () {
6
6
 
7
7
  window.selectedProposalsCountUpdate = function() {
8
8
  if(selectedProposalsCount() == 0){
9
- $("#js-recategorize-proposals-count").text("")
9
+ $("#js-selected-proposals-count").text("")
10
10
  } else {
11
- $("#js-recategorize-proposals-count").text(selectedProposalsCount());
11
+ $("#js-selected-proposals-count").text(selectedProposalsCount());
12
+ }
13
+
14
+ if(selectedProposalsCount() >= 2) {
15
+ $('button[data-action="merge-proposals"]').parent().show();
16
+ } else {
17
+ $('button[data-action="merge-proposals"]').parent().hide();
12
18
  }
13
19
  }
14
20
 
@@ -33,28 +39,27 @@ $(document).ready(function () {
33
39
  $("#js-other-actions-wrapper").addClass('hide');
34
40
  }
35
41
 
36
- let showRecategorizeProposalActions = function() {
37
- $("#js-recategorize-proposals-actions").removeClass('hide');
42
+ window.hideBulkActionForms = function() {
43
+ return $(".js-bulk-action-form").addClass('hide');
38
44
  }
39
45
 
40
- window.hideRecategorizeProposalActions = function() {
41
- return $("#js-recategorize-proposals-actions").addClass('hide');
42
- }
43
-
44
- if ($('#js-form-recategorize-proposals').length) {
45
- window.hideRecategorizeProposalActions();
46
+ if ($('.js-bulk-action-form').length) {
47
+ window.hideBulkActionForms();
46
48
  $("#js-bulk-actions-button").addClass('hide');
47
49
 
48
- $("#js-bulk-actions-recategorize").click(function(e){
50
+ $("#js-bulk-actions-dropdown ul li button").click(function(e){
49
51
  e.preventDefault();
52
+ let action = $(e.target).data("action");
50
53
 
51
- $('#js-form-recategorize-proposals').submit(function(){
52
- $('.layout-content > .callout-wrapper').html("");
53
- })
54
+ if(action) {
55
+ $(`#js-form-${action}`).submit(function(){
56
+ $('.layout-content > .callout-wrapper').html("");
57
+ })
54
58
 
55
- showRecategorizeProposalActions();
56
- hideBulkActionsButton(true);
57
- hideOtherActionsButtons();
59
+ $(`#js-${action}-actions`).removeClass('hide');
60
+ hideBulkActionsButton(true);
61
+ hideOtherActionsButtons();
62
+ }
58
63
  })
59
64
 
60
65
  // select all checkboxes
@@ -99,12 +104,12 @@ $(document).ready(function () {
99
104
  hideBulkActionsButton();
100
105
  }
101
106
 
102
- $('#js-form-recategorize-proposals').find(".js-proposal-id-"+proposal_id).prop('checked', checked);
107
+ $('.js-bulk-action-form').find(".js-proposal-id-"+proposal_id).prop('checked', checked);
103
108
  selectedProposalsCountUpdate();
104
109
  });
105
110
 
106
- $('#js-cancel-edit-category').on('click', function (e) {
107
- window.hideRecategorizeProposalActions()
111
+ $('.js-cancel-bulk-action').on('click', function (e) {
112
+ window.hideBulkActionForms()
108
113
  showBulkActionsButton();
109
114
  showOtherActionsButtons();
110
115
  });
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ # A cell to display when a proposal has been published.
6
+ class ProposalActivityCell < ActivityCell
7
+ def title
8
+ I18n.t(
9
+ "decidim.proposals.last_activity.new_proposal_at_html",
10
+ link: participatory_space_link
11
+ )
12
+ end
13
+
14
+ def resource_link_text
15
+ Decidim::Proposals::ProposalPresenter.new(resource).title
16
+ end
17
+ end
18
+ end
19
+ end
@@ -81,8 +81,12 @@ module Decidim
81
81
  def increment_score
82
82
  return unless proposal.accepted?
83
83
 
84
- proposal.authors.find_each do |author|
85
- Decidim::Gamification.increment_score(author, :accepted_proposals)
84
+ proposal.coauthorships.find_each do |coauthorship|
85
+ if coauthorship.user_group
86
+ Decidim::Gamification.increment_score(coauthorship.user_group, :accepted_proposals)
87
+ else
88
+ Decidim::Gamification.increment_score(coauthorship.author, :accepted_proposals)
89
+ end
86
90
  end
87
91
  end
88
92
  end
@@ -40,10 +40,10 @@ module Decidim
40
40
  attr_reader :form, :proposal, :attachment
41
41
 
42
42
  def create_proposal
43
- @proposal = Decidim.traceability.create!(
44
- Proposal,
45
- form.current_user,
46
- attributes
43
+ @proposal = Decidim::Proposals::ProposalBuilder.create(
44
+ attributes: attributes,
45
+ author: form.current_organization,
46
+ action_user: form.current_user
47
47
  )
48
48
  end
49
49
 
@@ -0,0 +1,49 @@
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 imports proposals from
7
+ # a participatory text.
8
+ class ImportParticipatoryText < Rectify::Command
9
+ # Public: Initializes the command.
10
+ #
11
+ # form - A form object with the params.
12
+ def initialize(form)
13
+ @form = form
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid.
19
+ # - :invalid if the form wasn't valid and we couldn't proceed.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ return broadcast(:invalid) unless form.valid?
24
+
25
+ transaction do
26
+ save_participatory_text(form)
27
+ parse_participatory_text_doc(form.document_text)
28
+ end
29
+
30
+ broadcast(:ok)
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :form
36
+
37
+ def save_participatory_text(form)
38
+ document = ParticipatoryText.find_or_initialize_by(component: form.current_component)
39
+ document.update!(title: form.title, description: form.description)
40
+ end
41
+
42
+ def parse_participatory_text_doc(document)
43
+ parser = MarkdownToProposals.new(form.current_component, form.current_user)
44
+ parser.parse(document)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -33,28 +33,14 @@ module Decidim
33
33
  proposals.map do |original_proposal|
34
34
  next if proposal_already_copied?(original_proposal, target_component)
35
35
 
36
- origin_attributes = original_proposal.attributes.except(
37
- "id",
38
- "created_at",
39
- "updated_at",
40
- "state",
41
- "answer",
42
- "answered_at",
43
- "decidim_component_id",
44
- "reference",
45
- "proposal_votes_count",
46
- "proposal_notes_count"
36
+ Decidim::Proposals::ProposalBuilder.copy(
37
+ original_proposal,
38
+ author: form.current_organization,
39
+ action_user: form.current_user,
40
+ extra_attributes: {
41
+ "component" => target_component
42
+ }
47
43
  )
48
-
49
- proposal = Decidim::Proposals::Proposal.new(origin_attributes)
50
- proposal.category = original_proposal.category
51
- proposal.component = target_component
52
- proposal.save!
53
-
54
- proposal.link_resources([original_proposal], "copied_from_component")
55
- original_proposal.coauthorships.each do |coauthorship|
56
- Decidim::Coauthorship.create(author: coauthorship.author, user_group: coauthorship.user_group, coauthorable: proposal)
57
- end
58
44
  end.compact
59
45
  end
60
46
 
@@ -0,0 +1,68 @@
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 merges proposals from
7
+ # one component to another.
8
+ class MergeProposals < Rectify::Command
9
+ # Public: Initializes the command.
10
+ #
11
+ # form - A form object with the params.
12
+ def initialize(form)
13
+ @form = form
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid.
19
+ # - :invalid if the form wasn't valid and we couldn't proceed.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ return broadcast(:invalid) unless form.valid?
24
+
25
+ broadcast(:ok, merge_proposals)
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :form
31
+
32
+ def merge_proposals
33
+ transaction do
34
+ merged_proposal = create_new_proposal
35
+ merged_proposal.link_resources(proposals_to_link, "copied_from_component")
36
+ form.proposals.each(&:destroy!) if form.same_component?
37
+ merged_proposal
38
+ end
39
+ end
40
+
41
+ def proposals_to_link
42
+ return previous_links if form.same_component?
43
+ form.proposals
44
+ end
45
+
46
+ def previous_links
47
+ @previous_links ||= form.proposals.flat_map do |proposal|
48
+ proposal.linked_resources(:proposals, "copied_from_component")
49
+ end
50
+ end
51
+
52
+ def create_new_proposal
53
+ original_proposal = form.proposals.first
54
+
55
+ Decidim::Proposals::ProposalBuilder.copy(
56
+ original_proposal,
57
+ author: form.current_organization,
58
+ action_user: form.current_user,
59
+ extra_attributes: {
60
+ component: form.target_component
61
+ },
62
+ skip_link: true
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,65 @@
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 imports proposals from
7
+ # a participatory text.
8
+ class PublishParticipatoryText < Rectify::Command
9
+ # Public: Initializes the command.
10
+ #
11
+ # form - A PreviewParticipatoryTextForm form object with the params.
12
+ def initialize(form)
13
+ @form = form
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid.
19
+ # - :invalid if the form wasn't valid and we couldn't proceed.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ transaction do
24
+ @publish_failures = {}
25
+ update_contents_and_resort_proposals(form)
26
+ publish_drafts
27
+ end
28
+
29
+ if @publish_failures.any?
30
+ broadcast(:invalid, @publish_failures)
31
+ else
32
+ broadcast(:ok)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :form
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
+ def publish_drafts
53
+ Decidim::Proposals::Proposal.where(component: form.current_component).drafts.find_each do |proposal|
54
+ add_failure(proposal) unless proposal.update(published_at: Time.current)
55
+ end
56
+ raise ActiveRecord::Rollback if @publish_failures.any?
57
+ end
58
+
59
+ def add_failure(proposal)
60
+ @publish_failures[proposal.id] = proposal.errors.full_messages
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,67 @@
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 splits proposals from
7
+ # one component to another.
8
+ class SplitProposals < Rectify::Command
9
+ # Public: Initializes the command.
10
+ #
11
+ # form - A form object with the params.
12
+ def initialize(form)
13
+ @form = form
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid.
19
+ # - :invalid if the form wasn't valid and we couldn't proceed.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ return broadcast(:invalid) unless form.valid?
24
+
25
+ broadcast(:ok, split_proposals)
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :form
31
+
32
+ def split_proposals
33
+ transaction do
34
+ form.proposals.flat_map do |original_proposal|
35
+ # If copying to the same component we only need one copy
36
+ # but linking to the original proposal links, not the
37
+ # original proposal.
38
+ create_proposal(original_proposal)
39
+ create_proposal(original_proposal) unless form.same_component?
40
+ end
41
+ end
42
+ end
43
+
44
+ def create_proposal(original_proposal)
45
+ split_proposal = Decidim::Proposals::ProposalBuilder.copy(
46
+ original_proposal,
47
+ author: form.current_organization,
48
+ action_user: form.current_user,
49
+ extra_attributes: {
50
+ component: form.target_component
51
+ },
52
+ skip_link: true
53
+ )
54
+
55
+ proposals_to_link = links_for(original_proposal)
56
+ split_proposal.link_resources(proposals_to_link, "copied_from_component")
57
+ end
58
+
59
+ def links_for(proposal)
60
+ return proposal unless form.same_component?
61
+
62
+ proposal.linked_resources(:proposals, "copied_from_component")
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ # A command with all the business logic when a user updates a proposal.
7
+ class UpdateProposal < Rectify::Command
8
+ include AttachmentMethods
9
+
10
+ # Public: Initializes the command.
11
+ #
12
+ # form - A form object with the params.
13
+ # proposal - the proposal to update.
14
+ def initialize(form, proposal)
15
+ @form = form
16
+ @proposal = proposal
17
+ @attached_to = proposal
18
+ end
19
+
20
+ # Executes the command. Broadcasts these events:
21
+ #
22
+ # - :ok when everything is valid, together with the proposal.
23
+ # - :invalid if the form wasn't valid and we couldn't proceed.
24
+ #
25
+ # Returns nothing.
26
+ def call
27
+ return broadcast(:invalid) if form.invalid?
28
+
29
+ if process_attachments?
30
+ @proposal.attachments.destroy_all
31
+
32
+ build_attachment
33
+ return broadcast(:invalid) if attachment_invalid?
34
+ end
35
+
36
+ transaction do
37
+ update_proposal
38
+ create_attachment if process_attachments?
39
+ end
40
+
41
+ broadcast(:ok, proposal)
42
+ end
43
+
44
+ private
45
+
46
+ attr_reader :form, :proposal, :attachment
47
+
48
+ def update_proposal
49
+ parsed_title = Decidim::ContentProcessor.parse_with_processor(:hashtag, form.title, current_organization: form.current_organization).rewrite
50
+ parsed_body = Decidim::ContentProcessor.parse_with_processor(:hashtag, form.body, current_organization: form.current_organization).rewrite
51
+
52
+ Decidim.traceability.update!(
53
+ proposal,
54
+ form.current_user,
55
+ title: parsed_title,
56
+ body: parsed_body,
57
+ category: form.category,
58
+ scope: form.scope,
59
+ address: form.address,
60
+ latitude: form.latitude,
61
+ longitude: form.longitude
62
+ )
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end