decidim-proposals 0.9.3 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -1
- data/app/assets/config/admin/decidim_proposals_manifest.js +1 -0
- data/app/assets/javascripts/decidim/proposals/admin/proposals.es6 +113 -0
- data/app/assets/javascripts/decidim/proposals/identity_selector_dialog.js.es6 +56 -0
- data/app/commands/decidim/proposals/admin/answer_proposal.rb +11 -5
- data/app/commands/decidim/proposals/admin/create_proposal.rb +25 -3
- data/app/commands/decidim/proposals/admin/create_proposal_note.rb +13 -8
- data/app/commands/decidim/proposals/admin/import_proposals.rb +83 -0
- data/app/commands/decidim/proposals/admin/update_proposal_category.rb +68 -0
- data/app/commands/decidim/proposals/create_proposal.rb +0 -12
- data/app/commands/decidim/proposals/endorse_proposal.rb +56 -0
- data/app/commands/decidim/proposals/publish_proposal.rb +60 -0
- data/app/commands/decidim/proposals/unendorse_proposal.rb +40 -0
- data/app/commands/decidim/proposals/update_proposal.rb +3 -3
- data/app/commands/decidim/proposals/vote_proposal.rb +1 -1
- data/app/commands/decidim/proposals/withdraw_proposal.rb +1 -1
- data/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +1 -1
- data/app/controllers/decidim/proposals/admin/proposal_notes_controller.rb +2 -2
- data/app/controllers/decidim/proposals/admin/proposals_controller.rb +50 -1
- data/app/controllers/decidim/proposals/admin/proposals_imports_controller.rb +35 -0
- data/app/controllers/decidim/proposals/proposal_endorsements_controller.rb +56 -0
- data/app/controllers/decidim/proposals/proposals_controller.rb +82 -9
- data/app/events/decidim/proposals/admin/update_proposal_category_event.rb +11 -0
- data/app/events/decidim/proposals/creation_enabled_event.rb +8 -0
- data/app/events/decidim/proposals/endorsing_enabled_event.rb +8 -0
- data/app/events/decidim/proposals/proposal_endorsed_event.rb +29 -0
- data/app/events/decidim/proposals/publish_proposal_event.rb +21 -0
- data/app/events/decidim/proposals/voting_enabled_event.rb +8 -0
- data/app/forms/decidim/proposals/admin/proposal_form.rb +9 -2
- data/app/forms/decidim/proposals/admin/proposals_import_form.rb +60 -0
- data/app/forms/decidim/proposals/proposal_form.rb +16 -5
- data/app/helpers/decidim/proposals/application_helper.rb +1 -0
- data/app/helpers/decidim/proposals/proposal_endorsements_helper.rb +117 -0
- data/app/helpers/decidim/proposals/proposal_votes_helper.rb +13 -6
- data/app/helpers/decidim/proposals/proposal_wizard_helper.rb +105 -0
- data/app/jobs/decidim/proposals/settings_change_job.rb +48 -0
- data/app/models/decidim/proposals/abilities/current_user_ability.rb +30 -8
- data/app/models/decidim/proposals/proposal.rb +38 -38
- data/app/models/decidim/proposals/proposal_endorsement.rb +31 -0
- data/app/models/decidim/proposals/proposal_note.rb +7 -0
- data/app/presenters/decidim/proposals/admin_log/proposal_note_presenter.rb +39 -0
- data/app/presenters/decidim/proposals/admin_log/proposal_presenter.rb +47 -0
- data/app/presenters/decidim/proposals/admin_log/value_types/proposal_state_presenter.rb +16 -0
- data/app/queries/decidim/proposals/similar_proposals.rb +53 -0
- data/app/types/decidim/proposals/proposal_type.rb +34 -0
- data/app/types/decidim/proposals/proposals_type.rb +34 -0
- data/app/views/decidim/participatory_processes/participatory_process_groups/_highlighted_proposals.html.erb +8 -0
- data/app/views/decidim/participatory_processes/participatory_process_groups/_proposal.html.erb +27 -0
- data/app/views/decidim/participatory_spaces/_highlighted_proposals.html.erb +10 -0
- data/app/views/decidim/participatory_spaces/_proposal.html.erb +27 -0
- data/app/views/decidim/proposals/admin/proposals/_bulk-actions.html.erb +15 -0
- data/app/views/decidim/proposals/admin/proposals/_js-callout.html.erb +6 -0
- data/app/views/decidim/proposals/admin/proposals/_proposal-tr.html.erb +63 -0
- data/app/views/decidim/proposals/admin/proposals/index.html.erb +12 -73
- data/app/views/decidim/proposals/admin/proposals/update_category.js.erb +25 -0
- data/app/views/decidim/proposals/admin/proposals_imports/new.html.erb +28 -0
- data/app/views/decidim/proposals/proposal_endorsements/_identity.html.erb +4 -0
- data/app/views/decidim/proposals/proposal_endorsements/identities.html.erb +12 -0
- data/app/views/decidim/proposals/proposal_endorsements/update_buttons_and_counters.js.erb +9 -0
- data/app/views/decidim/proposals/proposals/_endorsement_button.html.erb +11 -0
- data/app/views/decidim/proposals/proposals/_endorsement_identities_cabin.html.erb +13 -0
- data/app/views/decidim/proposals/proposals/_endorsement_xxs.html.erb +9 -0
- data/app/views/decidim/proposals/proposals/_endorsements_card_row.html.erb +22 -0
- data/app/views/decidim/proposals/proposals/_endorsements_count.html.erb +5 -0
- data/app/views/decidim/proposals/proposals/_endorsements_listing.html.erb +34 -0
- data/app/views/decidim/proposals/proposals/_proposal.html.erb +2 -2
- data/app/views/decidim/proposals/proposals/_proposal_preview.html.erb +36 -0
- data/app/views/decidim/proposals/proposals/_proposal_similar.html.erb +21 -0
- data/app/views/decidim/proposals/proposals/_vote_button.html.erb +8 -8
- data/app/views/decidim/proposals/proposals/_votes_count.html.erb +23 -6
- data/app/views/decidim/proposals/proposals/_voting_rules.html.erb +7 -3
- data/app/views/decidim/proposals/proposals/_wizard_aside.html.erb +16 -0
- data/app/views/decidim/proposals/proposals/_wizard_header.html.erb +31 -0
- data/app/views/decidim/proposals/proposals/compare.html.erb +19 -0
- data/app/views/decidim/proposals/proposals/edit_draft.html.erb +55 -0
- data/app/views/decidim/proposals/proposals/new.html.erb +7 -20
- data/app/views/decidim/proposals/proposals/preview.html.erb +18 -0
- data/app/views/decidim/proposals/proposals/show.html.erb +13 -4
- data/config/locales/ca.yml +156 -15
- data/config/locales/en.yml +156 -15
- data/config/locales/es.yml +157 -16
- data/config/locales/eu.yml +151 -7
- data/config/locales/fi.yml +151 -7
- data/config/locales/fr.yml +153 -9
- data/config/locales/gl.yml +151 -7
- data/config/locales/it.yml +151 -7
- data/config/locales/nl.yml +151 -7
- data/config/locales/pl.yml +148 -22
- data/config/locales/pt-BR.yml +151 -7
- data/config/locales/pt.yml +151 -7
- data/config/locales/ru.yml +0 -9
- data/config/locales/sv.yml +151 -7
- data/config/locales/uk.yml +87 -13
- data/db/migrate/20170307085300_migrate_proposal_reports_data_to_reports.rb +1 -1
- data/db/migrate/20171201115434_create_proposal_endorsements.rb +16 -0
- data/db/migrate/20171201122623_add_counter_cache_endorsements_to_proposals.rb +8 -0
- data/db/migrate/20171212102250_enable_pg_extensions.rb +7 -0
- data/db/migrate/20171220084719_add_published_at_to_proposals.rb +14 -0
- data/lib/decidim/proposals.rb +15 -0
- data/lib/decidim/proposals/admin_engine.rb +8 -0
- data/lib/decidim/proposals/commentable_proposal.rb +39 -0
- data/lib/decidim/proposals/engine.rb +69 -1
- data/lib/decidim/proposals/feature.rb +51 -6
- data/lib/decidim/proposals/test/factories.rb +78 -2
- data/lib/decidim/proposals/version.rb +1 -1
- metadata +76 -20
- data/app/events/decidim/proposals/create_proposal_event.rb +0 -9
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
class SettingsChangeJob < ApplicationJob
|
6
|
+
def perform(feature_id, previous_settings, current_settings)
|
7
|
+
feature = Decidim::Feature.find(feature_id)
|
8
|
+
|
9
|
+
if creation_enabled?(previous_settings, current_settings)
|
10
|
+
event = "decidim.events.proposals.creation_enabled"
|
11
|
+
event_class = Decidim::Proposals::CreationEnabledEvent
|
12
|
+
elsif voting_enabled?(previous_settings, current_settings)
|
13
|
+
event = "decidim.events.proposals.voting_enabled"
|
14
|
+
event_class = Decidim::Proposals::VotingEnabledEvent
|
15
|
+
elsif endorsing_enabled?(previous_settings, current_settings)
|
16
|
+
event = "decidim.events.proposals.endorsing_enabled"
|
17
|
+
event_class = Decidim::Proposals::EndorsingEnabledEvent
|
18
|
+
end
|
19
|
+
|
20
|
+
return unless event && event_class
|
21
|
+
|
22
|
+
Decidim::EventsManager.publish(
|
23
|
+
event: event,
|
24
|
+
event_class: event_class,
|
25
|
+
resource: feature,
|
26
|
+
recipient_ids: feature.participatory_space.followers.pluck(:id)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def creation_enabled?(previous_settings, current_settings)
|
33
|
+
current_settings[:creation_enabled] == true &&
|
34
|
+
previous_settings[:creation_enabled] == false
|
35
|
+
end
|
36
|
+
|
37
|
+
def voting_enabled?(previous_settings, current_settings)
|
38
|
+
(current_settings[:votes_enabled] == true && current_settings[:votes_blocked] == false) &&
|
39
|
+
(previous_settings[:votes_enabled] == false || previous_settings[:votes_blocked] == true)
|
40
|
+
end
|
41
|
+
|
42
|
+
def endorsing_enabled?(previous_settings, current_settings)
|
43
|
+
(current_settings[:endorsements_enabled] == true && current_settings[:endorsements_blocked] == false) &&
|
44
|
+
(previous_settings[:endorsements_enabled] == false || previous_settings[:endorsements_blocked] == true)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -16,14 +16,8 @@ module Decidim
|
|
16
16
|
@user = user
|
17
17
|
@context = context
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
can :unvote, Proposal do |_proposal|
|
24
|
-
authorized?(:vote) && voting_enabled?
|
25
|
-
end
|
26
|
-
|
19
|
+
setup_endorsement_related_abilities
|
20
|
+
setup_voting_related_abilities
|
27
21
|
can :create, Proposal if authorized?(:create) && creation_enabled?
|
28
22
|
can :edit, Proposal do |proposal|
|
29
23
|
proposal.editable_by?(user)
|
@@ -38,6 +32,24 @@ module Decidim
|
|
38
32
|
|
39
33
|
private
|
40
34
|
|
35
|
+
def setup_endorsement_related_abilities
|
36
|
+
can :endorse, Proposal do |_proposal|
|
37
|
+
authorized?(:endorse) && endorsements_enabled? && !endorsements_blocked?
|
38
|
+
end
|
39
|
+
can :unendorse, Proposal do |_proposal|
|
40
|
+
authorized?(:unendorse) && endorsements_enabled?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def setup_voting_related_abilities
|
45
|
+
can :vote, Proposal do |_proposal|
|
46
|
+
authorized?(:vote) && voting_enabled? && remaining_votes.positive?
|
47
|
+
end
|
48
|
+
can :unvote, Proposal do |_proposal|
|
49
|
+
authorized?(:vote) && voting_enabled?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
41
53
|
def authorized?(action)
|
42
54
|
return unless feature
|
43
55
|
|
@@ -62,6 +74,16 @@ module Decidim
|
|
62
74
|
feature_settings.vote_limit - votes_count
|
63
75
|
end
|
64
76
|
|
77
|
+
def endorsements_enabled?
|
78
|
+
return unless current_settings
|
79
|
+
current_settings.endorsements_enabled?
|
80
|
+
end
|
81
|
+
|
82
|
+
def endorsements_blocked?
|
83
|
+
return unless current_settings
|
84
|
+
current_settings.endorsements_blocked?
|
85
|
+
end
|
86
|
+
|
65
87
|
def voting_enabled?
|
66
88
|
return unless current_settings
|
67
89
|
current_settings.votes_enabled? && !current_settings.votes_blocked?
|
@@ -7,16 +7,19 @@ module Decidim
|
|
7
7
|
include Decidim::Resourceable
|
8
8
|
include Decidim::Authorable
|
9
9
|
include Decidim::HasFeature
|
10
|
-
include Decidim::
|
10
|
+
include Decidim::ScopableFeature
|
11
11
|
include Decidim::HasReference
|
12
12
|
include Decidim::HasCategory
|
13
13
|
include Decidim::Reportable
|
14
14
|
include Decidim::HasAttachments
|
15
15
|
include Decidim::Followable
|
16
|
-
include Decidim::
|
16
|
+
include Decidim::Proposals::CommentableProposal
|
17
|
+
include Decidim::Traceable
|
18
|
+
include Decidim::Loggable
|
17
19
|
|
18
20
|
feature_manifest_name "proposals"
|
19
21
|
|
22
|
+
has_many :endorsements, foreign_key: "decidim_proposal_id", class_name: "ProposalEndorsement", dependent: :destroy, counter_cache: "proposal_endorsements_count"
|
20
23
|
has_many :votes, foreign_key: "decidim_proposal_id", class_name: "ProposalVote", dependent: :destroy, counter_cache: "proposal_votes_count"
|
21
24
|
has_many :notes, foreign_key: "decidim_proposal_id", class_name: "ProposalNote", dependent: :destroy, counter_cache: "proposal_notes_count"
|
22
25
|
|
@@ -29,6 +32,7 @@ module Decidim
|
|
29
32
|
scope :evaluating, -> { where(state: "evaluating") }
|
30
33
|
scope :withdrawn, -> { where(state: "withdrawn") }
|
31
34
|
scope :except_withdrawn, -> { where.not(state: "withdrawn").or(where(state: nil)) }
|
35
|
+
scope :published, -> { where.not(published_at: nil) }
|
32
36
|
|
33
37
|
def self.order_randomly(seed)
|
34
38
|
transaction do
|
@@ -37,6 +41,10 @@ module Decidim
|
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
44
|
+
def self.log_presenter_class_for(_log)
|
45
|
+
Decidim::Proposals::AdminLog::ProposalPresenter
|
46
|
+
end
|
47
|
+
|
40
48
|
# Public: Check if the user has voted the proposal.
|
41
49
|
#
|
42
50
|
# Returns Boolean.
|
@@ -44,6 +52,14 @@ module Decidim
|
|
44
52
|
votes.where(author: user).any?
|
45
53
|
end
|
46
54
|
|
55
|
+
# Public: Check if the user has endorsed the proposal.
|
56
|
+
# - user_group: may be nil if user is not representing any user_group.
|
57
|
+
#
|
58
|
+
# Returns Boolean.
|
59
|
+
def endorsed_by?(user, user_group = nil)
|
60
|
+
endorsements.where(author: user, user_group: user_group).any?
|
61
|
+
end
|
62
|
+
|
47
63
|
# Public: Checks if the organization has given an answer for the proposal.
|
48
64
|
#
|
49
65
|
# Returns Boolean.
|
@@ -79,32 +95,6 @@ module Decidim
|
|
79
95
|
state == "withdrawn"
|
80
96
|
end
|
81
97
|
|
82
|
-
# Public: Overrides the `commentable?` Commentable concern method.
|
83
|
-
def commentable?
|
84
|
-
feature.settings.comments_enabled?
|
85
|
-
end
|
86
|
-
|
87
|
-
# Public: Overrides the `accepts_new_comments?` Commentable concern method.
|
88
|
-
def accepts_new_comments?
|
89
|
-
commentable? && !feature.current_settings.comments_blocked
|
90
|
-
end
|
91
|
-
|
92
|
-
# Public: Overrides the `comments_have_alignment?` Commentable concern method.
|
93
|
-
def comments_have_alignment?
|
94
|
-
true
|
95
|
-
end
|
96
|
-
|
97
|
-
# Public: Overrides the `comments_have_votes?` Commentable concern method.
|
98
|
-
def comments_have_votes?
|
99
|
-
true
|
100
|
-
end
|
101
|
-
|
102
|
-
# Public: Override Commentable concern method `users_to_notify_on_comment_created`
|
103
|
-
def users_to_notify_on_comment_created
|
104
|
-
return (followers | feature.participatory_space.admins).uniq if official?
|
105
|
-
followers
|
106
|
-
end
|
107
|
-
|
108
98
|
# Public: Overrides the `reported_content_url` Reportable concern method.
|
109
99
|
def reported_content_url
|
110
100
|
ResourceLocatorPresenter.new(self).url
|
@@ -119,7 +109,7 @@ module Decidim
|
|
119
109
|
#
|
120
110
|
# Returns an Integer with the maximum amount of votes, nil otherwise.
|
121
111
|
def maximum_votes
|
122
|
-
maximum_votes = feature.settings.
|
112
|
+
maximum_votes = feature.settings.threshold_per_proposal
|
123
113
|
return nil if maximum_votes.zero?
|
124
114
|
|
125
115
|
maximum_votes
|
@@ -134,26 +124,31 @@ module Decidim
|
|
134
124
|
votes.count >= maximum_votes
|
135
125
|
end
|
136
126
|
|
137
|
-
#
|
138
|
-
# authoring it or via a user group.
|
127
|
+
# Public: Can accumulate more votres than maximum for this proposal.
|
139
128
|
#
|
140
|
-
#
|
141
|
-
def
|
142
|
-
|
129
|
+
# Returns true if can accumulate, false otherwise
|
130
|
+
def can_accumulate_supports_beyond_threshold
|
131
|
+
feature.settings.can_accumulate_supports_beyond_threshold
|
143
132
|
end
|
144
133
|
|
145
134
|
# Checks whether the user can edit the given proposal.
|
146
135
|
#
|
147
136
|
# user - the user to check for authorship
|
148
137
|
def editable_by?(user)
|
149
|
-
|
138
|
+
return true if draft?
|
139
|
+
authored_by?(user) && !answered? && within_edit_time_limit? && !copied_from_other_component?
|
150
140
|
end
|
151
141
|
|
152
142
|
# Checks whether the user can withdraw the given proposal.
|
153
143
|
#
|
154
144
|
# user - the user to check for withdrawability.
|
155
145
|
def withdrawable_by?(user)
|
156
|
-
user && !withdrawn? && authored_by?(user)
|
146
|
+
user && !withdrawn? && authored_by?(user) && !copied_from_other_component?
|
147
|
+
end
|
148
|
+
|
149
|
+
# Public: Whether the proposal is a draft or not.
|
150
|
+
def draft?
|
151
|
+
published_at.nil?
|
157
152
|
end
|
158
153
|
|
159
154
|
# method for sort_link by number of comments
|
@@ -171,11 +166,16 @@ module Decidim
|
|
171
166
|
|
172
167
|
private
|
173
168
|
|
174
|
-
# Checks whether the proposal is inside the time window to be editable or not.
|
169
|
+
# Checks whether the proposal is inside the time window to be editable or not once published.
|
175
170
|
def within_edit_time_limit?
|
176
|
-
|
171
|
+
return true if draft?
|
172
|
+
limit = updated_at + feature.settings.proposal_edit_before_minutes.minutes
|
177
173
|
Time.current < limit
|
178
174
|
end
|
175
|
+
|
176
|
+
def copied_from_other_component?
|
177
|
+
linked_resources(:proposals, "copied_from_component").any?
|
178
|
+
end
|
179
179
|
end
|
180
180
|
end
|
181
181
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
# A proposal can include an endorsement per user or group.
|
6
|
+
class ProposalEndorsement < ApplicationRecord
|
7
|
+
belongs_to :proposal, foreign_key: "decidim_proposal_id", class_name: "Decidim::Proposals::Proposal", counter_cache: true
|
8
|
+
belongs_to :author, foreign_key: "decidim_author_id", class_name: "Decidim::User"
|
9
|
+
belongs_to :user_group, foreign_key: "decidim_user_group_id", class_name: "Decidim::UserGroup", optional: true
|
10
|
+
|
11
|
+
validates :proposal, uniqueness: { scope: [:author, :user_group] }
|
12
|
+
validate :author_and_proposal_same_organization
|
13
|
+
validate :proposal_not_rejected
|
14
|
+
|
15
|
+
scope :for_listing, -> { order(:decidim_user_group_id, :created_at) }
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Private: check if the proposal and the author have the same organization
|
20
|
+
def author_and_proposal_same_organization
|
21
|
+
return if !proposal || !author
|
22
|
+
errors.add(:proposal, :invalid) unless author.organization == proposal.organization
|
23
|
+
end
|
24
|
+
|
25
|
+
def proposal_not_rejected
|
26
|
+
return unless proposal
|
27
|
+
errors.add(:proposal, :invalid) if proposal.rejected?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -4,10 +4,17 @@ module Decidim
|
|
4
4
|
module Proposals
|
5
5
|
# A proposal can include a notes created by admins.
|
6
6
|
class ProposalNote < ApplicationRecord
|
7
|
+
include Decidim::Traceable
|
8
|
+
include Decidim::Loggable
|
9
|
+
|
7
10
|
belongs_to :proposal, foreign_key: "decidim_proposal_id", class_name: "Decidim::Proposals::Proposal", counter_cache: true
|
8
11
|
belongs_to :author, foreign_key: "decidim_author_id", class_name: "Decidim::User"
|
9
12
|
|
10
13
|
default_scope { order(created_at: :asc) }
|
14
|
+
|
15
|
+
def self.log_presenter_class_for(_log)
|
16
|
+
Decidim::Proposals::AdminLog::ProposalNotePresenter
|
17
|
+
end
|
11
18
|
end
|
12
19
|
end
|
13
20
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
module AdminLog
|
6
|
+
# This class holds the logic to present a `Decidim::Proposals::ProposalNote`
|
7
|
+
# for the `AdminLog` log.
|
8
|
+
#
|
9
|
+
# Usage should be automatic and you shouldn't need to call this class
|
10
|
+
# directly, but here's an example:
|
11
|
+
#
|
12
|
+
# action_log = Decidim::ActionLog.last
|
13
|
+
# view_helpers # => this comes from the views
|
14
|
+
# ProposalNotePresenter.new(action_log, view_helpers).present
|
15
|
+
class ProposalNotePresenter < Decidim::Log::BasePresenter
|
16
|
+
private
|
17
|
+
|
18
|
+
def diff_fields_mapping
|
19
|
+
{
|
20
|
+
body: :string
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def action_string
|
25
|
+
case action
|
26
|
+
when "create"
|
27
|
+
"decidim.proposals.admin_log.proposal_note.#{action}"
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def i18n_labels_scope
|
34
|
+
"activemodel.attributes.proposal_note"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
module AdminLog
|
6
|
+
# This class holds the logic to present a `Decidim::Proposals::Proposal`
|
7
|
+
# for the `AdminLog` log.
|
8
|
+
#
|
9
|
+
# Usage should be automatic and you shouldn't need to call this class
|
10
|
+
# directly, but here's an example:
|
11
|
+
#
|
12
|
+
# action_log = Decidim::ActionLog.last
|
13
|
+
# view_helpers # => this comes from the views
|
14
|
+
# ProposalPresenter.new(action_log, view_helpers).present
|
15
|
+
class ProposalPresenter < Decidim::Log::BasePresenter
|
16
|
+
private
|
17
|
+
|
18
|
+
def diff_fields_mapping
|
19
|
+
{
|
20
|
+
title: :string,
|
21
|
+
body: :string,
|
22
|
+
state: "Decidim::Proposals::AdminLog::ValueTypes::ProposalStatePresenter",
|
23
|
+
answered_at: :date,
|
24
|
+
answer: :i18n
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def action_string
|
29
|
+
case action
|
30
|
+
when "answer", "create"
|
31
|
+
"decidim.proposals.admin_log.proposal.#{action}"
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def i18n_labels_scope
|
38
|
+
"activemodel.attributes.proposal"
|
39
|
+
end
|
40
|
+
|
41
|
+
def has_diff?
|
42
|
+
action == "answer" || super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
module AdminLog
|
6
|
+
module ValueTypes
|
7
|
+
class ProposalStatePresenter < Decidim::Log::ValueTypes::DefaultPresenter
|
8
|
+
def present
|
9
|
+
return unless value
|
10
|
+
h.t(value, scope: "decidim.proposals.admin.proposal_answers.edit", default: value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
# Class used to retrieve similar proposals.
|
6
|
+
class SimilarProposals < Rectify::Query
|
7
|
+
include Decidim::TranslationsHelper
|
8
|
+
|
9
|
+
# Syntactic sugar to initialize the class and return the queried objects.
|
10
|
+
#
|
11
|
+
# features - Decidim::CurrentFeature
|
12
|
+
# proposal - Decidim::Proposals::Proposal
|
13
|
+
def self.for(features, proposal)
|
14
|
+
new(features, proposal).query
|
15
|
+
end
|
16
|
+
|
17
|
+
# Initializes the class.
|
18
|
+
#
|
19
|
+
# features - Decidim::CurrentFeature
|
20
|
+
# proposal - Decidim::Proposals::Proposal
|
21
|
+
def initialize(features, proposal)
|
22
|
+
@features = features
|
23
|
+
@proposal = proposal
|
24
|
+
end
|
25
|
+
|
26
|
+
# Retrieves similar proposals
|
27
|
+
def query
|
28
|
+
Decidim::Proposals::Proposal
|
29
|
+
.where(feature: @features)
|
30
|
+
.published
|
31
|
+
.where(
|
32
|
+
"GREATEST(#{title_similarity}, #{body_similarity}) >= ?",
|
33
|
+
proposal.title,
|
34
|
+
proposal.body,
|
35
|
+
Decidim::Proposals.similarity_threshold
|
36
|
+
)
|
37
|
+
.limit(Decidim::Proposals.similarity_limit)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :proposal
|
43
|
+
|
44
|
+
def title_similarity
|
45
|
+
"similarity(title, ?)"
|
46
|
+
end
|
47
|
+
|
48
|
+
def body_similarity
|
49
|
+
"similarity(body, ?)"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|