diff --git a/CHANGELOG.md b/CHANGELOG.md index 51ed2a44..86a037a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,9 @@ - **Analytics service filter**: Filter heatmap and stats by service type with configurable date range - **Analytics service breakdown**: Per-service success rate, failure count, and average posts per day - **Analytics AJAX endpoint**: JSON heatmap data for dynamic filtering without page reload -- **Social image generator**: Generate Open Graph images with article title overlay using PHP GD library (#157) -- **Social image config**: Background color, text color, overlay style, and site name override in component options (#157) +- **Social image generator**: Generate branded 1200x630 OG images with article title overlay using PHP GD (#157) +- **Social image config**: Background color, text color, font size, and site name branding options (#157) +- **Generate Social Image button**: One-click image generation in the Share Content panel (#157) - **AI caption generation**: Generate platform-optimized cross-post captions from article content using Claude or OpenAI (#161) - **AI provider config**: New "AI Caption Generation" fieldset in component options with provider, API key, model, and tone settings - **AI Generate button**: One-click AI generation button in the Share Content panel that fills all caption fields diff --git a/source/packages/com_mokosuitecross/config.xml b/source/packages/com_mokosuitecross/config.xml index c93837fd..8385117d 100644 --- a/source/packages/com_mokosuitecross/config.xml +++ b/source/packages/com_mokosuitecross/config.xml @@ -266,7 +266,7 @@ -
+
app->getIdentity(); - if (!$user->authorise('core.manage', 'com_mokosuitecross')) { + if (!$user->authorise('core.edit', 'com_mokosuitecross')) { echo json_encode(['success' => false, 'error' => 'Permission denied']); $this->app->close(); @@ -49,47 +48,40 @@ class SocialImageController extends BaseController return; } + $params = ComponentHelper::getParams('com_mokosuitecross'); + + if (!(int) $params->get('social_image_enabled', 0)) { + echo json_encode(['success' => false, 'error' => 'Social image generator is not enabled']); + $this->app->close(); + + return; + } + $db = Factory::getDbo(); $query = $db->getQuery(true) - ->select($db->quoteName(['id', 'title', 'images'])) + ->select($db->quoteName('title')) ->from($db->quoteName('#__content')) ->where($db->quoteName('id') . ' = ' . $articleId); $db->setQuery($query); - $article = $db->loadObject(); + $title = $db->loadResult(); - if (!$article) { + if (!$title) { 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', ''); + $siteName = $this->app->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'), + $config = [ + 'bg_color' => $params->get('social_image_bg_color', '#1a1a2e'), + 'text_color' => $params->get('social_image_text_color', '#ffffff'), + 'font_size' => $params->get('social_image_font_size', 48), + 'show_site_name' => (bool) $params->get('social_image_show_site_name', 1), ]; - $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()]; - } + $result = SocialImageHelper::generate($title, $siteName, $config); $this->app->setHeader('Content-Type', 'application/json; charset=utf-8'); echo json_encode($result); diff --git a/source/packages/plg_content_mokosuitecross/src/Extension/MokoSuiteCrossContent.php b/source/packages/plg_content_mokosuitecross/src/Extension/MokoSuiteCrossContent.php index 3b3c7e28..99fe1bb1 100644 --- a/source/packages/plg_content_mokosuitecross/src/Extension/MokoSuiteCrossContent.php +++ b/source/packages/plg_content_mokosuitecross/src/Extension/MokoSuiteCrossContent.php @@ -257,6 +257,53 @@ XML; $form->load($aiXml); $form->setFieldAttribute('mokosuitecross_ai_generate', 'description', $aiButtonHtml, 'attribs'); } + // Social Image Generator button (#157) + $siParams = ComponentHelper::getParams('com_mokosuitecross'); + $siEnabled = (bool) $siParams->get('social_image_enabled', 0); + + if ($siEnabled && $articleId > 0) { + $siToken = Session::getFormToken(); + $siUrl = Uri::base() . 'index.php?option=com_mokosuitecross&task=socialimage.generate&format=raw&article_id=' . $articleId . '&' . $siToken . '=1'; + + $siButtonHtml = '
' + . '' + . '' + . '' + . '
' + . ''; + + $siXml = ' +
+ +
'; + $form->load($siXml); + $form->setFieldAttribute('mokosuitecross_si_generate', 'description', $siButtonHtml, 'attribs'); + } // Cross-post history panel for existing articles