Class: DigitalAsset

Inherits:
ApplicationRecord show all
Extended by:
FriendlyId
Includes:
Models::Auditable, Models::ItemScopable, Models::Taggable, PgSearch::Model
Defined in:
app/models/digital_asset.rb

Overview

== Schema Information

Table name: digital_assets
Database name: primary

id :integer not null, primary key
ai_metadata_suggestions :jsonb
ai_visual_description :text
air_date :date
asset :jsonb
attachment_format :string(10)
attachment_height :integer
attachment_mime_type :string
attachment_name :string
attachment_size :integer
attachment_uid :string
attachment_width :integer
background_color :string
category :string(255)
cloudflare_data :jsonb not null
cloudflare_uid :string
duration_in_seconds :integer
expanded_description :text
fingerprint :bigint
fingerprint_legacy :string
image_colorspace :string
image_dpi :integer
inactive :boolean default(FALSE), not null
linked_assets_uids :string default([]), is an Array
locales :string default([]), not null, is an Array
location :string
merged_from_ids :integer default([]), is an Array
meta_description :text
meta_keywords :string
meta_title :string(255)
notes :text
position :integer default(100), not null
poster_format :string
poster_mime_type :string
poster_name :string
poster_offset :integer
poster_uid :string
reference_number :string
series :string
slug :string(140)
source :string
structured_transcript_json :jsonb
sub_header :string(255)
thumbnail_url :string
title :string(255)
transcribed_at :datetime
transcript :text
transcription_state :integer default(0)
translations :jsonb
type :string
url :string(255)
video_has_no_spoken_words :boolean default(FALSE)
vision_analyzed_at :datetime
vision_model_used :string
youtube_caption_synced_at :datetime
youtube_chapters_draft :jsonb
youtube_chapters_generation_error :text
youtube_chapters_generation_status :string
youtube_description :string
youtube_privacy_status :string
youtube_synced_at :datetime
youtube_title :string
youtube_upload_date :datetime
youtube_upload_status :string
created_at :datetime not null
updated_at :datetime not null
assemblyai_transcript_id :string
asset_file_id :string
cloudinary_asset_id :string
creator_id :integer
legacy_wistia_id :string(255)
poster_image_id :integer
purge_cache_request_id :string
updater_id :integer
youtube_id :string
youtube_thumbnail_image_id :integer

Indexes

by_type_inactive_id (type,inactive,id)
index_digital_assets_on_asset_file_id (asset_file_id) UNIQUE
index_digital_assets_on_cloudflare_uid (cloudflare_uid)
index_digital_assets_on_creator_id (creator_id)
index_digital_assets_on_inactive (inactive)
index_digital_assets_on_merged_from_ids (merged_from_ids) USING gin
index_digital_assets_on_poster_image_id (poster_image_id)
index_digital_assets_on_poster_offset (poster_offset)
index_digital_assets_on_slug (slug)
index_digital_assets_on_source (source)
index_digital_assets_on_transcription_state (transcription_state)
index_digital_assets_on_translations (translations) USING gin
index_digital_assets_on_type_and_slug (type,slug) UNIQUE
index_digital_assets_on_updater_id (updater_id)
index_digital_assets_on_url (url)
index_digital_assets_on_vision_analyzed_at (vision_analyzed_at)
index_digital_assets_on_youtube_thumbnail_image_id (youtube_thumbnail_image_id)
index_images_on_fingerprint (fingerprint) WHERE (((type)::text = 'Image'::text) AND (fingerprint IS NOT NULL))
type_category (type,category)
type_entity_id (type,legacy_wistia_id)
type_title (type,title)

Foreign Keys

fk_rails_... (creator_id => parties.id)
fk_rails_... (poster_image_id => digital_assets.id) ON DELETE => nullify
fk_rails_... (updater_id => parties.id)
fk_rails_... (youtube_thumbnail_image_id => digital_assets.id) ON DELETE => nullify

Direct Known Subclasses

Image, Video

Constant Summary collapse

%w[for-product-page for-support-page no-index].freeze
HIDDEN_TAGS =
%w[installation-plan pdf-thumbnail room-configuration video-poster auto-generated review-avatar].freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Instance Attribute Summary collapse

