Class: Decidim::EnhancedTextwork::Paragraph

Inherits:
ApplicationRecord show all
Includes:
Amendable, Coauthorable, DataPortability, Decidim::Endorsable, CommentableParagraph, ParticipatoryTextSection, Valuatable, Fingerprintable, Followable, HasAttachments, HasCategory, HasComponent, HasReference, Loggable, NewsletterParticipant, Randomable, Reportable, Resourceable, ScopableResource, Searchable, Traceable, TranslatableAttributes, TranslatableResource
Defined in:
app/models/decidim/enhanced_textwork/paragraph.rb

Overview

The data store for a Paragraph in the Decidim::EnhancedTextwork component.

Constant Summary collapse

POSSIBLE_STATES =
%w(not_answered evaluating accepted rejected withdrawn).freeze

Constants included from ParticipatoryTextSection

Decidim::EnhancedTextwork::ParticipatoryTextSection::LEVELS

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.data_portability_images(user) ⇒ Object



375
376
377
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 375

def self.data_portability_images(user)
  user_collection(user).map { |p| p.attachments.collect(&:file) }
end

.export_serializerObject



371
372
373
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 371

def self.export_serializer
  Decidim::EnhancedTextwork::ParagraphSerializer
end

.log_presenter_class_for(_log) ⇒ Object



100
101
102
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 100

def self.log_presenter_class_for(_log)
  Decidim::EnhancedTextwork::AdminLog::ParagraphPresenter
end

.newsletter_participant_ids(component) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 123

def self.newsletter_participant_ids(component)
  paragraphs = retrieve_paragraphs_for(component).uniq

  coauthors_recipients_ids = paragraphs.map { |p| p.notifiable_identities.pluck(:id) }.flatten.compact.uniq

  participants_has_voted_ids = Decidim::EnhancedTextwork::ParagraphVote.joins(:paragraph).where(paragraph: paragraphs).joins(:author).map(&:decidim_author_id).flatten.compact.uniq

  endorsements_participants_ids = Decidim::Endorsement.where(resource: paragraphs)
                                                      .where(decidim_author_type: "Decidim::UserBaseEntity")
                                                      .pluck(:decidim_author_id).to_a.compact.uniq

  commentators_ids = Decidim::Comments::Comment.user_commentators_ids_in(paragraphs)

  (endorsements_participants_ids + participants_has_voted_ids + coauthors_recipients_ids + commentators_ids).flatten.compact.uniq
end

.ransackable_scopes(_auth = nil) ⇒ Object



323
324
325
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 323

def self.ransackable_scopes(_auth = nil)
  [:valuator_role_ids_has]
end

.retrieve_paragraphs_for(component) ⇒ Object



114
115
116
117
118
119
120
121
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 114

def self.retrieve_paragraphs_for(component)
  Decidim::EnhancedTextwork::Paragraph.where(component: component).joins(:coauthorships)
                              .includes(:votes, :endorsements)
                              .where(decidim_coauthorships: { decidim_author_type: "Decidim::UserBaseEntity" })
                              .not_hidden
                              .published
                              .except_withdrawn
end

.sort_by_translated_title_ascObject



340
341
342
343
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 340

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_descObject



345
346
347
348
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 345

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

.sort_by_valuation_assignments_count_nulls_last_queryObject

Defines the base query so that ransack can actually sort by this value



299
300
301
302
303
304
305
306
307
308
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 299

def self.sort_by_valuation_assignments_count_nulls_last_query
  <<-SQL.squish
  (
    SELECT COUNT(decidim_enhanced_textwork_valuation_assignments.id)
    FROM decidim_enhanced_textwork_valuation_assignments
    WHERE decidim_enhanced_textwork_valuation_assignments.decidim_paragraph_id = decidim_enhanced_textwork_paragraphs.id
    GROUP BY decidim_enhanced_textwork_valuation_assignments.decidim_paragraph_id
  )
  SQL
