Class: Decidim::Initiative

Inherits:
ApplicationRecord
  • Object
show all
Includes:
ActiveModel::Dirty, Authorable, Comments::Commentable, DownloadYourData, FilterableResource, Followable, HasArea, HasAttachmentCollections, HasAttachments, HasReference, HasResourcePermission, Decidim::Initiatives::HasArea, Decidim::Initiatives::InitiativeSlug, Loggable, Participable, Publicable, Randomable, Reportable, Resourceable, ScopableParticipatorySpace, Searchable, ShareableWithToken, Traceable, TranslatableResource
Defined in:
app/models/decidim/initiative.rb

Overview

The data store for a Initiative in the Decidim::Initiatives component.

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Decidim::Initiatives::InitiativeSlug

#id_from_slug, #slug_from_id

Class Method Details

.export_serializerObject



161
162
163
# File 'app/models/decidim/initiative.rb', line 161

def self.export_serializer
  Decidim::Initiatives::DownloadYourDataInitiativeSerializer
end

.log_presenter_class_for(_log) ⇒ Object



165
166
167
# File 'app/models/decidim/initiative.rb', line 165

def self.log_presenter_class_for(_log)
  Decidim::Initiatives::AdminLog::InitiativePresenter
end

.ransack(params = {}, options = {}) ⇒ Object



481
482
483
# File 'app/models/decidim/initiative.rb', line 481

def self.ransack(params = {}, options = {})
  Initiatives::InitiativeSearch.new(self, params, options)
end

.ransackable_associations(_auth_object = nil) ⇒ Object



177
178
179
# File 'app/models/decidim/initiative.rb', line 177

def self.ransackable_associations(_auth_object = nil)
  %w(area scope taxonomies)
end

.ransackable_attributes(auth_object = nil) ⇒ Object



169
170
171
172
173
174
175
# File 'app/models/decidim/initiative.rb', line 169

def self.ransackable_attributes(auth_object = nil)
  base = %w(search_text title description id id_string supports_count author_name author_nickname)

  return base unless auth_object&.admin?

  base + %w(published_at state decidim_area_id type_id)
end

.ransackable_scopes(_auth_object = nil) ⇒ Object



181
182
183
# File 'app/models/decidim/initiative.rb', line 181

def self.ransackable_scopes(_auth_object = nil)
  [:with_any_state, :with_any_type, :with_any_scope, :with_any_area]
end

Instance Method Details

#accepts_offline_votes?Boolean

Returns:

  • (Boolean)


424
425
426
# File 'app/models/decidim/initiative.rb', line 424

def accepts_offline_votes?
  published? && (offline_signature_type? || any_signature_type?)
end

#accepts_online_unvotes?Boolean

Returns:

  • (Boolean)


432
433
434
# File 'app/models/decidim/initiative.rb', line 432

def accepts_online_unvotes?
  accepts_online_votes? && type.undo_online_signatures_enabled?
end

#accepts_online_votes?Boolean

Returns:

  • (Boolean)


428
429
430
# File 'app/models/decidim/initiative.rb', line 428

def accepts_online_votes?
  votes_enabled? && (online_signature_type? || any_signature_type?)
end

#allow_resource_permissions?Boolean

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

Returns:

  • (Boolean)


469
470
471
# File 'app/models/decidim/initiative.rb', line 469

def allow_resource_permissions?
  true
end

#answered?Boolean

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

Returns a Boolean.

Returns:

  • (Boolean)


240
241
242
# File 'app/models/decidim/initiative.rb', line 240

def answered?
  answered_at.present?
end

#author_nameObject

Public: Returns the author name. If it has been created by an organization it will return the organization’s name. Otherwise it will return author’s name.

Returns a string



210
211
212
# File 'app/models/decidim/initiative.rb', line 210

def author_name
  user_group&.name || author.name
end

#author_usersObject



420
421
422
# File 'app/models/decidim/initiative.rb', line 420

def author_users
  [author].concat(committee_members.excluding_author.map(&:user))
end

Public: Overrides participatory space’s banner image with the banner image defined for the initiative type.

Returns Decidim::BannerImageUploader



189
190
191
# File 'app/models/decidim/initiative.rb', line 189

def banner_image
  type.attached_uploader(:banner_image)
end

#commentable?Boolean

Public: Whether the object’s comments are visible or not.

Returns:

  • (Boolean)


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

def commentable?
  type.comments_enabled?
end

#comments_have_alignment?Boolean

Public: Overrides the ‘comments_have_alignment?` Commentable concern method.

Returns:

  • (Boolean)


401
402
403
# File 'app/models/decidim/initiative.rb', line 401

def comments_have_alignment?
  true
end

#comments_have_votes?Boolean

Public: Overrides the ‘comments_have_votes?` Commentable concern method.

Returns:

  • (Boolean)


406
407
408
# File 'app/models/decidim/initiative.rb', line 406

def comments_have_votes?
  true
end

#componentObject



448
449
450
# File 'app/models/decidim/initiative.rb', line 448

def component
  nil
end