Has many collapse

Methods included from Models::Taggable

#tag_records, #taggings

Has and belongs to many collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::Taggable

#add_tag, all_tags, #has_tag?, normalize_tag_names, not_tagged_with, #remove_tag, #tag_list, #tag_list=, #taggable_type_for_tagging, tagged_with, #tags, #tags=, tags_cloud, tags_exclude, tags_include, with_all_tags, with_any_tags, without_all_tags, without_any_tags

Methods included from Models::ItemScopable

by_product_category_id, by_product_category_path, by_product_category_path_exact, by_product_category_url, by_product_category_url_exact, by_product_line_path_full, by_product_line_url, by_product_line_url_full, not_by_product_category_id

Methods included from Models::Auditable

#all_skipped_columns, #audit_reference_data, #creator, #should_not_save_version, #stamp_record, #updater

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransortable_attributes, #to_relation

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#force_new_slugObject

Returns the value of attribute force_new_slug.



124
125
126
# File 'app/models/digital_asset.rb', line 124

def force_new_slug
  @force_new_slug
end

#refresh_cacheObject

Returns the value of attribute refresh_cache.



124
125
126
# File 'app/models/digital_asset.rb', line 124

def refresh_cache
  @refresh_cache
end

#titleObject (readonly)



142
# File 'app/models/digital_asset.rb', line 142

validates :title, presence: true

#urlObject (readonly)



143
# File 'app/models/digital_asset.rb', line 143

validates :url, uniqueness: { if: :url }

Class Method Details

.activeActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are active. Active Record Scope

Returns:

See Also:



155
# File 'app/models/digital_asset.rb', line 155

scope :active, -> { where(inactive: false) }

.all_localesObject

Note: all_tags method provided by Models::Taggable concern



360
361
362
# File 'app/models/digital_asset.rb', line 360

def self.all_locales
  pluck(Arel.sql('distinct unnest(locales) as locale')).compact.sort
end

.available_banner_tagsObject



531
532
533
534
535
# File 'app/models/digital_asset.rb', line 531

def self.available_banner_tags
  PagesController.page_ids.reject { |page_id| page_id.start_with?('h-') }.map do |page_id|
    banner_tag_for(page_id)
  end.uniq.sort
end

.available_og_image_tagsObject



543
544
545
546
547
# File 'app/models/digital_asset.rb', line 543

def self.available_og_image_tags
  PagesController.page_ids.reject { |page_id| page_id.start_with?('h-') }.map do |page_id|
    og_image_tag_for(page_id)
  end.uniq.sort
end

.available_page_tagsObject

Generate the available 'for-' page tags for all CMS pages.
Uses the same convention as the cohesive migration:
'for-' + page_id.tr('/', '-').parameterize + '-page'



507
508
509
510
511
# File 'app/models/digital_asset.rb', line 507

def self.available_page_tags
  PagesController.page_ids.reject { |page_id| page_id.start_with?('h-') }.map do |page_id|
    "for-#{page_id.tr('/', '-').parameterize}-page"
  end.uniq.sort
end

.available_page_tags_with_pathsObject



513
514
515
516
517
518
# File 'app/models/digital_asset.rb', line 513

def self.available_page_tags_with_paths
  PagesController.page_ids.reject { |page_id| page_id.start_with?('h-') }.each_with_object({}) do |page_id, hash|
    tag = "for-#{page_id.tr('/', '-').parameterize}-page"
    hash[tag] = "/#{page_id}"
  end
end

Build the banner image tag for a given CMS page id.
e.g. banner_tag_for("floor-heating/bathroom") => "banner-for-floor-heating-bathroom-page"



527
528
529
# File 'app/models/digital_asset.rb', line 527

def self.banner_tag_for(page_id)
  "banner-for-#{page_id.to_s.tr('/', '-').parameterize}-page"
end

.by_categoryActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are by category. Active Record Scope

Returns:

See Also:



212
# File 'app/models/digital_asset.rb', line 212

scope :by_category, ->(cat) { where(category: cat) }

.by_item_idsActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are by item ids. Active Record Scope

