decidim-initiatives 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -0
  3. data/app/assets/images/decidim/gamification/badges/initiatives.svg +1 -87
  4. data/app/assets/images/decidim/initiatives/icon.svg +1 -3
  5. data/app/assets/stylesheet/decidim/initiatives/initiatives-votes.css.scss +0 -1
  6. data/app/assets/stylesheet/decidim/initiatives/initiatives.scss +0 -8
  7. data/app/assets/stylesheet/decidim/initiatives/popularity_item.css.scss +0 -1
  8. data/app/assets/stylesheet/decidim/initiatives/print-initiative.css.scss +0 -3
  9. data/app/cells/decidim/initiatives/content_blocks/highlighted_initiatives/show.erb +4 -3
  10. data/app/cells/decidim/initiatives/initiative_m_cell.rb +11 -0
  11. data/app/commands/decidim/initiatives/admin/create_initiative_type.rb +3 -0
  12. data/app/commands/decidim/initiatives/admin/send_initiative_to_technical_validation.rb +17 -0
  13. data/app/commands/decidim/initiatives/admin/update_initiative.rb +29 -10
  14. data/app/commands/decidim/initiatives/admin/update_initiative_type.rb +3 -0
  15. data/app/commands/decidim/initiatives/attachment_methods.rb +33 -0
  16. data/app/commands/decidim/initiatives/create_initiative.rb +23 -1
  17. data/app/commands/decidim/initiatives/vote_initiative.rb +16 -0
  18. data/app/controllers/concerns/decidim/initiatives/admin/filterable.rb +18 -4
  19. data/app/controllers/concerns/decidim/initiatives/orderable.rb +3 -1
  20. data/app/controllers/concerns/decidim/initiatives/single_initiative_type.rb +26 -0
  21. data/app/controllers/decidim/initiatives/admin/initiatives_controller.rb +20 -1
  22. data/app/controllers/decidim/initiatives/create_initiative_controller.rb +35 -5
  23. data/app/controllers/decidim/initiatives/initiatives_controller.rb +17 -3
  24. data/app/controllers/decidim/initiatives/versions_controller.rb +20 -0
  25. data/app/events/decidim/initiatives/admin/initiative_sent_to_technical_validation_event.rb +21 -0
  26. data/app/events/decidim/initiatives/admin/support_threshold_reached_event.rb +13 -0
  27. data/app/forms/decidim/initiatives/admin/initiative_form.rb +31 -0
  28. data/app/forms/decidim/initiatives/admin/initiative_type_form.rb +5 -1
  29. data/app/forms/decidim/initiatives/initiative_form.rb +34 -0
  30. data/app/forms/decidim/initiatives/vote_form.rb +3 -1
  31. data/app/helpers/decidim/initiatives/application_helper.rb +104 -0
  32. data/app/helpers/decidim/initiatives/initiative_helper.rb +13 -0
  33. data/app/helpers/decidim/initiatives/initiatives_helper.rb +10 -0
  34. data/app/jobs/decidim/initiatives/export_initiatives_job.rb +25 -0
  35. data/app/mailers/decidim/initiatives/initiatives_mailer.rb +0 -21
  36. data/app/models/concerns/decidim/initiatives/has_area.rb +30 -0
  37. data/app/models/decidim/initiative.rb +62 -10
  38. data/app/permissions/decidim/initiatives/admin/permissions.rb +7 -0
  39. data/app/permissions/decidim/initiatives/permissions.rb +35 -10
  40. data/app/serializers/decidim/initiatives/initiative_serializer.rb +32 -0
  41. data/app/services/decidim/initiatives/diff_renderer.rb +18 -0
  42. data/app/services/decidim/initiatives/initiative_search.rb +59 -15
  43. data/app/services/decidim/initiatives/status_change_notifier.rb +4 -5
  44. data/app/views/decidim/initiatives/admin/exports/_dropdown.html.erb +8 -0
  45. data/app/views/decidim/initiatives/admin/initiatives/_form.html.erb +24 -1
  46. data/app/views/decidim/initiatives/admin/initiatives/_initiative_attachments.erb +43 -0
  47. data/app/views/decidim/initiatives/admin/initiatives/index.html.erb +8 -3
  48. data/app/views/decidim/initiatives/admin/initiatives_types/_form.html.erb +12 -0
  49. data/app/views/decidim/initiatives/create_initiative/fill_data.html.erb +44 -10
  50. data/app/views/decidim/initiatives/create_initiative/finish.html.erb +17 -10
  51. data/app/views/decidim/initiatives/create_initiative/previous_form.html.erb +2 -1
  52. data/app/views/decidim/initiatives/create_initiative/promotal_committee.html.erb +1 -1
  53. data/app/views/decidim/initiatives/create_initiative/select_initiative_type.html.erb +1 -2
  54. data/app/views/decidim/initiatives/create_initiative/show_similar_initiatives.html.erb +1 -1
  55. data/app/views/decidim/initiatives/initiative_signatures/fill_personal_data.html.erb +1 -0
  56. data/app/views/decidim/initiatives/initiatives/_author.html.erb +1 -1
  57. data/app/views/decidim/initiatives/initiatives/_filters.html.erb +16 -28
  58. data/app/views/decidim/initiatives/initiatives/_index_header.html.erb +3 -3
  59. data/app/views/decidim/initiatives/initiatives/_initiatives.html.erb +1 -1
  60. data/app/views/decidim/initiatives/initiatives/_interactions.html.erb +2 -3
  61. data/app/views/decidim/initiatives/initiatives/_tags.html.erb +3 -0
  62. data/app/views/decidim/initiatives/initiatives/index.html.erb +1 -1
  63. data/app/views/decidim/initiatives/initiatives/show.html.erb +1 -0
  64. data/app/views/decidim/initiatives/versions/index.html.erb +8 -0
  65. data/app/views/decidim/initiatives/versions/show.html.erb +10 -0
  66. data/app/views/layouts/decidim/_initiative_creation_header.html.erb +2 -1
  67. data/app/views/layouts/decidim/_initiative_header.html.erb +2 -1
  68. data/app/views/layouts/decidim/_initiative_signature_creation_header.html.erb +1 -1
  69. data/app/views/layouts/decidim/initiative_creation.html.erb +1 -2
  70. data/app/views/layouts/decidim/initiative_signature_creation.html.erb +2 -2
  71. data/config/locales/ar.yml +12 -19
  72. data/config/locales/bg-BG.yml +13 -0
  73. data/config/locales/ca.yml +79 -17
  74. data/config/locales/cs.yml +88 -26
  75. data/config/locales/da-DK.yml +1 -0
  76. data/config/locales/de.yml +67 -16
  77. data/config/locales/el.yml +528 -0
  78. data/config/locales/en.yml +82 -20
  79. data/config/locales/es-MX.yml +79 -17
  80. data/config/locales/es-PY.yml +79 -17
  81. data/config/locales/es.yml +81 -19
  82. data/config/locales/et-EE.yml +1 -0
  83. data/config/locales/eu.yml +4 -7
  84. data/config/locales/fi-plain.yml +79 -17
  85. data/config/locales/fi.yml +97 -35
  86. data/config/locales/fr-CA.yml +529 -0
  87. data/config/locales/fr.yml +66 -16
  88. data/config/locales/ga-IE.yml +1 -0
  89. data/config/locales/gl.yml +4 -7
  90. data/config/locales/hr-HR.yml +1 -0
  91. data/config/locales/hu.yml +21 -18
  92. data/config/locales/id-ID.yml +4 -7
  93. data/config/locales/is-IS.yml +4 -7
  94. data/config/locales/it.yml +105 -57
  95. data/config/locales/ja-JP.yml +535 -0
  96. data/config/locales/lt-LT.yml +1 -0
  97. data/config/locales/lv-LV.yml +529 -0
  98. data/config/locales/mt-MT.yml +1 -0
  99. data/config/locales/nl.yml +66 -16
  100. data/config/locales/no.yml +14 -29
  101. data/config/locales/pl.yml +241 -176
  102. data/config/locales/pt-BR.yml +5 -8
  103. data/config/locales/pt.yml +227 -176
  104. data/config/locales/ro-RO.yml +532 -0
  105. data/config/locales/ru.yml +4 -7
  106. data/config/locales/sk-SK.yml +468 -0
  107. data/config/locales/sk.yml +462 -0
  108. data/config/locales/sl.yml +18 -0
  109. data/config/locales/sr-CS.yml +8 -0
  110. data/config/locales/sv.yml +77 -26
  111. data/config/locales/tr-TR.yml +4 -7
  112. data/config/locales/uk.yml +4 -7
  113. data/db/migrate/20200320105920_index_foreign_keys_in_decidim_initiatives.rb +8 -0
  114. data/db/migrate/20200320105921_index_foreign_keys_in_decidim_initiatives_votes.rb +8 -0
  115. data/db/migrate/20200417120551_add_custom_signature_end_time_option.rb +7 -0
  116. data/db/migrate/20200424110930_add_attachments_enabled_option.rb +7 -0
  117. data/db/migrate/20200514085422_add_area_to_initiatives.rb +7 -0
  118. data/db/migrate/20200514102631_add_area_enabled_option_to_initiatives.rb +7 -0
  119. data/db/seeds/city2.jpeg +0 -0
  120. data/lib/decidim/initiatives/admin_engine.rb +4 -0
  121. data/lib/decidim/initiatives/engine.rb +1 -0
  122. data/lib/decidim/initiatives/participatory_space.rb +9 -1
  123. data/lib/decidim/initiatives/test/factories.rb +30 -1
  124. data/lib/decidim/initiatives/version.rb +1 -1
  125. metadata +55 -15
  126. data/app/views/decidim/initiatives/initiatives_mailer/notify_validating_request.html.erb +0 -3