end

.user_collection(author) ⇒ Object

Returns a collection scoped by an author. Overrides this method in DataPortability to support Coauthorable.



106
107
108
109
110
111
112
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 106

def self.user_collection(author)
  return unless author.is_a?(Decidim::User)

  joins(:coauthorships)
    .where(decidim_coauthorships: { coauthorable_type: name })
    .where("decidim_coauthorships.decidim_author_id = ? AND decidim_coauthorships.decidim_author_type = ? ", author.id, author.class.base_class.name)
end

.valuator_role_ids_has(value) ⇒ Object

method to filter by assigned valuator role ID



311
312
313
314
315
316
317
318
319
320
321
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 311

def self.valuator_role_ids_has(value)
  query = <<-SQL.squish
  :value = any(
    (SELECT decidim_enhanced_textwork_valuation_assignments.valuator_role_id
    FROM decidim_enhanced_textwork_valuation_assignments
    WHERE decidim_enhanced_textwork_valuation_assignments.decidim_paragraph_id = decidim_enhanced_textwork_paragraphs.id
    )
  )
  SQL
  where(query, value: value)
end

.with_valuation_assigned_to(user, space) ⇒ Object



81
82
83
84
85
86
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 81

def self.with_valuation_assigned_to(user, space)
  valuator_roles = space.user_roles(:valuator).where(user: user)

  includes(:valuation_assignments)
    .where(decidim_enhanced_textwork_valuation_assignments: { valuator_role_id: valuator_roles })
end

Instance Method Details

#accepted?Boolean

Public: Checks if the organization has accepted a paragraph.

Returns Boolean.

Returns:

  • (Boolean)


208
209
210
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 208

def accepted?
  state == "accepted"
end

#allow_resource_permissions?Boolean

Public: Overrides the `allow_resource_permissions?` Resourceable concern method.

Returns:

  • (Boolean)


380
381
382
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 380

def allow_resource_permissions?
  component.settings.resources_permissions_enabled
end

#answered?Boolean

Public: Checks if the organization has given an answer for the paragraph.

Returns Boolean.

Returns:

  • (Boolean)


194
195
196
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 194

def answered?
  answered_at.present?
end

#can_accumulate_supports_beyond_thresholdObject

Public: Can accumulate more votres than maximum for this paragraph.

Returns true if can accumulate, false otherwise



273
274
275
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 273

def can_accumulate_supports_beyond_threshold
  component.settings.can_accumulate_supports_beyond_threshold
end

#draft?Boolean

Public: Whether the paragraph is a draft or not.

Returns:

  • (Boolean)


294
295
296
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 294

def draft?
  published_at.nil?
end

#editable_by?(user) ⇒ Boolean

Checks whether the user can edit the given paragraph.

user - the user to check for authorship

Returns:

  • (Boolean)


280
281
282
283
284
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 280

def editable_by?(user)
  return true if draft?

  !published_state? && within_edit_time_limit? && !copied_from_other_component? && created_by?(user)
end

#evaluating?Boolean

Public: Checks if the organization has marked the paragraph as evaluating it.

Returns Boolean.

Returns:

  • (Boolean)


222
223
224
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 222

def evaluating?
  state == "evaluating"
end

#internal_stateObject

Public: Returns the internal state of the paragraph.

Returns Boolean.



178
179
180
181
182
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 178

def internal_state
  return amendment.state if emendation?

  self[:state]
end

#maximum_votesObject

Public: The maximum amount of votes allowed for this paragraph.

Returns an Integer with the maximum amount of votes, nil otherwise.



254
255
256
257
258
259
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 254

def maximum_votes
  maximum_votes = component.settings.threshold_per_paragraph
  return nil if maximum_votes.zero?

  maximum_votes
end

#maximum_votes_reached?Boolean

Public: The maximum amount of votes allowed for this paragraph. 0 means infinite.

Returns true if reached, false otherwise.

