Class: Decidim::Proposals::Proposal
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Decidim::Proposals::Proposal
- Includes:
- Amendable, Coauthorable, DownloadYourData, FilterableResource, Fingerprintable, Followable, HasAttachments, HasCategory, HasComponent, HasReference, Likeable, Loggable, NewsletterParticipant, CommentableProposal, Evaluable, ParticipatoryTextSection, Decidim::Publicable, Randomable, Reportable, Resourceable, ScopableResource, Searchable, SoftDeletable, Taxonomizable, Traceable, TranslatableAttributes, TranslatableResource
- Defined in:
- app/models/decidim/proposals/proposal.rb
Overview
The data store for a Proposal in the Decidim::Proposals component.
Constant Summary
Constants included from ParticipatoryTextSection
Decidim::Proposals::ParticipatoryTextSection::LEVELS
Class Method Summary collapse
- .download_your_data_images(user) ⇒ Object
-
.evaluator_role_ids_has(value) ⇒ Object
method to filter by assigned evaluator role ID.
- .export_serializer ⇒ Object
- .log_presenter_class_for(_log) ⇒ Object
- .most_commented_available?(component) ⇒ Boolean
- .most_liked_available?(component) ⇒ Boolean
- .newsletter_participant_ids(component) ⇒ Object
- .ransack(params = {}, options = {}) ⇒ Object
- .ransackable_associations(_auth_object = nil) ⇒ Object
- .ransackable_attributes(_auth_object = nil) ⇒ Object
- .ransackable_scopes(_auth_object = nil) ⇒ Object
- .retrieve_proposals_for(component) ⇒ Object
- .sort_by_translated_title_asc ⇒ Object
- .sort_by_translated_title_desc ⇒ Object
-
.user_collection(author) ⇒ Object
Returns a collection scoped by an author.
- .with_evaluation_assigned_to(user, space) ⇒ Object
- .with_more_authors_available?(component) ⇒ Boolean
Instance Method Summary collapse
-
#accepted? ⇒ Boolean
Public: Checks if the organization has accepted a proposal.
- #actions_for_comment(comment, current_user) ⇒ Object
-
#allow_resource_permissions? ⇒ Boolean
Public: Overrides the ‘allow_resource_permissions?` Resourceable concern method.
-
#answered? ⇒ Boolean
Public: Checks if the organization has given an answer for the proposal.
- #assign_state(token) ⇒ Object
-
#can_accumulate_votes_beyond_threshold ⇒ Object
Public: Can accumulate more votes than maximum for this proposal.
- #coauthor_invitations_for(user) ⇒ Object
-
#draft? ⇒ Boolean
Public: Whether the proposal is a draft or not.
-
#editable_by?(user) ⇒ Boolean
Checks whether the user can edit the given proposal.
-
#evaluating? ⇒ Boolean
Public: Checks if the organization has marked the proposal as evaluating it.
-
#internal_state ⇒ Object
Public: Returns the internal state of the proposal.
-
#maximum_votes ⇒ Object
Public: The maximum amount of votes allowed for this proposal.
-
#maximum_votes_reached? ⇒ Boolean
Public: The maximum amount of votes allowed for this proposal.
-
#official? ⇒ Boolean
Public: Whether the proposal is official or not.
-
#official_meeting? ⇒ Boolean
Public: Whether the proposal is created in a meeting or not.
-
#presenter ⇒ Object
Returns the presenter for this author, to be used in the views.
- #process_amendment_state_change! ⇒ Object
-
#published_state? ⇒ Boolean
Public: Checks if the organization has published the state for the proposal.
-
#rejected? ⇒ Boolean
Public: Checks if the organization has rejected a proposal.
-
#reported_attributes ⇒ Object
Public: Overrides the ‘reported_attributes` Reportable concern method.
-
#reported_searchable_content_extras ⇒ Object
Public: Overrides the ‘reported_searchable_content_extras` Reportable concern method.
-
#state ⇒ Object
Public: Returns the published state of the proposal.
-
#update_votes_count ⇒ Object
Public: Updates the vote count of this proposal.
- #user_has_actions?(user) ⇒ Boolean
-
#voted_by?(user) ⇒ Boolean
Public: Check if the user has voted the proposal.
- #withdraw! ⇒ Object
-
#withdrawable_by?(user) ⇒ Boolean
Checks whether the user can withdraw the given proposal.
-
#withdrawn? ⇒ Boolean
Public: Checks if the author has withdrawn the proposal.
-
#within_edit_time_limit? ⇒ Boolean
Checks whether the proposal is inside the time window to be editable or not once published.
Class Method Details
.download_your_data_images(user) ⇒ Object
485 486 487 |
# File 'app/models/decidim/proposals/proposal.rb', line 485 def self.download_your_data_images(user) user_collection(user).map { |p| p..collect(&:file) } end |
.evaluator_role_ids_has(value) ⇒ Object
method to filter by assigned evaluator role ID
409 410 411 412 413 414 415 416 417 418 419 |
# File 'app/models/decidim/proposals/proposal.rb', line 409 def self.evaluator_role_ids_has(value) query = <<~SQL.squish :value = any( (SELECT decidim_proposals_evaluation_assignments.evaluator_role_id FROM decidim_proposals_evaluation_assignments WHERE decidim_proposals_evaluation_assignments.decidim_proposal_id = decidim_proposals_proposals.id ) ) SQL where(query, value:) end |
.export_serializer ⇒ Object
481 482 483 |
# File 'app/models/decidim/proposals/proposal.rb', line 481 def self.export_serializer Decidim::Proposals::DownloadYourDataProposalSerializer end |
.log_presenter_class_for(_log) ⇒ Object
209 210 211 |
# File 'app/models/decidim/proposals/proposal.rb', line 209 def self.log_presenter_class_for(_log) Decidim::Proposals::AdminLog::ProposalPresenter end |
.most_commented_available?(component) ⇒ Boolean
177 178 179 180 181 182 183 184 185 186 |
# File 'app/models/decidim/proposals/proposal.rb', line 177 def self.most_commented_available?(component) return false unless component.settings.comments_enabled? where(component:) .published .not_hidden .not_withdrawn .where("comments_count > 0") .exists? end |
.most_liked_available?(component) ⇒ Boolean
188 189 190 191 192 193 194 195 |
# File 'app/models/decidim/proposals/proposal.rb', line 188 def self.most_liked_available?(component) where(component:) .published .not_hidden .not_withdrawn .where("likes_count > 0") .exists? end |
.newsletter_participant_ids(component) ⇒ Object
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'app/models/decidim/proposals/proposal.rb', line 232 def self.(component) proposals = retrieve_proposals_for(component).uniq = proposals.map { |p| p.notifiable_identities.pluck(:id) }.flatten.compact.uniq participants_has_voted_ids = Decidim::Proposals::ProposalVote.joins(:proposal).where(proposal: proposals).joins(:author).map(&:decidim_author_id).flatten.compact.uniq likes_participants_ids = Decidim::Like.where(resource: proposals) .where(decidim_author_type: "Decidim::UserBaseEntity") .pluck(:decidim_author_id).to_a.compact.uniq commentators_ids = Decidim::Comments::Comment.user_commentators_ids_in(proposals) (likes_participants_ids + participants_has_voted_ids + + commentators_ids).flatten.compact.uniq end |
.ransack(params = {}, options = {}) ⇒ Object
404 405 406 |
# File 'app/models/decidim/proposals/proposal.rb', line 404 def self.ransack(params = {}, = {}) ProposalSearch.new(self, params, ) end |
.ransackable_associations(_auth_object = nil) ⇒ Object
433 434 435 |
# File 'app/models/decidim/proposals/proposal.rb', line 433 def self.ransackable_associations(_auth_object = nil) %w(taxonomies proposal_state) end |
.ransackable_attributes(_auth_object = nil) ⇒ Object
429 430 431 |
# File 'app/models/decidim/proposals/proposal.rb', line 429 def self.ransackable_attributes(_auth_object = nil) %w(id_string search_text title body is_emendation comments_count proposal_votes_count published_at proposal_notes_count) end |
.ransackable_scopes(_auth_object = nil) ⇒ Object
421 422 423 |
# File 'app/models/decidim/proposals/proposal.rb', line 421 def self.ransackable_scopes(_auth_object = nil) [:with_any_origin, :with_any_state, :state_eq, :voted_by, :coauthored_by, :related_to, :with_any_taxonomies, :evaluator_role_ids_has] end |
.retrieve_proposals_for(component) ⇒ Object
223 224 225 226 227 228 229 230 |
# File 'app/models/decidim/proposals/proposal.rb', line 223 def self.retrieve_proposals_for(component) Decidim::Proposals::Proposal.where(component:).joins(:coauthorships) .includes(:votes, :likes) .where(decidim_coauthorships: { decidim_author_type: "Decidim::UserBaseEntity" }) .not_hidden .published .not_withdrawn end |
.sort_by_translated_title_asc ⇒ Object
450 451 452 453 |
# File 'app/models/decidim/proposals/proposal.rb', line 450 def self.sort_by_translated_title_asc field = Arel::Nodes::InfixOperation.new("->>", arel_table[:title], Arel::Nodes.build_quoted(I18n.locale)) order(Arel::Nodes::InfixOperation.new("", field, Arel.sql("ASC"))) end |
.sort_by_translated_title_desc ⇒ Object
455 456 457 458 |
# File 'app/models/decidim/proposals/proposal.rb', line 455 def self.sort_by_translated_title_desc field = Arel::Nodes::InfixOperation.new("->>", arel_table[:title], Arel::Nodes.build_quoted(I18n.locale)) order(Arel::Nodes::InfixOperation.new("", field, Arel.sql("DESC"))) end |
.user_collection(author) ⇒ Object
Returns a collection scoped by an author. Overrides this method in DownloadYourData to support Coauthorable.
215 216 217 218 219 220 221 |
# File 'app/models/decidim/proposals/proposal.rb', line 215 def self.user_collection() return unless .is_a?(Decidim::User) joins(:coauthorships) .where(decidim_coauthorships: { coauthorable_type: name }) .where("decidim_coauthorships.decidim_author_id = ? AND decidim_coauthorships.decidim_author_type = ? ", .id, .class.base_class.name) end |
.with_evaluation_assigned_to(user, space) ⇒ Object
161 162 163 164 165 166 |
# File 'app/models/decidim/proposals/proposal.rb', line 161 def self.with_evaluation_assigned_to(user, space) evaluator_roles = space.user_roles(:evaluator).where(user:) includes(:evaluation_assignments) .where(decidim_proposals_evaluation_assignments: { evaluator_role_id: evaluator_roles }) end |
.with_more_authors_available?(component) ⇒ Boolean
168 169 170 171 172 173 174 175 |
# File 'app/models/decidim/proposals/proposal.rb', line 168 def self.(component) where(component:) .published .not_hidden .not_withdrawn .where("coauthorships_count > 1") .exists? end |
Instance Method Details
#accepted? ⇒ Boolean
Public: Checks if the organization has accepted a proposal.
Returns Boolean.
307 308 309 |
# File 'app/models/decidim/proposals/proposal.rb', line 307 def accepted? state == "accepted" end |
#actions_for_comment(comment, current_user) ⇒ Object
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 |
# File 'app/models/decidim/proposals/proposal.rb', line 532 def actions_for_comment(comment, current_user) return if comment.commentable != self return unless .include?(current_user) return unless user_has_actions?(comment.) if (comment.).any? [ { label: I18n.t("decidim.proposals.actions.cancel_coauthor_invitation"), url: EngineRouter.main_proxy(component).(proposal_id: id, id: comment..id), icon: "user-forbid-line", method: :delete, data: { confirm: I18n.t("decidim.proposals.actions.cancel_coauthor_invitation_confirm") } } ] else [ { label: I18n.t("decidim.proposals.actions.mark_as_coauthor"), url: EngineRouter.main_proxy(component).(proposal_id: id, id: comment..id), icon: "user-add-line", method: :post, data: { confirm: I18n.t("decidim.proposals.actions.mark_as_coauthor_confirm") } } ] end end |
#allow_resource_permissions? ⇒ Boolean
Public: Overrides the ‘allow_resource_permissions?` Resourceable concern method.
490 491 492 |
# File 'app/models/decidim/proposals/proposal.rb', line 490 def component.settings. end |
#answered? ⇒ Boolean
Public: Checks if the organization has given an answer for the proposal.
Returns Boolean.
293 294 295 |
# File 'app/models/decidim/proposals/proposal.rb', line 293 def answered? answered_at.present? end |
#assign_state(token) ⇒ Object
35 36 37 38 39 |
# File 'app/models/decidim/proposals/proposal.rb', line 35 def assign_state(token) proposal_state = Decidim::Proposals::ProposalState.where(component:, token:).first self.proposal_state = proposal_state end |
#can_accumulate_votes_beyond_threshold ⇒ Object
Public: Can accumulate more votes than maximum for this proposal.
Returns true if can accumulate, false otherwise
374 375 376 |
# File 'app/models/decidim/proposals/proposal.rb', line 374 def can_accumulate_votes_beyond_threshold component.settings.can_accumulate_votes_beyond_threshold end |
#coauthor_invitations_for(user) ⇒ Object
560 561 562 |
# File 'app/models/decidim/proposals/proposal.rb', line 560 def (user) Decidim::Notification.where(event_class: "Decidim::Proposals::CoauthorInvitedEvent", resource: self, user:) end |
#draft? ⇒ Boolean
Public: Whether the proposal is a draft or not.
400 401 402 |
# File 'app/models/decidim/proposals/proposal.rb', line 400 def draft? published_at.nil? end |
#editable_by?(user) ⇒ Boolean
Checks whether the user can edit the given proposal.
user - the user to check for authorship
381 382 383 384 385 |
# File 'app/models/decidim/proposals/proposal.rb', line 381 def editable_by?(user) return true if draft? && created_by?(user) !published_state? && within_edit_time_limit? && !copied_from_other_component? && created_by?(user) end |
#evaluating? ⇒ Boolean
Public: Checks if the organization has marked the proposal as evaluating it.
Returns Boolean.
321 322 323 |
# File 'app/models/decidim/proposals/proposal.rb', line 321 def evaluating? state == "evaluating" end |
#internal_state ⇒ Object
Public: Returns the internal state of the proposal.
Returns Boolean.
277 278 279 280 281 |
# File 'app/models/decidim/proposals/proposal.rb', line 277 def internal_state return amendment.state if emendation? proposal_state&.token || "not_answered" end |
#maximum_votes ⇒ Object
Public: The maximum amount of votes allowed for this proposal.
Returns an Integer with the maximum amount of votes, nil otherwise.
355 356 357 358 359 360 |
# File 'app/models/decidim/proposals/proposal.rb', line 355 def maximum_votes maximum_votes = component.settings.threshold_per_proposal return nil if maximum_votes.zero? maximum_votes end |
#maximum_votes_reached? ⇒ Boolean
Public: The maximum amount of votes allowed for this proposal. 0 means infinite.
Returns true if reached, false otherwise.
365 366 367 368 369 |
# File 'app/models/decidim/proposals/proposal.rb', line 365 def maximum_votes_reached? return false unless maximum_votes votes.count >= maximum_votes end |
#official? ⇒ Boolean
Public: Whether the proposal is official or not.
343 344 345 |
# File 'app/models/decidim/proposals/proposal.rb', line 343 def official? .first.is_a?(Decidim::Organization) end |
#official_meeting? ⇒ Boolean
Public: Whether the proposal is created in a meeting or not.
348 349 350 |
# File 'app/models/decidim/proposals/proposal.rb', line 348 def official_meeting? .first.instance_of?(Decidim::Meetings::Meeting) end |
#presenter ⇒ Object
Returns the presenter for this author, to be used in the views. Required by ResourceRenderer.
327 328 329 |
# File 'app/models/decidim/proposals/proposal.rb', line 327 def presenter Decidim::Proposals::ProposalPresenter.new(self) end |
#process_amendment_state_change! ⇒ Object
513 514 515 516 517 518 519 520 521 |
# File 'app/models/decidim/proposals/proposal.rb', line 513 def process_amendment_state_change! return withdraw! if amendment.withdrawn? return unless %w(accepted rejected evaluating).member?(amendment.state) PaperTrail.request(enabled: false) do assign_state(amendment.state) update!(state_published_at: Time.current) end end |
#published_state? ⇒ Boolean
Public: Checks if the organization has published the state for the proposal.
Returns Boolean.
286 287 288 |
# File 'app/models/decidim/proposals/proposal.rb', line 286 def published_state? emendation? || state_published_at.present? end |
#rejected? ⇒ Boolean
Public: Checks if the organization has rejected a proposal.
Returns Boolean.
314 315 316 |
# File 'app/models/decidim/proposals/proposal.rb', line 314 def rejected? state == "rejected" end |
#reported_attributes ⇒ Object
Public: Overrides the ‘reported_attributes` Reportable concern method.
332 333 334 |
# File 'app/models/decidim/proposals/proposal.rb', line 332 def reported_attributes [:title, :body] end |
#reported_searchable_content_extras ⇒ Object
Public: Overrides the ‘reported_searchable_content_extras` Reportable concern method. Returns authors name or title in case it is a meeting
338 339 340 |
# File 'app/models/decidim/proposals/proposal.rb', line 338 def reported_searchable_content_extras [.map { |p| p.respond_to?(:name) ? p.name : p.title }.join("\n")] end |
#state ⇒ Object
Public: Returns the published state of the proposal.
Returns Boolean.
267 268 269 270 271 272 |
# File 'app/models/decidim/proposals/proposal.rb', line 267 def state return amendment.state if emendation? return nil unless published_state? || withdrawn? proposal_state&.token || "not_answered" end |
#update_votes_count ⇒ Object
Public: Updates the vote count of this proposal.
Returns nothing. rubocop:disable Rails/SkipsModelValidations
252 253 254 |
# File 'app/models/decidim/proposals/proposal.rb', line 252 def update_votes_count update_columns(proposal_votes_count: votes.count) end |
#user_has_actions?(user) ⇒ Boolean
523 524 525 526 527 528 529 530 |
# File 'app/models/decidim/proposals/proposal.rb', line 523 def user_has_actions?(user) return false if .include?(user) return false if user&.blocked? return false if user&.deleted? return false unless user&.confirmed? true end |
#voted_by?(user) ⇒ Boolean
Public: Check if the user has voted the proposal.
Returns Boolean.
260 261 262 |
# File 'app/models/decidim/proposals/proposal.rb', line 260 def voted_by?(user) ProposalVote.where(proposal: self, author: user).any? end |
#withdraw! ⇒ Object
394 395 396 397 |
# File 'app/models/decidim/proposals/proposal.rb', line 394 def withdraw! self.withdrawn_at = Time.zone.now save end |
#withdrawable_by?(user) ⇒ Boolean
Checks whether the user can withdraw the given proposal.
user - the user to check for withdrawability.
390 391 392 |
# File 'app/models/decidim/proposals/proposal.rb', line 390 def withdrawable_by?(user) user && !withdrawn? && (user) && !copied_from_other_component? end |
#withdrawn? ⇒ Boolean
Public: Checks if the author has withdrawn the proposal.
Returns Boolean.
300 301 302 |
# File 'app/models/decidim/proposals/proposal.rb', line 300 def withdrawn? withdrawn_at.present? end |
#within_edit_time_limit? ⇒ Boolean
Checks whether the proposal is inside the time window to be editable or not once published.
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
# File 'app/models/decidim/proposals/proposal.rb', line 495 def within_edit_time_limit? return true if draft? return true if component.settings.proposal_edit_time == "infinite" time_value, time_unit = component.settings.edit_time limit_time = case time_unit when "minutes" updated_at + time_value.minutes when "hours" updated_at + time_value.hours else updated_at + time_value.days end Time.current < limit_time end |