@@ -12,15 +12,24 @@ module Decidim
12
12
  attribute :description, String
13
13
  attribute :type_id, Integer
14
14
  attribute :scope_id, Integer
15
+ attribute :area_id, Integer
15
16
  attribute :decidim_user_group_id, Integer
16
17
  attribute :signature_type, String
18
+ attribute :signature_end_date, Date
17
19
  attribute :state, String
20
+ attribute :attachment, AttachmentForm
18
21
 
19
22
  validates :title, :description, presence: true
20
23
  validates :title, length: { maximum: 150 }
21
24
  validates :signature_type, presence: true
22
25
  validates :type_id, presence: true
26
+ validates :area, presence: true, if: ->(form) { form.area_id.present? }
23
27
  validate :scope_exists
28
+ validate :notify_missing_attachment_if_errored
29
+ validate :trigger_attachment_errors
30
+ validates :signature_end_date, date: { after: Date.current }, if: lambda { |form|
31
+ form.context.initiative_type.custom_signature_end_date_enabled? && form.signature_end_date.present?
32
+ }
24
33
 
25
34
  def map_model(model)
26
35
  self.type_id = model.type.id
@@ -35,6 +44,10 @@ module Decidim
35
44
  super.presence
36
45
  end
37
46
 
47
+ def area
48
+ @area ||= current_organization.areas.find_by(id: area_id)
49
+ end
50
+
38
51
  private