Returns:

  • (Boolean)


264
265
266
267
268
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 264

def maximum_votes_reached?
  return false unless maximum_votes

  votes.count >= maximum_votes
end

#official?Boolean

Public: Whether the paragraph is official or not.

Returns:

  • (Boolean)


242
243
244
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 242

def official?
  authors.first.is_a?(Decidim::Organization)
end

#official_meeting?Boolean

Public: Whether the paragraph is created in a meeting or not.

Returns:

  • (Boolean)


247
248
249
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 247

def official_meeting?
  authors.first.class.name == "Decidim::Meetings::Meeting"
end

#process_amendment_state_change!Object



393
394
395
396
397
398
399
400
401
402
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 393

def process_amendment_state_change!
  return unless %w(accepted rejected evaluating withdrawn).member?(amendment.state)

  PaperTrail.request(enabled: false) do
    update!(
      state: amendment.state,
      state_published_at: Time.current
    )
  end
end

#published?Boolean

Public: Checks if the paragraph has been published or not.

Returns Boolean.

Returns:

  • (Boolean)


158
159
160
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 158

def published?
  published_at.present?
end

#published_state?Boolean

Public: Checks if the organization has published the state for the paragraph.

Returns Boolean.

Returns:

  • (Boolean)


187
188
189
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 187

def published_state?
  emendation? || state_published_at.present?
end

#rejected?Boolean

Public: Checks if the organization has rejected a paragraph.

Returns Boolean.

Returns:

  • (Boolean)


215
216
217
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 215

def rejected?
  state == "rejected"
end

#reported_attributesObject

Public: Overrides the `reported_attributes` Reportable concern method.



232
233
234
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 232

def reported_attributes
  [:title, :body]
end

#reported_content_urlObject

Public: Overrides the `reported_content_url` Reportable concern method.



227
228
229
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 227

def reported_content_url
  ResourceLocatorPresenter.new(self).url
end

#reported_searchable_content_extrasObject

Public: Overrides the `reported_searchable_content_extras` Reportable concern method.



237
238
239
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 237

def reported_searchable_content_extras
  [authors.map(&:name).join("\n")]
end

#stateObject

Public: Returns the published state of the paragraph.

Returns Boolean.



165
166
167
168
169
170
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 165

def state
  return amendment.state if emendation?
  return nil unless published_state? || withdrawn?

  super
end

#update_votes_countObject

Public: Updates the vote count of this paragraph.

Returns nothing. rubocop:disable Rails/SkipsModelValidations



143
144
145
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 143

def update_votes_count
  update_columns(paragraph_votes_count: votes.count)
end

#voted_by?(user) ⇒ Boolean

Public: Check if the user has voted the paragraph.

Returns Boolean.

Returns:

  • (Boolean)


151
152
153
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 151

def voted_by?(user)
  ParagraphVote.where(paragraph: self, author: user).any?
end

#withdrawable_by?(user) ⇒ Boolean

Checks whether the user can withdraw the given paragraph.

user - the user to check for withdrawability.

Returns:

  • (Boolean)


289
290
291
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 289

def withdrawable_by?(user)
  user && !withdrawn? && authored_by?(user) && !copied_from_other_component?
end

#withdrawn?Boolean

Public: Checks if the author has withdrawn the paragraph.

Returns Boolean.

Returns:

  • (Boolean)


201
202
203
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 201

def withdrawn?
  internal_state == "withdrawn"
end

#within_edit_time_limit?Boolean

Checks whether the paragraph is inside the time window to be editable or not once published.

Returns:

  • (Boolean)


385
386
387
388
389
390
391
# File 'app/models/decidim/enhanced_textwork/paragraph.rb', line 385

def within_edit_time_limit?
  return true if draft?
  return true if component.settings.paragraph_edit_time == "infinite"

  limit = updated_at + component.settings.paragraph_edit_before_minutes.minutes
  Time.current < limit
end