Files
MokoSuiteCross/source/packages/com_mokosuitecross/src/Controller/SocialImageController.php
T
jmiller d951d86b3a
Universal: Auto Version Bump / Version Bump (push) Successful in 8s
feat: add social image generator with PHP GD for OG images (#157)
- SocialImageHelper: generates 1200x630 OG images with title overlay
- SocialImageController: AJAX endpoint to generate from article data
- Config fields: bg color, text color, overlay style, site name override
- Supports background image scaling, dark/light overlay, TTF fonts

Closes #157
Authored-by: Moko Consulting
2026-06-28 11:43:09 -05:00

99 lines
3.3 KiB
PHP

<?php
/**
* @package MokoSuiteCross
* @subpackage com_mokosuitecross
* @author Moko Consulting <hello@mokoconsulting.tech>
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
* SPDX-License-Identifier: GPL-3.0-or-later
*/
namespace Joomla\Component\MokoSuiteCross\Administrator\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\MokoSuiteCross\Administrator\Helper\SocialImageHelper;
class SocialImageController extends BaseController
{
public function generate(): void
{
if (!Session::checkToken('get')) {
echo json_encode(['success' => false, 'error' => 'Invalid token']);
$this->app->close();
return;
}
$user = $this->app->getIdentity();
if (!$user->authorise('core.manage', 'com_mokosuitecross')) {
echo json_encode(['success' => false, 'error' => 'Permission denied']);
$this->app->close();
return;
}
$articleId = $this->input->getInt('article_id', 0);
if ($articleId < 1) {
echo json_encode(['success' => false, 'error' => 'Missing article ID']);
$this->app->close();
return;
}
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'title', 'images']))
->from($db->quoteName('#__content'))
->where($db->quoteName('id') . ' = ' . $articleId);
$db->setQuery($query);
$article = $db->loadObject();
if (!$article) {
echo json_encode(['success' => false, 'error' => 'Article not found']);
$this->app->close();
return;
}
$params = ComponentHelper::getParams('com_mokosuitecross');
$siteName = $params->get('social_image_site_name', '') ?: Factory::getApplication()->get('sitename', '');
$options = [
'bg_color' => $params->get('social_image_bg_color', '#1a1a2e'),
'text_color' => $params->get('social_image_text_color', '#ffffff'),
'overlay' => $params->get('social_image_overlay', 'dark'),
];
$backgroundPath = null;
$images = json_decode($article->images ?? '{}', true);
if (!empty($images['image_intro'])) {
$backgroundPath = JPATH_ROOT . '/' . ltrim($images['image_intro'], '/');
} elseif (!empty($images['image_fulltext'])) {
$backgroundPath = JPATH_ROOT . '/' . ltrim($images['image_fulltext'], '/');
}
try {
$imagePath = SocialImageHelper::generate($article->title, $siteName, $backgroundPath, $options);
$imageUrl = str_replace(JPATH_ROOT, Uri::root(true), str_replace('\\', '/', $imagePath));
$result = ['success' => true, 'image_url' => $imageUrl, 'image_path' => $imagePath];
} catch (\Throwable $e) {
$result = ['success' => false, 'error' => $e->getMessage()];
}
$this->app->setHeader('Content-Type', 'application/json; charset=utf-8');
echo json_encode($result);
$this->app->close();
}
}