decidim-proposals 0.25.0 → 0.26.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/proposals/collaborative_draft_m_cell.rb +1 -1
  3. data/app/cells/decidim/proposals/cost_report_cell.rb +3 -3
  4. data/app/cells/decidim/proposals/participatory_text_proposal_cell.rb +1 -1
  5. data/app/cells/decidim/proposals/proposal_m_cell.rb +4 -5
  6. data/app/cells/decidim/proposals/proposals_picker_cell.rb +7 -5
  7. data/app/commands/decidim/proposals/update_proposal.rb +3 -2
  8. data/app/controllers/concerns/decidim/proposals/orderable.rb +21 -8
  9. data/app/controllers/decidim/proposals/admin/proposals_merges_controller.rb +4 -1
  10. data/app/controllers/decidim/proposals/admin/proposals_splits_controller.rb +4 -1
  11. data/app/controllers/decidim/proposals/proposals_controller.rb +6 -3
  12. data/app/events/decidim/proposals/proposal_mentioned_event.rb +8 -0
  13. data/app/events/decidim/proposals/publish_proposal_event.rb +26 -0
  14. data/app/forms/decidim/proposals/admin/proposals_file_import_form.rb +31 -0
  15. data/app/forms/decidim/proposals/admin/proposals_fork_form.rb +8 -3
  16. data/app/helpers/decidim/proposals/application_helper.rb +1 -6
  17. data/app/helpers/decidim/proposals/map_helper.rb +1 -1
  18. data/app/helpers/decidim/proposals/proposals_helper.rb +1 -1
  19. data/app/models/decidim/proposals/proposal.rb +4 -3
  20. data/app/packs/src/decidim/proposals/add_proposal.js +8 -2
  21. data/app/packs/src/decidim/proposals/admin/proposals_picker.js +15 -0
  22. data/app/presenters/decidim/proposals/proposal_presenter.rb +2 -48
  23. data/app/queries/decidim/proposals/similar_proposals.rb +1 -1
  24. data/app/services/decidim/proposals/proposal_search.rb +9 -4
  25. data/app/views/decidim/proposals/admin/imports/_proposals_fields.html.erb +11 -0
  26. data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +7 -2
  27. data/app/views/decidim/proposals/collaborative_drafts/_edit_form_fields.html.erb +1 -1
  28. data/app/views/decidim/proposals/collaborative_drafts/_filters_small_view.html.erb +3 -3
  29. data/app/views/decidim/proposals/proposals/_edit_form_fields.html.erb +2 -2
  30. data/app/views/decidim/proposals/proposals/_filters.html.erb +2 -0
  31. data/app/views/decidim/proposals/proposals/_filters_small_view.html.erb +3 -3
  32. data/app/views/decidim/proposals/proposals/_proposals.html.erb +18 -0
  33. data/app/views/decidim/proposals/proposals/index.html.erb +0 -5
  34. data/app/views/decidim/proposals/proposals/participatory_texts/_index.html.erb +2 -2
  35. data/config/brakeman.ignore +88 -0
  36. data/config/locales/ar.yml +0 -5
  37. data/config/locales/bg.yml +0 -2
  38. data/config/locales/ca.yml +58 -7
  39. data/config/locales/cs.yml +60 -5
  40. data/config/locales/de.yml +0 -5
  41. data/config/locales/el.yml +0 -5
  42. data/config/locales/en.yml +57 -6
  43. data/config/locales/es-MX.yml +56 -5
  44. data/config/locales/es-PY.yml +56 -5
  45. data/config/locales/es.yml +56 -5
  46. data/config/locales/eu.yml +41 -6
  47. data/config/locales/fi-plain.yml +56 -5
  48. data/config/locales/fi.yml +56 -5
  49. data/config/locales/fr-CA.yml +56 -5
  50. data/config/locales/fr.yml +59 -8
  51. data/config/locales/gl.yml +55 -6
  52. data/config/locales/hu.yml +0 -5
  53. data/config/locales/id-ID.yml +0 -4
  54. data/config/locales/is-IS.yml +0 -2
  55. data/config/locales/it.yml +13 -5
  56. data/config/locales/ja.yml +79 -30
  57. data/config/locales/lv.yml +0 -5
  58. data/config/locales/nl.yml +57 -5
  59. data/config/locales/no.yml +0 -5
  60. data/config/locales/pl.yml +7 -12
  61. data/config/locales/pt-BR.yml +1 -6
  62. data/config/locales/pt.yml +37 -5
  63. data/config/locales/ro-RO.yml +467 -382
  64. data/config/locales/ru.yml +0 -2
  65. data/config/locales/sk.yml +0 -5
  66. data/config/locales/sr-CS.yml +0 -1
  67. data/config/locales/sv.yml +54 -5
  68. data/config/locales/tr-TR.yml +0 -5
  69. data/config/locales/uk.yml +0 -2
  70. data/config/locales/val-ES.yml +1 -0
  71. data/config/locales/zh-CN.yml +0 -5
  72. data/lib/decidim/proposals/component.rb +37 -4
  73. data/lib/decidim/proposals/engine.rb +4 -0
  74. data/lib/decidim/proposals/import/proposal_answer_creator.rb +95 -0
  75. data/lib/decidim/proposals/import/proposal_creator.rb +124 -0
  76. data/lib/decidim/proposals/import/proposals_answers_verifier.rb +29 -0
  77. data/lib/decidim/proposals/import/proposals_verifier.rb +16 -0
  78. data/lib/decidim/proposals/import.rb +12 -0
  79. data/lib/decidim/proposals/proposal_serializer.rb +6 -3
  80. data/lib/decidim/proposals/test/factories.rb +1 -9
  81. data/lib/decidim/proposals/version.rb +1 -1
  82. data/lib/decidim/proposals.rb +1 -1
  83. metadata +35 -27
  84. data/lib/decidim/proposals/proposal_creator.rb +0 -98
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fcbc05060c74a0d2a571bc0bf918d9fd299b0dcec051c37363232b215aa93a1a
4
- data.tar.gz: 0445507b0557f9bfda5930f2962890379f3f04682a9dd954afd8cf26ebf87ae8
3
+ metadata.gz: 6de6e1aba63e22b53acd81b90dba35747c450fd0896f02a09b7e2ed4e0f4c399
4
+ data.tar.gz: f12f55b08a0b73b9be1551c5ddb9a19c6660a05bcb7e91566cd8473ec345e643
5
5
  SHA512:
6
- metadata.gz: f64ef4865607f8e874a23733387f06d0715e32e38f86cf5f88da12aa76ce6fe859ea01fdba242a49b79105c23260a818ae396c5dc514fffee63c1da4a8b262b8
7
- data.tar.gz: 51af55d39551b937a5209b1704a3bfec9d1706057f032d9f6ab5d0e9c558b66491ca0edf2c62f3e6407102cef7d5fbd5a8b3ded4f6c3026599e365a1f48e6c1b
6
+ metadata.gz: 33765293150b20320f7af104930f1a7253e54779728d8bd20eb4297899c290ffe5d946339870bf5536c5d9fe73f1632f97768c0a15a3359dcbbaee70f83a5a71
7
+ data.tar.gz: 79e1c45b96cb97a895b205830181c866b77bcb0ca7dbf3996ccc0463605615bf480ccc4dd9dc4ee3dad1d7280eb7a62d9a19da42facd0c693dd9ee1fea1a7b0d
@@ -23,7 +23,7 @@ module Decidim
23
23
  end
24
24
 
25
25
  def description
26
- decidim_sanitize(present(model).body.truncate(100, separator: /\s/))
26
+ decidim_sanitize_editor(present(model).body.truncate(100, separator: /\s/))
27
27
  end
28
28
 
29
29
  def has_badge?
@@ -18,7 +18,7 @@ module Decidim
18
18
  end
19
19
 
20
20
  def cost_report
21
- decidim_sanitize(translated_attribute(model.cost_report).html_safe)
21
+ decidim_sanitize_editor(translated_attribute(model.cost_report).html_safe)
22
22
  end
23
23
 
24
24
  def needs_text_toggle?
@@ -26,7 +26,7 @@ module Decidim
26
26
  end