#created_by_individual?Boolean

Public: Check if an initiative has been created by an individual person. If it is false, then it has been created by an authorized organization.

Returns a Boolean

Returns:

  • (Boolean)


202
203
204
# File 'app/models/decidim/initiative.rb', line 202

def created_by_individual?
  decidim_user_group_id.nil?
end

#enough_committee_members?Boolean

Returns:

  • (Boolean)


440
441
442
# File 'app/models/decidim/initiative.rb', line 440

def enough_committee_members?
  committee_members.approved.count >= minimum_committee_members
end

#has_authorship?(user) ⇒ Boolean

Public: Checks if user is the author or is part of the promotal committee of the initiative.

Returns a Boolean.

Returns:

  • (Boolean)


414
415
416
417
418
# File 'app/models/decidim/initiative.rb', line 414

def has_authorship?(user)
  return true if author.id == user.id

  committee_members.approved.where(decidim_users_id: user.id).any?
end

#has_signature_interval_defined?Boolean

Public: Returns whether the signature interval is already defined or not.

Returns:

  • (Boolean)


282
283
284
# File 'app/models/decidim/initiative.rb', line 282

def has_signature_interval_defined?
  signature_end_date.present? && signature_start_date.present?
end

#hashtagObject

Public: Returns the hashtag for the initiative.



287
288
289
# File 'app/models/decidim/initiative.rb', line 287

def hashtag
  attributes["hashtag"].to_s.delete("#")
end

#minimum_committee_membersObject



436
437
438
# File 'app/models/decidim/initiative.rb', line 436

def minimum_committee_members
  type.minimum_committee_members || Decidim::Initiatives.minimum_committee_members
end

#missing_committee_membersObject



444
445
446
# File 'app/models/decidim/initiative.rb', line 444

def missing_committee_members
  minimum_committee_members - committee_members.approved.count
end

#offline_votes_countObject



336
337
338
339
340
# File 'app/models/decidim/initiative.rb', line 336

def offline_votes_count
  return 0 if online_signature_type?

  offline_votes["total"].to_i
end

#offline_votes_count_for(scope) ⇒ Object



350
351
352
353
354
355
356
# File 'app/models/decidim/initiative.rb', line 350

def offline_votes_count_for(scope)
  return 0 if online_signature_type?

  scope_key = (scope&.id || "global").to_s

  (offline_votes || {}).fetch(scope_key, 0).to_i
end

#online_votes_countObject

Public: Calculates all the votes across all the scopes.

Returns an Integer.



330
331
332
333
334
# File 'app/models/decidim/initiative.rb', line 330

def online_votes_count
  return 0 if offline_signature_type?

  online_votes["total"].to_i
end

#online_votes_count_for(scope) ⇒ Object



342
343
344
345
346
347
348
# File 'app/models/decidim/initiative.rb', line 342

def online_votes_count_for(scope)
  return 0 if offline_signature_type?

  scope_key = (scope&.id || "global").to_s

  (online_votes || {}).fetch(scope_key, 0).to_i
end

#percentageObject

Public: Returns the percentage of required supports reached



313
314
315
# File 'app/models/decidim/initiative.rb', line 313

def percentage
  [supports_count * 100 / supports_required, 100].min
end

#publish!Object

Public: Publishes this initiative

Returns true if the record was properly saved, false otherwise.



261
262
263
264
265
266
267
268
269
270
# File 'app/models/decidim/initiative.rb', line 261

def publish!
  return false if published?

  update(
    published_at: Time.current,
    state: "open",
    signature_start_date: Date.current,
    signature_end_date: signature_end_date || (Date.current + Decidim::Initiatives.default_signature_time_period_length)
  )
end

#reported_attributesObject

Public: Overrides the ‘reported_attributes` Reportable concern method.



220
221
222
# File 'app/models/decidim/initiative.rb', line 220

def reported_attributes
  [:title, :description]
end

#reported_content_urlObject

Public: Overrides the ‘reported_content_url` Reportable concern method.



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

def reported_content_url
  ResourceLocatorPresenter.new(self).url
end

#scopes_enabledObject

Public: Overrides scopes enabled attribute value. For initiatives it will not be directly managed by the user and it will be enabled by default.



254
255
256
# File 'app/models/decidim/initiative.rb', line 254

def scopes_enabled
  true
end

#scopes_enabled?Boolean

Public: Overrides scopes enabled flag available in other models like participatory space or assemblies. For initiatives it will not be directly managed by the user and it will be enabled by default.

Returns:

  • (Boolean)


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

def scopes_enabled?
  true
end

#set_offline_votes_totalObject



370
371
372
373
374
# File 'app/models/decidim/initiative.rb', line 370

def set_offline_votes_total
  return if offline_votes.blank?

  offline_votes["total"] = offline_votes[scope&.id.to_s] || offline_votes["global"]
end

#shareable_url(share_token) ⇒ Object



477
478
479
# File 'app/models/decidim/initiative.rb', line 477

def shareable_url(share_token)
  EngineRouter.main_proxy(self).initiative_url(self, share_token: share_token.token)