39
52
 
40
53
  def scope_exists
@@ -42,6 +55,27 @@ module Decidim
42
55
 
43
56
  errors.add(:scope_id, :invalid) unless InitiativesTypeScope.where(decidim_initiatives_types_id: type_id, decidim_scopes_id: scope_id).exists?
44
57
  end
58
+
59
+ # This method will add an error to the `attachment` field only if there's
60
+ # any error in any other field. This is needed because when the form has
61
+ # an error, the attachment is lost, so we need a way to inform the user of
62
+ # this problem.
63
+ def notify_missing_attachment_if_errored
64
+ return if attachment.blank?
65
+
66
+ errors.add(:attachment, :needs_to_be_reattached) if errors.any?
67
+ end
68
+
69
+ def trigger_attachment_errors
70
+ return if attachment.blank?
71
+ return if attachment.valid?
72
+
73
+ attachment.errors.each { |error| errors.add(:attachment, error) }
74
+
75
+ attachment = Attachment.new(file: attachment.try(:file))
76
+
77
+ errors.add(:attachment, :file) if !attachment.save && attachment.errors.has_key?(:file)
78
+ end
45
79
  end
46
80
  end
47
81
  end
@@ -96,7 +96,9 @@ module Decidim
96
96
 
97
97
  errors.add(:base, :invalid) unless authorized? &&