Returns:

See Also:



213
214
215
216
217
218
# File 'app/models/digital_asset.rb', line 213

scope :by_item_ids, ->(*item_ids) {
  item_ids = [item_ids].flatten.map(&:presence).compact.uniq
  return none unless item_ids.present?

  where('exists(select 1 from digital_assets_items dai where dai.item_id IN (?) and dai.digital_asset_id = digital_assets.id)', item_ids)
}

.by_item_skusActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are by item skus. Active Record Scope

Returns:

See Also:



263
# File 'app/models/digital_asset.rb', line 263

scope :by_item_skus, ->(*item_skus) { joins(:items).where(items: { sku: item_skus }) }

.by_party_idsActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are by party ids. Active Record Scope

Returns:

See Also:



282
283
284
285
286
287
# File 'app/models/digital_asset.rb', line 282

scope :by_party_ids, ->(*party_ids) {
  party_ids = [party_ids].flatten.map(&:presence).compact.uniq
  return none unless party_ids.present?

  where('exists(select 1 from digital_assets_parties dap inner join parties p on p.id = dap.party_id where (dap.party_id IN (:party_ids) OR p.customer_id IN (:party_ids)) and dap.digital_asset_id = digital_assets.id)', party_ids: party_ids)
}

.by_product_category_id_directActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are by product category id direct. Active Record Scope

Returns:

See Also:



193
194
195
196
197
198
# File 'app/models/digital_asset.rb', line 193

scope :by_product_category_id_direct, ->(*pc_ids) do
  ids = [pc_ids].flatten
  return none if ids.empty?

  where('exists(select 1 from digital_assets_product_categories dapc where dapc.digital_asset_id = digital_assets.id and dapc.product_category_id in (?))', ids)
end

.by_product_category_id_direct_or_optionalActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are by product category id direct or optional. Active Record Scope

Returns:

See Also:



199
200
201
202
203
204
205
206
207
208
# File 'app/models/digital_asset.rb', line 199

scope :by_product_category_id_direct_or_optional, ->(*pc_ids) do
  ids = [pc_ids].flatten
  return none if ids.empty?

  sql = <<-EOS
    exists(select 1 from digital_assets_product_categories dapc where dapc.digital_asset_id = digital_assets.id and dapc.product_category_id in (?))
    OR not exists(select 1 from digital_assets_product_categories dapc where dapc.digital_asset_id = digital_assets.id)
  EOS
  where(sql, ids)
end

.by_product_line_idActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are by product line id. Active Record Scope

Returns:

See Also:



169
170
171
172
173
174
175
176
177
178
179
180
# File 'app/models/digital_asset.rb', line 169

scope :by_product_line_id, ->(*pl_ids) do
  ids = [pl_ids].flatten.map(&:presence).compact.map(&:to_i).uniq
  return none if ids.empty?

  where(
    'EXISTS(SELECT 1 FROM digital_asset_product_lines dapl ' \
    'INNER JOIN product_lines pl ON pl.id = dapl.product_line_id ' \
    'WHERE dapl.digital_asset_id = digital_assets.id ' \
    'AND pl.ltree_path_ids <@ ANY(SELECT ltree_path_ids FROM product_lines WHERE id = ANY(ARRAY[?]::integer[])))',
    ids
  )
end

.by_product_line_id_directActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are by product line id direct. Active Record Scope

Returns:

See Also:



163
164
165
166
167
168
# File 'app/models/digital_asset.rb', line 163

scope :by_product_line_id_direct, ->(*pl_ids) do
  ids = [pl_ids].flatten.map(&:presence).compact.uniq
  return none if ids.empty?

  where('EXISTS(SELECT 1 FROM digital_asset_product_lines dapl WHERE dapl.digital_asset_id = digital_assets.id AND dapl.product_line_id IN (?))', ids)
end

.by_product_line_pathActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are by product line path. Active Record Scope

Returns:

See Also:



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

scope :by_product_line_path, ->(slug_path) {
  return all if slug_path.blank?

  where(
    'EXISTS(SELECT 1 FROM digital_asset_product_lines dapl ' \
    'INNER JOIN product_lines pl ON pl.id = dapl.product_line_id ' \
    'WHERE dapl.digital_asset_id = digital_assets.id AND pl.ltree_path_slugs <@ ?)',
    slug_path
  )
}

.categorizedActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are categorized. Active Record Scope

Returns:

See Also:



296
# File 'app/models/digital_asset.rb', line 296

scope :categorized, -> { where.not(category: nil) }

.exclude_tagsActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are exclude tags. Active Record Scope

Returns:

See Also:



268
269
270
271
272
273
# File 'app/models/digital_asset.rb', line 268

scope :exclude_tags, ->(*tag_names) {
  tag_names = [tag_names].flatten.map(&:presence).compact.map(&:downcase)
  return all if tag_names.empty?

  not_tagged_with(tag_names)
}

.imagesActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are images. Active Record Scope

Returns:

See Also:



297
# File 'app/models/digital_asset.rb', line 297

scope :images, -> { where(type: 'Image') }

.localized_forActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are localized for. Active Record Scope

Returns:

See Also:



280
# File 'app/models/digital_asset.rb', line 280

scope :localized_for, ->(*locales) { where.overlap(locales: LocaleUtility.locales_and_fallbacks(locales)) }

.localized_for_or_notActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are localized for or not. Active Record Scope

Returns:

See Also:



281
# File 'app/models/digital_asset.rb', line 281

scope :localized_for_or_not, ->(*locales) { localized_for(locales).or(where(DigitalAsset[:locales].eq('{}'))) }

.not_by_party_idsActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are not by party ids. Active Record Scope

Returns:

See Also:



290
291
292
293
294
295
# File 'app/models/digital_asset.rb', line 290

scope :not_by_party_ids, ->(*party_ids) {
  party_ids = [party_ids].flatten.map(&:presence).compact.uniq
  return all unless party_ids.present?

  where('NOT exists(select 1 from digital_assets_parties dap inner join parties p on p.id = dap.party_id where (dap.party_id IN (:party_ids) OR p.customer_id IN (:party_ids)) and dap.digital_asset_id = digital_assets.id)', party_ids: party_ids)
}

.not_by_product_line_idActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are not by product line id. Active Record Scope

Returns:

See Also:



181
182
183
184
185
186
187
188
189
190
191
192
# File 'app/models/digital_asset.rb', line 181

scope :not_by_product_line_id, ->(*pl_ids) do
  ids = [pl_ids].flatten.compact.map(&:to_i)
  return all if ids.empty?

  where.not(
    'EXISTS(SELECT 1 FROM digital_asset_product_lines dapl ' \
    'INNER JOIN product_lines pl ON pl.id = dapl.product_line_id ' \
    'WHERE dapl.digital_asset_id = digital_assets.id ' \
    'AND pl.ltree_path_ids <@ ANY(SELECT ltree_path_ids FROM product_lines WHERE id = ANY(ARRAY[?]::integer[])))',
    ids
  )
end

.not_by_product_line_pathActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are not by product line path. Active Record Scope

Returns:

See Also:



325
326
327
328
329
330
331
332
333
334
# File 'app/models/digital_asset.rb', line 325

scope :not_by_product_line_path, ->(slug_path) {
  return all if slug_path.blank?

  where.not(
    'EXISTS(SELECT 1 FROM digital_asset_product_lines dapl ' \
    'INNER JOIN product_lines pl ON pl.id = dapl.product_line_id ' \
    'WHERE dapl.digital_asset_id = digital_assets.id AND pl.ltree_path_slugs <@ ?)',
    slug_path
  )
}

.og_image_tag_for(page_id) ⇒ Object

Build the og:image tag for a given CMS page id.
e.g. og_image_tag_for("floor-heating/bathroom") => "og-image-for-floor-heating-bathroom-page"



539
540
541
# File 'app/models/digital_asset.rb', line 539

def self.og_image_tag_for(page_id)
  "og-image-for-#{page_id.to_s.tr('/', '-').parameterize}-page"
end

.page_tag_for(page_id) ⇒ Object

Build the canonical page tag for a given CMS page id (e.g. "towel-warmer").



