diff --git a/app/controllers/public/api/v1/portals/articles_controller.rb b/app/controllers/public/api/v1/portals/articles_controller.rb
index 02023559b..e6cc2aa69 100644
--- a/app/controllers/public/api/v1/portals/articles_controller.rb
+++ b/app/controllers/public/api/v1/portals/articles_controller.rb
@@ -17,7 +17,9 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B
limit_results
end
- def show; end
+ def show
+ @og_image_url = helpers.set_og_image_url(@portal.name, @article.title)
+ end
def tracking_pixel
@article = @portal.articles.find_by(slug: permitted_params[:article_slug])
diff --git a/app/controllers/public/api/v1/portals/categories_controller.rb b/app/controllers/public/api/v1/portals/categories_controller.rb
index c3a7b59e9..ebfcb310a 100644
--- a/app/controllers/public/api/v1/portals/categories_controller.rb
+++ b/app/controllers/public/api/v1/portals/categories_controller.rb
@@ -8,7 +8,9 @@ class Public::Api::V1::Portals::CategoriesController < Public::Api::V1::Portals:
@categories = @portal.categories.order(position: :asc)
end
- def show; end
+ def show
+ @og_image_url = helpers.set_og_image_url(@portal.name, @category.name)
+ end
private
diff --git a/app/controllers/public/api/v1/portals_controller.rb b/app/controllers/public/api/v1/portals_controller.rb
index e8fb867cb..df4552432 100644
--- a/app/controllers/public/api/v1/portals_controller.rb
+++ b/app/controllers/public/api/v1/portals_controller.rb
@@ -4,7 +4,9 @@ class Public::Api::V1::PortalsController < Public::Api::V1::Portals::BaseControl
before_action :redirect_to_portal_with_locale, only: [:show]
layout 'portal'
- def show; end
+ def show
+ @og_image_url = helpers.set_og_image_url('', @portal.header_text)
+ end
def sitemap
@help_center_url = @portal.custom_domain || ChatwootApp.help_center_root
diff --git a/app/helpers/portal_helper.rb b/app/helpers/portal_helper.rb
index 2b1ab7b21..3ed303556 100644
--- a/app/helpers/portal_helper.rb
+++ b/app/helpers/portal_helper.rb
@@ -1,4 +1,21 @@
module PortalHelper
+ def set_og_image_url(portal_name, title)
+ cdn_url = GlobalConfig.get('OG_IMAGE_CDN_URL')['OG_IMAGE_CDN_URL']
+ return if cdn_url.blank?
+
+ client_ref = GlobalConfig.get('OG_IMAGE_CLIENT_REF')['OG_IMAGE_CLIENT_REF']
+
+ uri = URI.parse(cdn_url)
+ uri.path = '/og'
+ uri.query = URI.encode_www_form(
+ clientRef: client_ref,
+ title: title,
+ portalName: portal_name
+ )
+
+ uri.to_s
+ end
+
def generate_portal_bg_color(portal_color, theme)
base_color = theme == 'dark' ? 'black' : 'white'
"color-mix(in srgb, #{portal_color} 20%, #{base_color})"
diff --git a/app/views/public/api/v1/portals/_hero.html.erb b/app/views/public/api/v1/portals/_hero.html.erb
index f71574692..0ce5ddde6 100644
--- a/app/views/public/api/v1/portals/_hero.html.erb
+++ b/app/views/public/api/v1/portals/_hero.html.erb
@@ -1,4 +1,15 @@
<% if !@is_plain_layout_enabled %>
+<% content_for :head do %>
+
<%= @portal.name %>
+
+
+ <% if @og_image_url.present? %>
+
+
+
+
+ <% end %>
+<% end %>
diff --git a/app/views/public/api/v1/portals/articles/show.html.erb b/app/views/public/api/v1/portals/articles/show.html.erb
index 080ab4a6a..9568709b9 100644
--- a/app/views/public/api/v1/portals/articles/show.html.erb
+++ b/app/views/public/api/v1/portals/articles/show.html.erb
@@ -2,13 +2,23 @@
<%= @article.title %> | <%= @portal.name %>
<% if @article.meta["title"].present? %>
">
+ ">
+ ">
<% end %>
<% if @article.meta["description"].present? %>
">
+ ">
+ ">
<% end %>
<% if @article.meta["tags"].present? %>
">
<% end %>
+ <% if @og_image_url.present? %>
+
+
+
+
+ <% end %>
<% end %>
<% if !@is_plain_layout_enabled %>
diff --git a/app/views/public/api/v1/portals/categories/show.html.erb b/app/views/public/api/v1/portals/categories/show.html.erb
index 3e36b4753..702483355 100644
--- a/app/views/public/api/v1/portals/categories/show.html.erb
+++ b/app/views/public/api/v1/portals/categories/show.html.erb
@@ -1,7 +1,16 @@
<% content_for :head do %>
<%= @category.name %> | <%= @portal.name %>
+
<% if @category.description.present? %>
+
+
+ <% end %>
+ <% if @og_image_url.present? %>
+
+
+
+
<% end %>
<% end %>
@@ -35,7 +44,7 @@
<%= I18n.t('public_portal.common.last_updated_on', last_updated_on: article.updated_at.strftime("%b %d, %Y")) %>
-
+
<% end %>
<% end %>
diff --git a/config/installation_config.yml b/config/installation_config.yml
index a089a4ea7..58d593502 100644
--- a/config/installation_config.yml
+++ b/config/installation_config.yml
@@ -360,3 +360,17 @@
value: 'v22.0'
locked: true
# ------- End of Instagram Channel Related Config ------- #
+
+# ------- OG Image Related Config ------- #
+- name: OG_IMAGE_CDN_URL
+ display_title: 'OG Image CDN URL'
+ description: 'The CDN URL for serving OG images'
+ value: ''
+ locked: false
+- name: OG_IMAGE_CLIENT_REF
+ display_title: 'OG Image Client Reference'
+ description: 'Token used to block unauthorized access to OG images'
+ value: ''
+ locked: false
+ type: secret
+# ------- End of OG Image Related Config ------- #
diff --git a/enterprise/app/controllers/enterprise/super_admin/app_configs_controller.rb b/enterprise/app/controllers/enterprise/super_admin/app_configs_controller.rb
index 2454295dc..67ce3a6a9 100644
--- a/enterprise/app/controllers/enterprise/super_admin/app_configs_controller.rb
+++ b/enterprise/app/controllers/enterprise/super_admin/app_configs_controller.rb
@@ -33,6 +33,7 @@ module Enterprise::SuperAdmin::AppConfigsController
def internal_config_options
%w[CHATWOOT_INBOX_TOKEN CHATWOOT_INBOX_HMAC_KEY ANALYTICS_TOKEN CLEARBIT_API_KEY DASHBOARD_SCRIPTS INACTIVE_WHATSAPP_NUMBERS BLOCKED_EMAIL_DOMAINS
- CAPTAIN_CLOUD_PLAN_LIMITS ACCOUNT_SECURITY_NOTIFICATION_WEBHOOK_URL CHATWOOT_INSTANCE_ADMIN_EMAIL]
+ CAPTAIN_CLOUD_PLAN_LIMITS ACCOUNT_SECURITY_NOTIFICATION_WEBHOOK_URL CHATWOOT_INSTANCE_ADMIN_EMAIL
+ OG_IMAGE_CDN_URL OG_IMAGE_CLIENT_REF]
end
end
diff --git a/spec/helpers/portal_helper_spec.rb b/spec/helpers/portal_helper_spec.rb
index dfd37138a..241b4c6dd 100644
--- a/spec/helpers/portal_helper_spec.rb
+++ b/spec/helpers/portal_helper_spec.rb
@@ -250,12 +250,45 @@ describe PortalHelper do
describe '#thumbnail_bg_color' do
it 'returns the correct color based on username length' do
expect(helper.thumbnail_bg_color('')).to be_in(['#6D95BA', '#A4C3C3', '#E19191'])
- expect(helper.thumbnail_bg_color('Joe')).to eq('#6D95BA') # Length 3, so index is 0
- expect(helper.thumbnail_bg_color('John')).to eq('#A4C3C3') # Length 4, so index is 1
- expect(helper.thumbnail_bg_color('Jane james')).to eq('#A4C3C3') # Length 10, so index is 1
- expect(helper.thumbnail_bg_color('Jane_123')).to eq('#E19191') # Length 8, so index is 2
- expect(helper.thumbnail_bg_color('AlexanderTheGreat')).to eq('#E19191') # Length 17, so index is 2
- expect(helper.thumbnail_bg_color('Reginald John Sans')).to eq('#6D95BA') # Length 18, so index is 0
+ expect(helper.thumbnail_bg_color('Joe')).to eq('#6D95BA')
+ expect(helper.thumbnail_bg_color('John')).to eq('#A4C3C3')
+ expect(helper.thumbnail_bg_color('Jane james')).to eq('#A4C3C3')
+ expect(helper.thumbnail_bg_color('Jane_123')).to eq('#E19191')
+ expect(helper.thumbnail_bg_color('AlexanderTheGreat')).to eq('#E19191')
+ expect(helper.thumbnail_bg_color('Reginald John Sans')).to eq('#6D95BA')
+ end
+ end
+
+ describe '#set_og_image_url' do
+ let(:portal_name) { 'Chatwoot Portal' }
+ let(:title) { 'Welcome to Chatwoot' }
+
+ context 'when CDN URL is present' do
+ before do
+ InstallationConfig.create!(name: 'OG_IMAGE_CDN_URL', value: 'https://cdn.example.com')
+ InstallationConfig.create!(name: 'OG_IMAGE_CLIENT_REF', value: 'client-123')
+ end
+
+ it 'returns the composed OG image URL with correct params' do
+ result = helper.set_og_image_url(portal_name, title)
+ uri = URI.parse(result)
+ expect(uri.path).to eq('/og')
+ params = Rack::Utils.parse_query(uri.query)
+ expect(params['clientRef']).to eq('client-123')
+ expect(params['title']).to eq(title)
+ expect(params['portalName']).to eq(portal_name)
+ end
+ end
+
+ context 'when CDN URL is blank' do
+ before do
+ InstallationConfig.create!(name: 'OG_IMAGE_CDN_URL', value: '')
+ InstallationConfig.create!(name: 'OG_IMAGE_CLIENT_REF', value: 'client-123')
+ end
+
+ it 'returns nil' do
+ expect(helper.set_og_image_url(portal_name, title)).to be_nil
+ end
end
end
end