98
98
  authorization_handler &&
99
- authorization_handler_metadata_variations.any? { |variation| authorization.metadata.symbolize_keys == variation.symbolize_keys }
99
+ authorization_handler_metadata_variations.any? do |variation|
100
+ authorization.metadata.symbolize_keys.except(:extras) == variation.symbolize_keys.except(:extras)
101
+ end
100
102
  end
101
103
 
102
104
  def author
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ # Custom helpers, scoped to the initiatives engine.
6
+ #
7
+ module ApplicationHelper
8
+ include Decidim::CheckBoxesTreeHelper
9
+
10
+ def filter_states_values
11
+ TreeNode.new(
12
+ TreePoint.new("", t("decidim.initiatives.application_helper.filter_state_values.all")),
13
+ [
14
+ TreePoint.new("open", t("decidim.initiatives.application_helper.filter_state_values.open")),
15
+ TreeNode.new(
16
+ TreePoint.new("closed", t("decidim.initiatives.application_helper.filter_state_values.closed")),
17
+ [
18
+ TreePoint.new("accepted", t("decidim.initiatives.application_helper.filter_state_values.accepted")),
19
+ TreePoint.new("rejected", t("decidim.initiatives.application_helper.filter_state_values.rejected"))
20
+ ]
21
+ ),
22
+ TreePoint.new("answered", t("decidim.initiatives.application_helper.filter_state_values.answered"))
23
+ ]
24
+ )
25
+ end
26
+
27
+ def filter_scopes_values
28
+ main_scopes = current_organization.scopes.top_level
29
+
30
+ scopes_values = main_scopes.includes(:scope_type, :children).flat_map do |scope|
31
+ TreeNode.new(
32
+ TreePoint.new(scope.id.to_s, translated_attribute(scope.name, current_organization)),
33
+ scope_children_to_tree(scope)
34
+ )
35
+ end
36
+
37
+ scopes_values.prepend(TreePoint.new("global", t("decidim.scopes.global")))
38
+
39
+ TreeNode.new(
40
+ TreePoint.new("", t("decidim.initiatives.application_helper.filter_scope_values.all")),
41
+ scopes_values
42
+ )
43
+ end
44
+
45
+ def scope_children_to_tree(scope)
46
+ return unless scope.children.any?
47
+
48
+ scope.children.includes(:scope_type, :children).flat_map do |child|
49
+ TreeNode.new(
50
+ TreePoint.new(child.id.to_s, translated_attribute(child.name, current_organization)),
51
+ scope_children_to_tree(child)
52
+ )
53
+ end
54
+ end
55
+
56
+ def filter_types_values
57
+ types_values = Decidim::InitiativesType.where(organization: current_organization).map do |type|
58
+ TreeNode.new(
59
+ TreePoint.new(type.id.to_s, type.title[I18n.locale.to_s])
60
+ )
61
+ end
62
+
63
+ TreeNode.new(
64
+ TreePoint.new("", t("decidim.initiatives.application_helper.filter_type_values.all")),
65
+ types_values
66
+ )
67
+ end
68
+
69
+ def filter_areas_values
70
+ areas_or_types = areas_for_select(current_organization)
71
+
72
+ areas_values = if areas_or_types.first.is_a?(Decidim::Area)
73
+ filter_areas(areas_or_types)
74
+ else
75
+ filter_areas_and_types(areas_or_types)
76
+ end
77
+
78
+ TreeNode.new(
79
+ TreePoint.new("", t("decidim.initiatives.application_helper.filter_area_values.all")),
80
+ areas_values
81
+ )
82
+ end
83
+
84
+ def filter_areas(areas)
85
+ areas.map do |area|
86
+ TreeNode.new(
87
+ TreePoint.new(area.id.to_s, area.name[I18n.locale.to_s])
88
+ )
89
+ end
90
+ end
91
+
92
+ def filter_areas_and_types(area_types)
93
+ area_types.map do |area_type|
94
+ TreeNode.new(
95
+ TreePoint.new(area_type.area_ids.join("_"), area_type.name[I18n.locale.to_s]),
96
+ area_type.areas.map do |area|
97
+ TreePoint.new(area.id.to_s, area.name[I18n.locale.to_s])
98
+ end
99
+ )
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -5,6 +5,7 @@ module Decidim
5
5
  # Helper method related to initiative object and its internal state.