27
27
 
28
28
  def cost_report_short
29
- decidim_sanitize(
29
+ decidim_sanitize_editor(
30
30
  html_truncate(
31
31
  translated_attribute(model.cost_report).html_safe,
32
32
  length: 200
@@ -35,7 +35,7 @@ module Decidim
35
35
  end
36
36
 
37
37
  def execution_period
38
- decidim_sanitize(translated_attribute(model.execution_period).html_safe)
38
+ decidim_sanitize_editor(translated_attribute(model.execution_period).html_safe)
39
39
  end
40
40
  end
41
41
  end
@@ -35,7 +35,7 @@ module Decidim
35
35
  return unless model.participatory_text_level == "article"
36
36
 
37
37
  formatted = simple_format(present(model).body)
38
- decidim_sanitize(strip_links(formatted))
38
+ decidim_sanitize_editor(strip_links(formatted))
39
39
  end
40
40
 
41
41
  def resource_path
@@ -25,7 +25,7 @@ module Decidim
25
25
  end
26
26
 
27
27
  def body
28
- decidim_sanitize(present(model).body)
28
+ decidim_sanitize_editor(present(model).body)
29
29
  end
30
30
 
31
31
  def has_state?
@@ -118,7 +118,7 @@ module Decidim
118
118
  end
119
119
 
120
120
  def has_image?
121
- @has_image ||= model.component.settings.allow_card_image && model.attachments.find_by("content_type like '%image%'").present?
121
+ @has_image ||= model.attachments.map(&:image?).any?
122
122
  end
123
123
 
124
124
  def resource_image_path
@@ -127,7 +127,6 @@ module Decidim
127
127
 
128
128
  def cache_hash
129
129
  hash = []
130
- hash << "decidim/proposals/proposal_m"
131
130
  hash << I18n.locale.to_s
132
131
  hash << model.cache_key_with_version
133
132
  hash << model.proposal_votes_count
@@ -143,9 +142,9 @@ module Decidim
143
142
  hash << model.follows_count
144
143
  hash << Digest::MD5.hexdigest(model.authors.map(&:cache_key_with_version).to_s)
145
144
  hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
146
- hash << model.component.participatory_space.active_step.id if model.component.participatory_space.has_steps?
145
+ hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
147
146
 
148
- hash.join("/")
147
+ hash.join(Decidim.cache_key_separator)
149
148
  end
150
149
  end
151
150
  end
@@ -50,9 +50,10 @@ module Decidim
50
50
 
51
51
  def filtered_proposals
52
52
  @filtered_proposals ||= if filtered?
53
- proposals.where("title::text ILIKE ?", "%#{search_text}%")
54
- .or(proposals.where("reference ILIKE ?", "%#{search_text}%"))
55
- .or(proposals.where("id::text ILIKE ?", "%#{search_text}%"))
53
+ table_name = Decidim::Proposals::Proposal.table_name
54
+ proposals.where(%("#{table_name}"."title"::text ILIKE ?), "%#{search_text}%")
55
+ .or(proposals.where(%("#{table_name}"."reference" ILIKE ?), "%#{search_text}%"))
56
+ .or(proposals.where(%("#{table_name}"."id"::text ILIKE ?), "%#{search_text}%"))
56
57
  else
57
58
  proposals
58
59
  end
@@ -60,8 +61,9 @@ module Decidim
60
61
 
61
62
  def proposals
62
63
  @proposals ||= Decidim.find_resource_manifest(:proposals).try(:resource_scope, component)
63
- &.published
64
- &.order(id: :asc)
64
+ &.includes(:component)
65
+ &.published
66
+ &.order(id: :asc)
65
67
  end
66
68
 
67
69
  def proposals_collection_name
@@ -45,11 +45,12 @@ module Decidim
45
45
  else
46
46
  update_proposal
47
47
  end
48
- create_gallery if process_gallery?
49
- create_attachments if process_attachments?
50
48
 
51
49
  photo_cleanup!
52
50
  document_cleanup!
51
+
52
+ create_gallery if process_gallery?
53
+ create_attachments if process_attachments?
53
54
  end
54
55
 
55
56
  broadcast(:ok, proposal)
@@ -15,19 +15,32 @@ module Decidim
15
15
 
16
16
  # Available orders based on enabled settings
17
17
  def available_orders
18
- @available_orders ||= begin
19
- available_orders = %w(random recent)
20
- available_orders << "most_voted" if most_voted_order_available?
21
- available_orders << "most_endorsed" if current_settings.endorsements_enabled?
22
- available_orders << "most_commented" if component_settings.comments_enabled?
23
- available_orders << "most_followed" << "with_more_authors"
24
- available_orders
18
+ @available_orders ||= [default_order] + possible_orders.excluding(default_order)
19
+ end
20
+
21
+ def possible_orders
22
+ @possible_orders ||= begin
23
+ possible_orders = %w(random recent)
24
+ possible_orders << "most_voted" if most_voted_order_available?
25
+ possible_orders << "most_endorsed" if current_settings.endorsements_enabled?
26
+ possible_orders << "most_commented" if component_settings.comments_enabled?
27
+ possible_orders << "most_followed" << "with_more_authors"
28
+ possible_orders
25
29
  end
26
30
  end
27
31
 
28
32
  def default_order
33
+ @default_order ||= begin
34
+ default_order = current_settings.default_sort_order.presence || component_settings.default_sort_order
35
+ return order_by_default if default_order == "default"
36
+
37
+ possible_orders.include?(default_order) ? default_order : order_by_default
38
+ end
39
+ end
40
+
41
+ def order_by_default
29
42
  if order_by_votes?
30
- detect_order("most_voted")
43
+ "most_voted"
31
44
  else
32
45
  "random"
33
46
  end
@@ -16,7 +16,10 @@ module Decidim
16
16
  end
17
17
 
18
18
  on(:invalid) do
19
- flash[:alert] = I18n.t("proposals_merges.create.invalid", scope: "decidim.proposals.admin")
19
+ flash[:alert_html] = Decidim::ValidationErrorsPresenter.new(
20
+ I18n.t("proposals_merges.create.invalid", scope: "decidim.proposals.admin"),
21
+ @form
22
+ ).message
20
23
  redirect_to EngineRouter.admin_proxy(current_component).root_path
21
24
  end
22
25
  end
@@ -16,7 +16,10 @@ module Decidim
16
16
  end
17
17
 
18
18
  on(:invalid) do
19
- flash.now[:alert] = I18n.t("proposals_splits.create.invalid", scope: "decidim.proposals.admin")
19
+ flash[:alert_html] = Decidim::ValidationErrorsPresenter.new(
20
+ I18n.t("proposals_splits.create.invalid", scope: "decidim.proposals.admin"),
21
+ @form
22
+ ).message
20
23
  redirect_to EngineRouter.admin_proxy(current_component).root_path
21
24
  end
22
25
  end
@@ -32,7 +32,7 @@ module Decidim
32
32
  .published
33
33
  .not_hidden
34
34
  .only_amendables
35
- .includes(:category, :scope)
35
+ .includes(:category, :scope, :attachments, :coauthorships)
36
36
  .order(position: :asc)
37
37
  render "decidim/proposals/proposals/participatory_texts/participatory_text"
38
38
  else
@@ -41,7 +41,7 @@ module Decidim
41
41
  .published
42
42
  .not_hidden
43
43
 
44
- @proposals = @base_query.includes(:component, :coauthorships)
44
+ @proposals = @base_query.includes(:component, :coauthorships, :attachments)
45
45
  @all_geocoded_proposals = @base_query.geocoded
46
46
 
47
47
  @voted_proposals = if current_user
@@ -91,6 +91,7 @@ module Decidim
91
91
  end
92
92
 
93
93
  def compare
94
+ enforce_permission_to :edit, :proposal, proposal: @proposal
94
95
  @step = :step_2
95
96
  @similar_proposals ||= Decidim::Proposals::SimilarProposals
96
97
  .for(current_component, @proposal)
@@ -103,7 +104,7 @@ module Decidim
103
104
  end
104
105
 
105
106
  def complete
106
- enforce_permission_to :create, :proposal
107
+ enforce_permission_to :edit, :proposal, proposal: @proposal
107
108
  @step = :step_3
108
109
 
109
110
  @form = form_proposal_model
@@ -112,11 +113,13 @@ module Decidim
112
113
  end
113
114
 
114
115
  def preview
116
+ enforce_permission_to :edit, :proposal, proposal: @proposal
115
117
  @step = :step_4
116
118
  @form = form(ProposalForm).from_model(@proposal)
117
119
  end
118
120
 
119
121
  def publish
122
+ enforce_permission_to :edit, :proposal, proposal: @proposal
120
123
  @step = :step_4
121
124
  PublishProposal.call(@proposal, current_user) do
122
125
  on(:ok) do
@@ -7,6 +7,14 @@ module Decidim
7
7
 
8
8
  i18n_attributes :mentioned_proposal_title
9
9
 
10
+ def safe_resource_translated_text
11
+ resource_text
12
+ end
13
+
14
+ def perform_translation?
15
+ false
16
+ end
17
+
10
18
  private
11
19
 
12
20
  def mentioned_proposal_title
@@ -4,11 +4,37 @@ module Decidim
4
4
  module Proposals
5
5
  class PublishProposalEvent < Decidim::Events::SimpleEvent
6
6
  include Decidim::Events::CoauthorEvent
7
+ include Decidim::Core::Engine.routes.url_helpers
8
+ include ActionView::Helpers::UrlHelper
9
+ include Decidim::Events::MachineTranslatedEvent
7
10
 
8
11
  def resource_text
9
12
  resource.body
10
13
  end
11
14
 
15
+ def i18n_options
16
+ author_path = link_to("@#{author.nickname}", profile_path(author.nickname))
17
+ author_string = "#{author.name} #{author_path}"
18
+ super.merge({ author: author_string })
19
+ end
20
+
21
+ def translatable_resource
22
+ resource
23
+ end
24
+
25
+ def translatable_text
26
+ resource.body
27
+ end
28
+
29
+ def safe_resource_text
30
+ locale = resource.respond_to?(:content_original_language) ? resource.content_original_language : I18n.locale
31
+ I18n.with_locale(locale) { translated_attribute(resource_text).to_s.html_safe }
32
+ end
33
+
34
+ def safe_resource_translated_text
35
+ I18n.with_locale(I18n.locale) { translated_attribute(resource_text, nil, true).to_s.html_safe }
36
+ end
37
+
12
38
  private
13
39
 
14
40
  def i18n_scope
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ module Admin
6
+ # A form object to be used when admin users want to import proposals from
7
+ # a file.
8
+ class ProposalsFileImportForm < Decidim::Admin::ImportForm
9
+ attribute :user_group_id, Integer
10
+
11
+ def user_groups
12
+ Decidim::UserGroups::ManageableUserGroups.for(current_user).verified
13
+ end
14
+
15
+ protected
16
+
17
+ def user_group
18
+ @user_group ||= Decidim::UserGroup.find_by(
19
+ organization: current_organization,
20
+ id: user_group_id.to_i
21
+ )
22
+ end
23
+
24
+ def importer_context
25
+ context[:user_group] = user_group
26
+ context
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -30,14 +30,19 @@ module Decidim
30
30
 
31
31
  private
32
32
 
33
+ def errors_set
34
+ @errors_set ||= Set[]
35
+ end
36
+
33
37
  def mergeable_to_same_component
34
38
  return true unless same_component?
35
39
 
36
- public_proposals = proposals.any? do |proposal|
37
- !proposal.official? || proposal.votes.any? || proposal.endorsements.any?
40
+ proposals.each do |proposal|
41
+ errors_set << :not_official unless proposal.official?
42
+ errors_set << :supported if proposal.votes.any? || proposal.endorsements.any?
38
43
  end
39
44
 
40
- errors.add(:proposal_ids, :invalid) if public_proposals
45
+ errors_set.each { |error| errors.add(:base, error) } if errors_set.any?
41
46
  end
42
47
 
43
48
  def same_participatory_space
@@ -102,12 +102,7 @@ module Decidim
102
102
 
103
103
  # If the content is safe, HTML tags are sanitized, otherwise, they are stripped.
104
104
  def render_proposal_body(proposal)
105
- body = present(proposal).body(links: true, strip_tags: !safe_content?)
106
- body = simple_format(body, {}, sanitize: false)
107
-
108
- return body unless safe_content?
109
-
110
- decidim_sanitize(body)
105
+ render_sanitized_content(proposal, :body)
111
106
  end
112
107
 
113
108
  # Returns :text_area or :editor based on the organization' settings.
@@ -19,7 +19,7 @@ module Decidim
19
19
  .slice(:latitude, :longitude, :address)
20
20
  .merge(
21
21
  title: decidim_html_escape(present(proposal).title),
22
- body: html_truncate(decidim_sanitize(present(proposal).body), length: 100),
22
+ body: html_truncate(decidim_sanitize_editor(present(proposal).body), length: 100),
23
23
  icon: icon("proposals", width: 40, height: 70, remove_icon_class: true),
24
24
  link: proposal_path(proposal)
25
25
  )
@@ -7,7 +7,7 @@ module Decidim
7
7
  def proposal_reason_callout_announcement
8
8
  {
9
9
  title: proposal_reason_callout_title,
10
- body: decidim_sanitize(translated_attribute(@proposal.answer))
10
+ body: decidim_sanitize_editor(translated_attribute(@proposal.answer))
11
11
  }
12
12
  end
13
13
 
@@ -70,12 +70,13 @@ module Decidim
70
70
  scope :except_drafts, -> { where.not(published_at: nil) }
71
71
  scope :published, -> { where.not(published_at: nil) }
72
72
  scope :order_by_most_recent, -> { order(created_at: :desc) }
73
+
73
74
  scope :sort_by_valuation_assignments_count_asc, lambda {
74
- order("#{sort_by_valuation_assignments_count_nulls_last_query}ASC NULLS FIRST")
75
+ order(Arel.sql("#{sort_by_valuation_assignments_count_nulls_last_query} ASC NULLS FIRST").to_s)
75
76
  }
76
77
 
77
78
  scope :sort_by_valuation_assignments_count_desc, lambda {
78
- order("#{sort_by_valuation_assignments_count_nulls_last_query}DESC NULLS LAST")
79
+ order(Arel.sql("#{sort_by_valuation_assignments_count_nulls_last_query} DESC NULLS LAST").to_s)
79
80
  }
80
81
 
81
82
  def self.with_valuation_assigned_to(user, space)
@@ -278,7 +279,7 @@ module Decidim
278
279
  #
279
280
  # user - the user to check for authorship
280
281
  def editable_by?(user)
281
- return true if draft?
282
+ return true if draft? && created_by?(user)
282
283
 
283
284
  !published_state? && within_edit_time_limit? && !copied_from_other_component? && created_by?(user)
284
285
  end
@@ -6,8 +6,14 @@ $(() => {
6
6
  const $addressInput = $("#address_input");
7
7
  const $addressInputField = $("input", $addressInput);
8
8
  const $map = $("#address_map");
9
- const latFieldName = getCoordinateInputName("latitude", $addressInputField, {})
10
- const longFieldName = getCoordinateInputName("longitude", $addressInputField, {})
9
+ let latFieldName = "latitude";
10
+ let longFieldName = "longitude";
11
+
12
+ if ($addressInputField.length > 0) {
13
+ latFieldName = getCoordinateInputName("latitude", $addressInputField, {})
14
+ longFieldName = getCoordinateInputName("longitude", $addressInputField, {})
15
+ }
16
+
11
17
  $map.hide();
12
18
 
13
19
  if ($checkbox.length > 0) {
@@ -8,6 +8,7 @@ $(() => {
8
8
  }
9
9
 
10
10
  let jqxhr = null
11
+ let filterBuffer = ""
11
12
 
12
13
  toggleNoProposals()
13
14
 
@@ -15,15 +16,29 @@ $(() => {
15
16
  const filter = event.target.value.toLowerCase()
16
17
 
17
18
  if (pickerMore) {
19
+ if (filter.length < 3) {
20
+ return
21
+ }
22
+
23
+ if (filter === filterBuffer) {
24
+ return
25
+ }
26
+
18
27
  if (jqxhr !== null) {
19
28
  jqxhr.abort()
20
29
  }
21
30
 
22
31
  $content.html("<div class='loading-spinner'></div>")
23
32
  jqxhr = $.get(`${pickerPath}?q=${filter}`, (data) => {
33
+ filterBuffer = filter
24
34
  $content.html(data)
25
35
  jqxhr = null
26
36
  toggleNoProposals()
37
+
38
+ if (typeof window.theDataPicker === "object" && window.theDataPicker.current !== null) {
39
+ window.theDataPicker._handleCheckboxes($content);
40
+ window.theDataPicker._handleLinks($content);
41
+ }
27
42
  })
28
43
  } else {
29
44
  $("#proposals_list li").each((index, li) => {
@@ -8,6 +8,7 @@ module Decidim
8
8
  class ProposalPresenter < Decidim::ResourcePresenter
9
9
  include Rails.application.routes.mounted_helpers
10
10
  include ActionView::Helpers::UrlHelper
11
+ include Decidim::SanitizeHelper
11
12
 
12
13
  def author
13
14
  @author ||= if official?
@@ -49,15 +50,7 @@ module Decidim
49
50
  def body(links: false, extras: true, strip_tags: false, all_locales: false)
50
51
  return unless proposal
51
52
 
52
- handle_locales(proposal.body, all_locales) do |content|
53
- content = strip_tags(sanitize_text(content)) if strip_tags
54
-
55
- renderer = Decidim::ContentRenderers::HashtagRenderer.new(content)
56
- content = renderer.render(links: links, extras: extras).html_safe
57
-
58
- content = Decidim::ContentRenderers::LinkRenderer.new(content).render if links
59
- content
60
- end
53
+ content_handle_locale(proposal.body, all_locales, extras, links, strip_tags)
61
54
  end
62
55
 
63
56
  # Returns the proposal versions, hiding not published answers
@@ -89,45 +82,6 @@ module Decidim
89
82
  def resource_manifest
90
83
  proposal.class.resource_manifest
91
84
  end
92
-
93
- private
94
-
95
- def sanitize_unordered_lists(text)
96
- text.gsub(%r{(?=.*</ul>)(?!.*?<li>.*?</ol>.*?</ul>)<li>}) { |li| "#{li}• " }
97
- end
98
-
99
- def sanitize_ordered_lists(text)
100
- i = 0
101
-
102
- text.gsub(%r{(?=.*</ol>)(?!.*?<li>.*?</ul>.*?</ol>)<li>}) do |li|
103
- i += 1
104
-
105
- li + "#{i}. "
106
- end
107
- end
108
-
109
- def add_line_feeds_to_paragraphs(text)
110
- text.gsub("</p>") { |p| "#{p}\n\n" }
111
- end
112
-
113
- def add_line_feeds_to_list_items(text)
114
- text.gsub("</li>") { |li| "#{li}\n" }
115
- end
116
-
117
- # Adds line feeds after the paragraph and list item closing tags.
118
- #
119
- # Returns a String.
120
- def add_line_feeds(text)
121
- add_line_feeds_to_paragraphs(add_line_feeds_to_list_items(text))
122
- end
123
-
124
- # Maintains the paragraphs and lists separations with their bullet points and
125
- # list numberings where appropriate.
126
- #
127
- # Returns a String.
128
- def sanitize_text(text)
129
- add_line_feeds(sanitize_ordered_lists(sanitize_unordered_lists(text)))
130
- end
131
85
  end
132
86
  end
133
87
  end
@@ -31,7 +31,7 @@ module Decidim
31
31
  .published
32
32
  .not_hidden
33
33
  .where(
34
- "GREATEST(#{title_similarity}, #{body_similarity}) >= ?",
34
+ Arel.sql("GREATEST(#{title_similarity}, #{body_similarity}) >= ?").to_s,
35
35
  *similarity_params,
36
36
  Decidim::Proposals.similarity_threshold
37
37
  )
@@ -12,8 +12,9 @@ module Decidim
12
12
  # page - The page number to paginate the results.
13
13
  # per_page - The number of proposals to return per page.
14
14
  def initialize(options = {})
15
- base = options[:state]&.member?("withdrawn") ? Proposal.withdrawn : Proposal.except_withdrawn
16
- super(base, options)
15
+ options[:scope] = options.fetch(:scope, Proposal)
16
+ options[:scope] = options[:state_withdraw] == "withdrawn" ? options[:scope].withdrawn : options[:scope].except_withdrawn
17
+ super(options[:scope], options)
17
18
  end
18
19
 
19
20
  # Handle the activity filter
@@ -34,10 +35,14 @@ module Decidim
34
35
  end
35
36
  end
36
37
 
38
+ def search_state_withdraw
39
+ return query if state_withdraw == "withdrawn"
40
+
41
+ query.except_withdrawn
42
+ end
43
+
37
44
  # Handle the state filter
38
45
  def search_state
39
- return query if state.member? "withdrawn"
40
-
41
46
  apply_scopes(%w(accepted rejected evaluating state_not_published), state)
42
47
  end
43
48
 
@@ -0,0 +1,11 @@
1
+ <% if current_organization.user_groups_enabled? && form.object.user_groups.any? %>
2
+ <div class="field">
3
+ <%= form.select(
4
+ :user_group_id,
5
+ form.object.user_groups.map { |g| [g.name, g.id] },
6
+ selected: form.object.user_group_id.presence,
7
+ include_blank: current_user.name,
8
+ label: true
9
+ ) %>
10
+ </div>
11
+ <% end %>
@@ -12,8 +12,13 @@
12
12
 
13
13
  <% if allowed_to? :import, :proposals %>
14
14
  <%= import_dropdown do %>
15
- <% content_tag :li do %>
16
- <% link_to t("actions.import", scope: "decidim.proposals", name: t("models.proposal.name", scope: "decidim.proposals.admin")), new_proposals_import_path %>
15
+ <li class="imports--component imports--proposals">
16
+ <%= link_to t("actions.import", scope: "decidim.proposals", name: t("models.proposal.name", scope: "decidim.proposals.admin")), new_proposals_import_path %>
17
+ </li>
18
+ <% current_component.manifest.import_manifests.each do |import_manifest| %>
19
+ <li class="imports--file imports--<%= import_manifest.name %>">
20
+ <%= link_to import_manifest.message(:label, self), admin_imports_path(current_component, name: import_manifest.name) %>
21
+ </li>
17
22
  <% end %>
18
23
  <% end %>
19
24
  <% end %>
@@ -61,7 +61,7 @@
61
61
  <% end %>
62
62
 
63
63
  <% if component_settings.attachments_allowed? %>
64
- <fieldset>
64
+ <fieldset class="attachments_container">
65
65
  <legend><%= t("attachment_legend", scope: "decidim.proposals.collaborative_drafts.edit") %></legend>
66
66
  <%= form.fields_for :attachment, @form.attachment do |nested_form| %>
67
67
  <div class="field">
@@ -1,13 +1,13 @@
1
1
  <div class="filters-controls hide-for-mediumlarge">
2
- <button data-open="filter-box" class="filters-controls__trigger">
2
+ <button data-open="filter-box" class="filters-controls__trigger" aria-controls="filter-box" aria-haspopup="dialog">
3
3
  <%= t ".filter" %>
4
4
  <%= icon "caret-bottom", class: "icon--small float-right", aria_label: t(".unfold"), role: "img" %>
5
5
  </button>
6
6
  </div>
7
7
 
8
- <div class="reveal" id="filter-box" data-reveal>
8
+ <div class="reveal" id="filter-box" data-reveal role="dialog" aria-modal="true" aria-labelledby="filter-box-label">
9
9
  <div class="reveal__header">
10
- <h3 class="reveal__title"><%= t ".filter_by" %>:</h3>
10
+ <h3 id="filter-box-label" class="reveal__title"><%= t ".filter_by" %>:</h3>
11
11
  <button class="close-button" data-close aria-label="<%= t(".close_modal") %>" type="button">
12
12
  <span aria-hidden="true">&times;</span>
13
13
  </button>