521
522
523
# File 'app/models/digital_asset.rb', line 521

def self.page_tag_for(page_id)
  "for-#{page_id.to_s.tr('/', '-').parameterize}-page"
end

.ransackable_scopes(_auth_object = nil) ⇒ Object



352
353
354
355
356
# File 'app/models/digital_asset.rb', line 352

def self.ransackable_scopes(_auth_object = nil)
  %i[keyword_search by_product_line_id by_product_line_path by_product_category_id_direct tag_presence
     by_party_ids by_item_ids related_to_item_id show_hidden_tags exclude_tags tags_include
     not_by_product_line_id not_by_party_ids]
end

A relation of DigitalAssets that are related to item id. Active Record Scope

Returns:

See Also:



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'app/models/digital_asset.rb', line 222

scope :related_to_item_id, ->(item_id) {
  return none if item_id.blank?

  item = Item.find_by(id: item_id)
  return none unless item

  # Get product line path for ltree ancestor matching
  pl_path = item.primary_pl_path_slugs
  pc_path = item.pc_path_slugs

  # Build OR conditions for:
  # 1. Directly linked to the item
  # 2. Linked to item's product line or any ancestor (using ltree)
  # 3. Linked to item's product category or any ancestor (using ltree)
  conditions = []
  binds = {}

  # Direct item link
  conditions << 'EXISTS(SELECT 1 FROM digital_assets_items dai WHERE dai.item_id = :item_id AND dai.digital_asset_id = digital_assets.id)'
  binds[:item_id] = item_id

  # Product line link (item's PL path is descendant of or equal to the image's PL path)
  if pl_path.present?
    conditions << "EXISTS(SELECT 1 FROM digital_asset_product_lines dapl
                   INNER JOIN product_lines pl ON pl.id = dapl.product_line_id
                   WHERE dapl.digital_asset_id = digital_assets.id
                   AND :pl_path <@ pl.ltree_path_slugs)"
    binds[:pl_path] = pl_path
  end

  # Product category link (item's PC path is descendant of or equal to the image's PC path)
  if pc_path.present?
    conditions << "EXISTS(SELECT 1 FROM digital_assets_product_categories dapc
                   INNER JOIN product_categories pc ON pc.id = dapc.product_category_id
                   WHERE dapc.digital_asset_id = digital_assets.id
                   AND :pc_path <@ pc.ltree_path_slugs)"
    binds[:pc_path] = pc_path
  end

  where(conditions.join(' OR '), binds)
}

.show_hidden_tagsActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are show hidden tags. Active Record Scope

Returns:

See Also:



266
# File 'app/models/digital_asset.rb', line 266

scope :show_hidden_tags, ->(val) { val.to_b ? all : not_tagged_with(HIDDEN_TAGS) }

.tag_presenceActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are tag presence. Active Record Scope

Returns:

See Also:



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

scope :tag_presence, ->(tag_present) {
  return if tag_present.blank?

  tag_present.to_b ? joins(:taggings).distinct : left_joins(:taggings).where(taggings: { id: nil })
}

.tagged_with_allActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are tagged with all. Active Record Scope

Returns:

See Also:



265
# File 'app/models/digital_asset.rb', line 265

scope :tagged_with_all, ->(*tag_names) { with_all_tags(*tag_names) }

.videosActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are videos. Active Record Scope

Returns:

See Also:



298
# File 'app/models/digital_asset.rb', line 298

scope :videos, -> { where(type: 'Video') }

.with_product_line_urlsActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are with product line urls. Active Record Scope

Returns:

See Also:



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