6
6
  module InitiativeHelper
7
7
  include Decidim::SanitizeHelper
8
+ include Decidim::ResourceVersionsHelper
8
9
 
9
10
  # Public: The css class applied based on the initiative state to
10
11
  # the initiative badge.
@@ -99,6 +100,18 @@ module Decidim
99
100
 
100
101
  send("#{tag}_to", "", html_options, &block)
101
102
  end
103
+
104
+ def can_edit_custom_signature_end_date?(initiative)
105
+ return false unless initiative.custom_signature_end_date_enabled?
106
+
107
+ initiative.created? || initiative.validating?
108
+ end
109
+
110
+ def can_edit_area?(initiative)
111
+ return false unless initiative.area_enabled?
112
+
113
+ initiative.created? || initiative.validating?
114
+ end
102
115
  end
103
116
  end
104
117
  end
@@ -7,6 +7,7 @@ module Decidim
7
7
  def initiatives_filter_form_for(filter)
8
8
  content_tag :div, class: "filters" do
9
9
  form_for filter,
10
+ namespace: filter_form_namespace,
10
11
  builder: Decidim::Initiatives::InitiativesFilterFormBuilder,
11
12
  url: url_for,
12
13
  as: :filter,
@@ -17,6 +18,15 @@ module Decidim
17
18
  end
18
19
  end
19
20
  end
21
+
22
+ private
23
+
24
+ # Creates a unique namespace for a filter form to prevent dupliacte IDs in
25
+ # the DOM when multiple filter forms are rendered with the same fields (e.g.
26
+ # for desktop and mobile).
27
+ def filter_form_namespace
28
+ "filters_#{SecureRandom.uuid}"
29
+ end
20
30
  end
21
31
  end
22
32
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Initiatives
5
+ class ExportInitiativesJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(user, format)
9
+ export_data = Decidim::Exporters.find_exporter(format).new(collection, serializer).export
10
+
11
+ ExportMailer.export(user, "initiatives", export_data).deliver_now
12
+ end
13
+
14
+ private
15
+
16
+ def collection
17
+ Decidim::Initiative.all
18
+ end
19
+
20
+ def serializer
21
+ Decidim::Initiatives::InitiativeSerializer
22
+ end
23
+ end
24
+ end
25
+ end
@@ -51,27 +51,6 @@ module Decidim
51
51
  end
52
52
  end
