0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-08 17:53:55 +08:00
discourse/app/controllers/admin/config/logo_controller.rb
Penar Musaraj 2d895de297
FEATURE: generated OpenGraph images for topics (#39194)
Adds the ability to automatically generate Open Graph images for topics
that don't have their own image. When enabled via the
generate_topic_og_image site setting, a background job creates a branded
1200×630 PNG for each new topic OP.

Key changes:
- TopicOgImageGenerator builds an SVG and converts it to PNG via
ImageMagick
  - Images generated via GenerateTopicOgImage Sidekiq job
- Generated images handled via og_image_upload_id column on the topics
table
- TopicView#image_url falls back to the generated OG image when no topic
image is available

This includes a preview for admins so they can see if they like this
feature or not:


https://github.com/user-attachments/assets/62d17704-4ee5-4859-ac37-b127fe27b85c


And it also respects the site's default theme: 

<img width="1200" height="630" alt="image"
src="https://github.com/user-attachments/assets/678f5c5d-e593-4385-b800-141196dfc221"
/>

---------

Co-authored-by: discourse-patch-triage[bot] <272280883+discourse-patch-triage[bot]@users.noreply.github.com>
Co-authored-by: Gabriel Grubba <gabriel@discourse.org>
2026-06-26 10:37:10 -04:00

73 lines
2.1 KiB
Ruby
Vendored

# frozen_string_literal: true
class Admin::Config::LogoController < Admin::AdminController
def index
end
def og_image_preview
raise Discourse::InvalidParameters.new(:topic_id) if params[:topic_id].blank?
topic = Topic.find_by(id: params[:topic_id])
raise Discourse::NotFound if topic.nil?
if !TopicOgImageGenerator.eligible?(topic)
render json: {
error: I18n.t("topic_og_image.preview_not_eligible"),
},
status: :unprocessable_entity
return
end
png_bytes = TopicOgImageGenerator.new(topic).generate_bytes
if png_bytes.blank?
render json: {
error: I18n.t("topic_og_image.preview_generation_failed"),
},
status: :unprocessable_entity
return
end
data_uri = "data:image/png;base64,#{Base64.strict_encode64(png_bytes)}"
render json: { url: data_uri, topic_id: topic.id, topic_title: topic.title }
end
def update
settings =
%i[
logo
logo_dark
large_icon
favicon
logo_small
logo_small_dark
mobile_logo
mobile_logo_dark
manifest_icon
manifest_screenshots
apple_touch_icon
digest_logo
generate_topic_og_image
opengraph_image
x_summary_large_image
].filter_map do |setting|
next if SiteSetting.hidden_settings.include?(setting)
{ setting_name: setting, value: params[setting] }
end
SiteSetting::Update.call(guardian:, params: { settings: }) do
on_success { render json: success_json }
on_failed_policy(:settings_are_visible) do |policy|
raise Discourse::InvalidParameters, policy.reason
end
on_failed_policy(:settings_are_unshadowed_globally) do |policy|
raise Discourse::InvalidParameters, policy.reason
end
on_failed_policy(:settings_are_configurable) do |policy|
raise Discourse::InvalidParameters, policy.reason
end
on_failed_policy(:values_are_valid) do |policy|
raise Discourse::InvalidParameters, policy.reason
end
end
end
end