end

#slugObject

Public: Overrides slug attribute from participatory processes.



391
392
393
# File 'app/models/decidim/initiative.rb', line 391

def slug
  slug_from_id(id)
end

#supports_countObject

Public: Calculates the number of total current supports.

Returns an Integer.



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

def supports_count
  online_votes_count + offline_votes_count
end

#supports_count_for(scope) ⇒ Object

Public: Calculates the number of current supports for a scope.

Returns an Integer.



301
302
303
# File 'app/models/decidim/initiative.rb', line 301

def supports_count_for(scope)
  online_votes_count_for(scope) + offline_votes_count_for(scope)
end

#supports_goal_reached?Boolean

Public: Whether the supports required objective has been reached

Returns:

  • (Boolean)


318
319
320
# File 'app/models/decidim/initiative.rb', line 318

def supports_goal_reached?
  votable_initiative_type_scopes.map(&:scope).all? { |scope| supports_goal_reached_for?(scope) }
end

#supports_goal_reached_for?(scope) ⇒ Boolean

Public: Whether the supports required objective has been reached for a scope

Returns:

  • (Boolean)


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

def supports_goal_reached_for?(scope)
  supports_count_for(scope) >= supports_required_for(scope)
end

#supports_required_for(scope) ⇒ Object

Public: Calculates the number of supports required to accept the initiative for a scope.

Returns an Integer.



308
309
310
# File 'app/models/decidim/initiative.rb', line 308

def supports_required_for(scope)
  initiative_type_scopes.find_by(decidim_scopes_id: scope&.id).supports_required
end

#to_paramObject



395
396
397
# File 'app/models/decidim/initiative.rb', line 395

def to_param
  slug
end

#unpublish!Object

Public: Unpublishes this initiative

Returns true if the record was properly saved, false otherwise.



275
276
277
278
279
# File 'app/models/decidim/initiative.rb', line 275

def unpublish!
  return false unless published?

  update(published_at: nil, state: "discarded")
end

#update_online_votes_countersObject



358
359
360
361
362
363
364
365
366
367
368
# File 'app/models/decidim/initiative.rb', line 358

def update_online_votes_counters
  online_votes = votes.group(:scope).count.each_with_object({}) do |(scope, count), counters|
    counters[scope&.id || "global"] = count
    counters["total"] ||= 0
    counters["total"] += count
  end

  # rubocop:disable Rails/SkipsModelValidations
  update_column("online_votes", online_votes)
  # rubocop:enable Rails/SkipsModelValidations
end

#user_allowed_to_comment?(user) ⇒ Boolean

Returns:

  • (Boolean)


473
474
475
# File 'app/models/decidim/initiative.rb', line 473

def user_allowed_to_comment?(user)
  ActionAuthorizer.new(user, "comment", self, nil).authorize.ok?
end

#user_role_config_for(_user, _role_name) ⇒ Object

Public: Returns an empty object. This method should be implemented by ‘ParticipatorySpaceResourceable`, but for some reason this model does not implement this interface.



464
465
466
# File 'app/models/decidim/initiative.rb', line 464

def user_role_config_for(_user, _role_name)
  Decidim::ParticipatorySpaceRoleConfig::Base.new(:empty_role_name)
end

#validate_sms_code_on_votes?Boolean

Public: Checks if the type the initiative belongs to enables SMS code verification step. Tis configuration is ignored if the organization does not have the sms authorization available

Returns a Boolean

Returns:

  • (Boolean)


457
458
459
# File 'app/models/decidim/initiative.rb', line 457

def validate_sms_code_on_votes?
  organization.available_authorizations.include?("sms") && type.validate_sms_code_on_votes?
end

#votable_initiative_type_scopesObject

Public: Finds all the InitiativeTypeScopes that are eligible to be voted by a user. Usually this is only the ‘scoped_type` but voting on children of the scoped type is enabled we have to filter all the available scopes in the initiative type to select the ones that are a descendant of the initiative type.

Returns an Array of Decidim::InitiativesScopeType.



382
383
384
385
386
387
388
# File 'app/models/decidim/initiative.rb', line 382

def votable_initiative_type_scopes
  return Array(scoped_type) unless type.child_scope_threshold_enabled?

  initiative_type_scopes.select do |initiative_type_scope|
    initiative_type_scope.scope.present? && (scoped_type.global_scope? || scoped_type.scope.ancestor_of?(initiative_type_scope.scope))
  end.prepend(scoped_type).uniq
end

#voted_by?(user) ⇒ Boolean

Public: Check if the user has voted the question.

Returns Boolean.

Returns:

  • (Boolean)


233
234
235
# File 'app/models/decidim/initiative.rb', line 233

def voted_by?(user)
  votes.where(author: user).any?
end

#votes_enabled?Boolean

Returns:

  • (Boolean)


224
225
226
227
228
# File 'app/models/decidim/initiative.rb', line 224

def votes_enabled?
  published? &&
    signature_start_date <= Date.current &&
    signature_end_date >= Date.current
end