53
53
 
54
- # Notify an initiative requesting technical validation
55
- def notify_validating_request(initiative, user)
56
- return if user.email.blank?
57
-
58
- @organization = initiative.organization
59
- @link = decidim_admin_initiatives.edit_initiative_url(initiative, host: @organization.host)
60
-
61
- with_user(user) do
62
- @subject = I18n.t(
63
- "decidim.initiatives.initiatives_mailer.technical_validation_for",
64
- title: translated_attribute(initiative.title)
65
- )
66
- @body = I18n.t(
67
- "decidim.initiatives.initiatives_mailer.technical_validation_body_for",
68
- title: translated_attribute(initiative.title)
69
- )
70
-
71
- mail(to: "#{user.name} <#{user.email}>", subject: @subject)
72
- end
73
- end
74
-
75
54
  # Notify progress to all initiative subscribers.
76
55
  def notify_progress(initiative, user)
77
56
  return if user.email.blank?
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Initiatives
7
+ module HasArea
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ belongs_to :area,
12
+ foreign_key: "decidim_area_id",
13
+ class_name: "Decidim::Area",
14
+ optional: true
15
+
16
+ delegate :areas, to: :organization
17
+
18
+ validate :area_belongs_to_organization
19
+ end
20
+
21
+ private
22
+
23
+ def area_belongs_to_organization
24
+ return unless area && organization
25
+
26
+ errors.add(:area, :invalid) unless areas.where(id: area.id).exists?
27
+ end
28
+ end
29
+ end
30
+ end
@@ -19,6 +19,7 @@ module Decidim
19
19
  include Decidim::HasReference
20
20
  include Decidim::Randomable
21
21
  include Decidim::Searchable
22
+ include Decidim::Initiatives::HasArea
22
23
 
23
24
  belongs_to :organization,
24
25
  foreign_key: "decidim_organization_id",
@@ -30,7 +31,8 @@ module Decidim
30
31
  inverse_of: :initiatives
31
32
 
32
33
  delegate :type, :scope, :scope_name, to: :scoped_type, allow_nil: true
33
- delegate :promoting_committee_enabled?, to: :type
34
+ delegate :attachments_enabled?, :promoting_committee_enabled?, :custom_signature_end_date_enabled?, :area_enabled?, to: :type
35
+ delegate :name, to: :area, prefix: true, allow_nil: true
34
36
 
35
37
  has_many :votes,
36
38
  foreign_key: "decidim_initiative_id",
@@ -67,24 +69,32 @@ module Decidim
67
69
  case_sensitive: false
68
70
 
69
71
  scope :open, lambda {
70
- published
71
- .where.not(state: [:discarded, :rejected, :accepted, :created])
72
- .where("signature_start_date <= ?", Date.current)
73
- .where("signature_end_date >= ?", Date.current)
72
+ where.not(state: [:discarded, :rejected, :accepted, :created])
73
+ .currently_signable
74
74
  }
75
75
  scope :closed, lambda {
76
- published
77
- .where(state: [:discarded, :rejected, :accepted])
78
- .or(where("signature_start_date > ?", Date.current))
79
- .or(where("signature_end_date < ?", Date.current))
76
+ where(state: [:discarded, :rejected, :accepted])
77
+ .or(currently_unsignable)
80
78
  }
81
79
  scope :published, -> { where.not(published_at: nil) }
82
80
  scope :with_state, ->(state) { where(state: state) if state.present? }
83
81
 
82
+ scope :currently_signable, lambda {
83
+ where("signature_start_date <= ?", Date.current)
84
+ .where("signature_end_date >= ?", Date.current)
85
+ }
86
+ scope :currently_unsignable, lambda {
87
+ where("signature_start_date > ?", Date.current)
88
+ .or(where("signature_end_date < ?", Date.current))
89
+ }
90
+
91
+ scope :answered, -> { where.not(answered_at: nil) }
92
+
84
93
  scope :public_spaces, -> { published }
