diff --git a/libraries/src/Opengraph/MappableFieldInterface.php b/libraries/src/Opengraph/MappableFieldInterface.php new file mode 100644 index 00000000000..369b55153d6 --- /dev/null +++ b/libraries/src/Opengraph/MappableFieldInterface.php @@ -0,0 +1,32 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\CMS\Opengraph; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + + +/** + * Interface for fields that can be mapped to OpenGraph groups. + * + * @since __DEPLOY_VERSION__ + */ +interface MappableFieldInterface +{ + /** + * Returns the OpenGraph group this field should be listed under. + * + * @return OpengraphGroup One of the enum cases defined in {@see OpengraphGroup}. + * + * @since __DEPLOY_VERSION__ + */ + public static function getOpengraphGroup(): OpengraphGroup; +} diff --git a/libraries/src/Opengraph/OpengraphGroup.php b/libraries/src/Opengraph/OpengraphGroup.php new file mode 100644 index 00000000000..33834a20d09 --- /dev/null +++ b/libraries/src/Opengraph/OpengraphGroup.php @@ -0,0 +1,36 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\CMS\Opengraph; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Enumerates the logical OpenGraph groups that a custom field-type can + * register itself under. + * + * Third-party field-type plugins should return one of these cases from + * {@see MappableFieldInterface::getOpengraphGroup()} so the System – OpenGraph + * plugin knows how to categorise the field inside its mapping drop-down. + * + * @since __DEPLOY_VERSION__ + */ +enum OpengraphGroup: string +{ + /** Standard textual content (e.g. single-line, multi-line). */ + case TEXT = 'text-fields'; + + /** Image or media file (intro/full images, custom media fields, …). */ + case IMAGE = 'image-fields'; + + /** Alternate-text associated with an image (accessibility / SEO). */ + case IMAGE_ALT = 'image-alt-fields'; +} diff --git a/plugins/system/opengraph/src/Extension/opengraph.php b/plugins/system/opengraph/src/Extension/opengraph.php index faaf9b91d82..db146727101 100644 --- a/plugins/system/opengraph/src/Extension/opengraph.php +++ b/plugins/system/opengraph/src/Extension/opengraph.php @@ -25,6 +25,7 @@ use Joomla\CMS\Uri\Uri; use Joomla\Component\Categories\Administrator\Model\CategoryModel; use Joomla\Component\Content\Administrator\Model\ArticleModel; +use Joomla\Component\Fields\Administrator\Helper\FieldsHelper; use Joomla\Event\SubscriberInterface; use Joomla\Registry\Registry; @@ -425,8 +426,10 @@ private function getMultipleArticleMenuParams(string $option, string $view, ?int return $active->getParams(); } } elseif ($view === 'featured') { + // For featured view (no category ID) return $active->getParams(); } else { + // For other multi-article views, just return menu params return $active->getParams(); } } @@ -511,6 +514,23 @@ private function getOgTagsFromCategoryMappings(Registry $categoryParams, object */ private function getFieldValue(object $article, string $fieldName, array $articleImages): string { + + // Check if it's a custom field + if (strpos($fieldName, 'field.') === 0) { + $customFieldName = substr($fieldName, 6); + // Load custom fields for the article + $customFields = FieldsHelper::getFields('com_content.article', $article, true); + + foreach ($customFields as $field) { + if ($field->name == $customFieldName) { + return $field->value ?? ''; + } + } + + return ''; + } + + // Handle standard article fields $value = ''; switch ($fieldName) { diff --git a/plugins/system/opengraph/src/Field/OpengraphField.php b/plugins/system/opengraph/src/Field/OpengraphField.php index 88de25fd638..7f58b742ad9 100644 --- a/plugins/system/opengraph/src/Field/OpengraphField.php +++ b/plugins/system/opengraph/src/Field/OpengraphField.php @@ -10,10 +10,15 @@ namespace Joomla\Plugin\System\Opengraph\Field; use Joomla\CMS\Factory; +use Joomla\CMS\Fields\FieldsServiceInterface; use Joomla\CMS\Form\Field\GroupedlistField; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; +use Joomla\CMS\Opengraph\MappableFieldInterface; +use Joomla\CMS\Opengraph\OpengraphGroup; use Joomla\CMS\Opengraph\OpengraphServiceInterface; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Component\Fields\Administrator\Helper\FieldsHelper; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; @@ -97,6 +102,81 @@ protected function getGroups() } + + if (!$cmp instanceof FieldsServiceInterface) { + return $groups; + } + + // Allowed field types for each OpenGraph group + $allowedFieldTypes = [ + OpengraphGroup::TEXT->value => ['text', 'textarea'], + OpengraphGroup::IMAGE->value => ['media', 'imagelist'], + OpengraphGroup::IMAGE_ALT->value => ['text'], + ]; + + $nativeTypes = $allowedFieldTypes[$fieldType] ?? []; + + + + $catId = (int) $this->form->getValue('id'); // editing existing cat + if (!$catId) { + // Creating a new category: use the chosen parent so assignments still work + $catId = (int) $this->form->getValue('parent_id'); + } + + // Dummy item with catid so FieldsService filters by assignment + $scopeItem = $catId ? (object) ['catid' => $catId] : null; + + $customFields = FieldsHelper::getFields('com_content.article', $scopeItem); + $customOptions = []; + + foreach ($customFields as $field) { + $accept = \in_array($field->type, $nativeTypes, true); + + + // If not native-allowed, see if the field’s plugin implements our interface + if (!$accept) { + // Ensure the specific fields plugin is loaded + PluginHelper::importPlugin('fields', $field->type); + + $ucType = ucfirst((string) $field->type); + + // Candidate class names in priority order (modern first, then legacy) + $candidates = [ + "Joomla\\Plugin\\Fields\\{$ucType}\\Extension\\{$ucType}", // J4/5 namespaced + "Joomla\\Plugin\\Fields\\{$ucType}\\Field\\{$ucType}Field", // some third-party patterns + "PlgFields{$ucType}", // legacy non-namespaced + ]; + + $implements = false; + + foreach ($candidates as $fqcn) { + if (class_exists($fqcn) && is_subclass_of($fqcn, MappableFieldInterface::class)) { + $implements = ($fqcn::getOpengraphGroup()->value === $fieldType); + if ($implements) { + $accept = true; + break; + } + } + } + } + + + if (!$accept) { + continue; + } + + + + $label = $field->title . ' (' . $field->name . ')'; + $customOptions[] = HTMLHelper::_('select.option', 'field.' . $field->name, $label); + } + + if (!empty($customOptions)) { + $groups['Custom Fields'] = $customOptions; + } + + return $groups; } }