scope :with_product_line_urls, -> {
  all.select_append('
   ARRAY(
     select pl.slug_ltree::text
     from product_lines pl
     inner join digital_asset_product_lines dapl on dapl.product_line_id = pl.id
     where
      dapl.digital_asset_id = digital_assets.id
    ) as product_line_urls
  ')
}

.without_product_categoriesActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are without product categories. Active Record Scope

Returns:

See Also:



210
# File 'app/models/digital_asset.rb', line 210

scope :without_product_categories, -> { where_not_exists(:product_categories) }

.without_product_linesActiveRecord::Relation<DigitalAsset>

A relation of DigitalAssets that are without product lines. Active Record Scope

Returns:

See Also:



211
# File 'app/models/digital_asset.rb', line 211

scope :without_product_lines, -> { where_not_exists(:digital_asset_product_lines) }

Instance Method Details

#alertsObject



364
365
366
367
368
# File 'app/models/digital_asset.rb', line 364

def alerts
  r = []
  r << 'SEO Title is too long, keep it at 65 characters or less' if meta_title&.size&.> 65
  r
end

#all_my_itemsObject



489
490
491
492
493
494
495
496
497
498
499
500
501
502
# File 'app/models/digital_asset.rb', line 489

def all_my_items
  item_ids = items.active.pluck(:id)

  plids = product_line_ids
  pcids = product_category_ids
  if plids.present? && pcids.present?
    related_items = Item.active.condition_new
    related_items = related_items.by_product_line_id(plids) if plids.present?
    related_items = related_items.by_product_category_id(pcids) if pcids.present?
    item_ids += related_items.pluck(:id)
  end
  item_ids.uniq!
  Item.where(id: item_ids).order(:sku)
end

#asset_identifierObject



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

def asset_identifier
  SecureRandom.base58(6).downcase
end


446
447
448
449
# File 'app/models/digital_asset.rb', line 446

def cross_links_opportunities_to_parties
  self.party_ids = party_ids | opportunities.pluck(:customer_id)
  true
end

#digital_asset_product_linesActiveRecord::Relation<DigitalAssetProductLine>

Returns:

See Also:



130
# File 'app/models/digital_asset.rb', line 130

has_many :digital_asset_product_lines, -> { order(:position) }, inverse_of: :digital_asset, validate: false

#dimensionsObject



415
416
417
418
419
# File 'app/models/digital_asset.rb', line 415

def dimensions
  return unless attachment_width.present? && attachment_height.present?

  "#{attachment_width} x #{attachment_height}"
end

#file_basenameObject



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

def file_basename
  return unless attachment_name.present?

  File.basename(attachment_name, '.*')
end

#generated_imagesActiveRecord::Relation<GeneratedImage>

Returns:

See Also:



133
# File 'app/models/digital_asset.rb', line 133

has_many :generated_images, foreign_key: :source_image_id, dependent: :destroy, inverse_of: :source_image

#is_image?Boolean

Returns:

  • (Boolean)


440
441
442
443
444
# File 'app/models/digital_asset.rb', line 440

def is_image?
  return true if type == 'Image'

  false
end

#is_video?Boolean

Returns:

  • (Boolean)


434
435
436
437
438
# File 'app/models/digital_asset.rb', line 434

def is_video?
  return true if type == 'Video'

  false
end

#itemsActiveRecord::Relation<Item>

Returns:

  • (ActiveRecord::Relation<Item>)

See Also:



136
# File 'app/models/digital_asset.rb', line 136

has_and_belongs_to_many :items, validate: false

#opportunitiesActiveRecord::Relation<Opportunity>

Returns:

See Also:



140
# File 'app/models/digital_asset.rb', line 140

has_and_belongs_to_many :opportunities, validate: false, inverse_of: :digital_assets

#partiesActiveRecord::Relation<Party>

Returns:

  • (ActiveRecord::Relation<Party>)

See Also:



137
# File 'app/models/digital_asset.rb', line 137

has_and_belongs_to_many :parties, validate: false

#product_categoriesActiveRecord::Relation<ProductCategory>

Returns:

See Also:



135
# File 'app/models/digital_asset.rb', line 135

has_and_belongs_to_many :product_categories, validate: false

#product_linesActiveRecord::Relation<ProductLine>

Returns:

See Also:



131
# File 'app/models/digital_asset.rb', line 131

has_many :product_lines, through: :digital_asset_product_lines, validate: false

#product_lines_displayObject



411
412
413
# File 'app/models/digital_asset.rb', line 411

def product_lines_display
  product_lines.collect { |pl| pl.name }.join(', ')
end

#product_lines_for_sortingObject



403
404
405
# File 'app/models/digital_asset.rb', line 403

def product_lines_for_sorting
  product_lines.collect { |pl| pl.self_and_ancestors }.flatten.collect { |pl| pl.slug_ltree }.map { |slt| "product-line-#{slt}" }.join(' ')
end

#purge_edge_cacheObject



451
452
453
454
# File 'app/models/digital_asset.rb', line 451

def purge_edge_cache
  urls = site_maps.map(&:url)
  EdgeCacheWorker.perform_async('urls' => urls) if urls.present?
end

#reviewsActiveRecord::Relation<Review>

Legacy associations removed: showcases, showcase_rooms

Returns:

  • (ActiveRecord::Relation<Review>)

See Also:



139
# File 'app/models/digital_asset.rb', line 139

has_and_belongs_to_many :reviews, validate: false

#sanitize_urlsObject



456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
# File 'app/models/digital_asset.rb', line 456

def sanitize_urls
  sd = Seo::DeparameterizeLinks.new
  ld = Seo::HtmlLinkSanitizer.new
  imgf = Seo::ImageMissingSizeFiller.new

  if expanded_description.present?
    logger.info "Sanitizing urls in expanded_description for digital asset:#{id}"
    h = expanded_description
    h = ld.process(h).html_out
    h = sd.process(h).html_out
    h = imgf.process(h).html_out
    # Nokogiri URL-encodes spaces in href attributes, corrupting {{ locale }} to {{%20locale%20}}.
    h = h.gsub(/\{\{%20([\w_]+)%20\}\}/) { "{{#{Regexp.last_match(1)}}}" }
    self.expanded_description = h
  end

  return unless transcript.present?

  logger.info "Sanitizing urls in transcript for digital asset:#{id}"
  h = transcript
  h = ld.process(h).html_out
  h = sd.process(h).html_out
  h = imgf.process(h).html_out
  # Nokogiri URL-encodes spaces in href attributes, corrupting {{ locale }} to {{%20locale%20}}.
  h = h.gsub(/\{\{%20([\w_]+)%20\}\}/) { "{{#{Regexp.last_match(1)}}}" }
  self.transcript = h
end

#seo_titleObject



376
377
378
379
# File 'app/models/digital_asset.rb', line 376

def seo_title
  s = meta_title.presence || title.presence || file_basename
  s&.first(133)
end

#should_generate_new_friendly_id?Boolean

Returns:

  • (Boolean)


399
400
401
# File 'app/models/digital_asset.rb', line 399

def should_generate_new_friendly_id?
  meta_title_changed? || title_changed? || attachment_name_changed? || url_changed? || force_new_slug.to_b
end

#should_sanitize_urls?Boolean

Returns:

  • (Boolean)


484
485
486
487
# File 'app/models/digital_asset.rb', line 484

def should_sanitize_urls?
  (expanded_description_changed? && expanded_description.present?) ||
    (transcript_changed? && transcript.present?)
end

#site_mapsActiveRecord::Relation<SiteMap>

Returns:

  • (ActiveRecord::Relation<SiteMap>)

See Also:



132
# File 'app/models/digital_asset.rb', line 132

has_many :site_maps, as: :resource, dependent: :destroy

#slug_candidatesObject



381
382
383
384
385
386
387
388
389
# File 'app/models/digital_asset.rb', line 381

def slug_candidates
  sc = []
  if url.present?
    sc << [:url]
    sc << %i[url asset_identifier]
  end
  sc << %i[seo_title asset_identifier]
  sc
end

#tags_displayObject



407
408
409
# File 'app/models/digital_asset.rb', line 407

def tags_display
  tags.join(', ')
end

#thumbnail_urlObject



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

def thumbnail_url
  # Placeholder, implement in specialized class
end


421
422
423
424
425
426
427
428
429
430
431
432
# File 'app/models/digital_asset.rb', line 421

def touch_related
  return unless @refresh_cache.to_b

  if product_line_ids.present?
    paths = ProductLine.where(id: product_line_ids).pluck(:ltree_path_ids).compact
    unless paths.empty?
      ProductLine.where(ProductLine[:ltree_path_ids].ltree_descendant(paths))
                 .find_each(&:touch)
    end
  end
  items.find_each(&:touch)
end