85
94
  scope :signature_type_updatable, -> { created }
86
95
 
87
96
  scope :order_by_most_recent, -> { order(created_at: :desc) }
97
+ scope :order_by_most_recently_published, -> { order(published_at: :desc) }
88
98
  scope :order_by_supports, -> { order(Arel.sql("initiative_votes_count + coalesce(offline_votes, 0) desc")) }
89
99
  scope :order_by_most_commented, lambda {
90
100
  select("decidim_initiatives.*")
@@ -227,7 +237,7 @@ module Decidim
227
237
  published_at: Time.current,
228
238
  state: "published",
229
239
  signature_start_date: Date.current,
230
- signature_end_date: Date.current + Decidim::Initiatives.default_signature_time_period_length
240
+ signature_end_date: signature_end_date || Date.current + Decidim::Initiatives.default_signature_time_period_length
231
241
  )
232
242
  end
233
243
 
@@ -301,6 +311,10 @@ module Decidim
301
311
  committee_members.approved.where(decidim_users_id: user.id).any?
302
312
  end
303
313
 
314
+ def author_users
315
+ [author].concat(committee_members.excluding_author.map(&:user))
316
+ end
317
+
304
318
  def accepts_offline_votes?
305
319
  published? && (offline_signature_type? || any_signature_type?)
306
320
  end
@@ -368,5 +382,43 @@ module Decidim
368
382
 
369
383
  # Allow ransacker to search on an Enum Field
370
384
  ransacker :state, formatter: proc { |int| states[int] }
385
+
386
+ ransacker :type_id do
387
+ Arel.sql("decidim_initiatives_type_scopes.decidim_initiatives_types_id")
388
+ end
389
+
390
+ # method for sort_link by number of supports
391
+ ransacker :supports_count do
392
+ query = <<~SQL
393
+ (
394
+ SELECT
395
+ CASE
396
+ WHEN signature_type = 0 THEN 0
397
+ ELSE COALESCE(offline_votes, 0)
398
+ END
399
+ +
400
+ CASE
401
+ WHEN signature_type = 1 THEN 0
402
+ ELSE initiative_votes_count + initiative_supports_count
403
+ END
404
+ FROM decidim_initiatives as initiatives
405
+ WHERE initiatives.id = decidim_initiatives.id
406
+ GROUP BY initiatives.id
407
+ )
408
+ SQL
409
+ Arel.sql(query)
410
+ end
411
+
412
+ ransacker :id_string do
413
+ Arel.sql(%{cast("decidim_initiatives"."id" as text)})
414
+ end
415
+
416
+ ransacker :author_name do
417
+ Arel.sql("decidim_users.name")
418
+ end
419
+
420
+ ransacker :author_nickname do
421
+ Arel.sql("decidim_users.nickname")
422
+ end
371
423
  end
372
424
  end
@@ -35,6 +35,7 @@ module Decidim
35
35
  initiative_type_scope_action?
36
36
  initiative_committee_action?
37
37
  initiative_admin_user_action?
38
+ initiative_export_action?
38
39
  moderator_action?
39
40
  allow! if permission_action.subject == :attachment
40
41
 
@@ -69,6 +70,8 @@ module Decidim
69
70
  def attachment_action?
70
71
  return unless permission_action.subject == :attachment
71
72
 
73
+ disallow! && return unless initiative.attachments_enabled?
74
+
72
75
  attachment = context.fetch(:attachment, nil)
73
76
  attached = attachment&.attached_to
74
77
 
@@ -158,6 +161,10 @@ module Decidim
158
161
  end
159
162
  end
160
163
 
164
+ def initiative_export_action?
165
+ allow! if permission_action.subject == :initiatives && permission_action.action == :export
166
+ end
167
+
161
168
  def moderator_action?
162
169
  return unless permission_action.subject == :moderation
163
170