forked from decidim/decidim
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'upstream/feature/update-meta-tags-for-s…
…ocial-previews' into temp-decidim-reviews
- Loading branch information
Showing
9 changed files
with
428 additions
and
111 deletions.
There are no files selected for viewing
1 change: 0 additions & 1 deletion
1
decidim-assemblies/app/views/decidim/assemblies/assemblies/show.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 0 additions & 103 deletions
103
decidim-core/app/helpers/decidim/meta_image_url_resolver.rb
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
175 changes: 175 additions & 0 deletions
175
decidim-core/app/resolvers/decidim/meta_image_url_resolver.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
# frozen_string_literal: true | ||
|
||
module Decidim | ||
# Resolves the most appropriate image URL for meta tags from a given resource within an organization. | ||
# Searches in the following order: attachment images, images in resource description, participatory space images, and default content block images. | ||
# Ensures the selected image is resized to 1200x630 pixels. | ||
class MetaImageUrlResolver | ||
include TranslatableAttributes | ||
|
||
def initialize(resource, current_organization) | ||
@resource = resource | ||
@current_organization = current_organization | ||
end | ||
|
||
# Resolves the image URL to be used for meta tags. | ||
# | ||
# @return [String, nil] - The resolved image URL or nil if no image is found. | ||
def resolve | ||
blob = determine_image_blob.presence | ||
resize_image_blob(blob) if blob.present? | ||
end | ||
|
||
private | ||
|
||
# Determines the image blob to be used for meta tags by following a hierarchy. | ||
# | ||
# @return [ActiveStorage::Blob, nil] - The image blob or nil if no image is found. | ||
def determine_image_blob | ||
methods_with_args = [ | ||
(has_attached_fields? ? { method: :blob_from_image_fields } : { method: :blob_from_attachment_image }), | ||
{ method: :blob_from_description, args: [@resource] }, | ||
{ method: :blob_from_participatory_space }, | ||
{ method: :blob_from_content_blocks } | ||
] | ||
|
||
methods_with_args << { method: :blob_from_attachment_image } if has_attached_fields? | ||
|
||
methods_with_args.each do |hash| | ||
next if hash[:method].nil? | ||
|
||
method = hash[:method] | ||
args = hash[:args] || [] | ||
blob = send(method, *args) | ||
return blob if blob.present? | ||
end | ||
nil | ||
end | ||
|
||
# Checks if the resource has attached fields like hero_image or banner_image. | ||
# | ||
# @return [Boolean] - True if the resource has attached fields, false otherwise. | ||
def has_attached_fields? | ||
@resource.respond_to?(:hero_image) || @resource.respond_to?(:banner_image) | ||
end | ||
|
||
# Fetches the image blob from the resource's attached image fields. | ||
# | ||
# @return [ActiveStorage::Blob, nil] - The image blob or nil if not found. | ||
def blob_from_image_fields | ||
path = get_attached_uploader_path(@resource, [:hero_image, :banner_image]) | ||
return unless path | ||
|
||
blob_key = extract_blob_key_from_path(path) | ||
find_blob_by_key(blob_key) | ||
end | ||
|
||
# Fetches the default homepage image blob. | ||
# | ||
# @return [ActiveStorage::Blob, nil] - The image blob or nil if not found. | ||
def blob_from_content_blocks | ||
content_block = Decidim::ContentBlock.find_by( | ||
organization: @current_organization, | ||
manifest_name: :hero, | ||
scope_name: :homepage | ||
) | ||
|
||
return unless content_block | ||
|
||
attachment = content_block.attachments.first | ||
return unless attachment&.file&.attached? | ||
|
||
attachment.file.blob | ||
end | ||
|
||
# Fetches the first attachment image blob. | ||
# | ||
# @return [ActiveStorage::Blob, nil] - The image blob or nil if not found. | ||
def blob_from_attachment_image | ||
attachment = find_image_attachment(@resource) | ||
return unless attachment&.file&.attached? | ||
|
||
attachment.file.blob | ||
end | ||
|
||
# Finds the first image attachment for a given entity. | ||
# | ||
# @param [Object] entity - The entity to search for attachments. | ||
# | ||
# @return [Decidim::Attachment, nil] - The first image attachment or nil if not found. | ||
def find_image_attachment(entity) | ||
entity.try(:attachments)&.find { |a| a.content_type.in?(%w(image/jpeg image/png)) } | ||
end | ||
|
||
# Extracts the first image blob from the resource description. | ||
# | ||
# @return [ActiveStorage::Blob, nil] - The image blob or nil if not found. | ||
def blob_from_description(resource) | ||
html_fields = [resource.try(:body), resource.try(:description), resource.try(:short_description)] | ||
|
||
html_fields.each do |html| | ||
next unless html | ||
|
||
html = translated_attribute(html).strip if html.is_a?(Hash) | ||
next if html.blank? | ||
|
||
image_element = Nokogiri::HTML(html).css("img").first | ||
next unless image_element | ||
|
||
image_url = image_element["src"] | ||
next if image_url.blank? | ||
|
||
blob_key = extract_blob_key_from_path(image_url) | ||
blob = find_blob_by_key(blob_key) | ||
return blob if blob.present? | ||
end | ||
|
||
nil | ||
end | ||
|
||
# Fetches the image blob from the participatory space. | ||
# | ||
# @return [ActiveStorage::Blob, nil] - The image blob or nil if not found. | ||
def blob_from_participatory_space | ||
participatory_space = @resource.try(:participatory_space) | ||
return unless participatory_space | ||
|
||
blob = participatory_space.try(:hero_image).try(:blob) || participatory_space.try(:banner_image).try(:blob) | ||
blob.presence || blob_from_description(participatory_space) | ||
end | ||
|
||
# Resizes the image blob to the specified dimensions (1200x630) if it is larger. | ||
# | ||
# @param [ActiveStorage::Blob] blob - The image blob to be resized. | ||
# | ||
# @return [String] - The URL of the resized image. | ||
def resize_image_blob(blob) | ||
return unless blob | ||
|
||
resized_variant = blob.variant(resize_to_limit: [1200, 630]).processed | ||
Rails.application.routes.url_helpers.rails_representation_url(resized_variant, only_path: true) | ||
end | ||
|
||
# Retrieves the path for the attached uploader of the specified image types. | ||
# | ||
# @param [Object] resource - The resource to search for attached uploaders. | ||
# @param [Array<Symbol>] image_types - The types of images to search for. | ||
# | ||
# @return [String, nil] - The path of the attached uploader or nil if not found. | ||
def get_attached_uploader_path(resource, image_types) | ||
image_types.each do |image_type| | ||
path = resource.try(:attached_uploader, image_type)&.path | ||
return path if path | ||
end | ||
nil | ||
end | ||
|
||
def extract_blob_key_from_path(path) | ||
path.split("/").second_to_last | ||
end | ||
|
||
def find_blob_by_key(blob_key) | ||
ActiveStorage::Blob.find_signed(blob_key) if blob_key.present? | ||
end | ||
end | ||
end |
Oops, something went wrong.