diff --git a/build/media_source/templates/administrator/atum/scss/vendor/choicesjs/choices.scss b/build/media_source/templates/administrator/atum/scss/vendor/choicesjs/choices.scss index 95935cfa55bf6..da429f3e40688 100644 --- a/build/media_source/templates/administrator/atum/scss/vendor/choicesjs/choices.scss +++ b/build/media_source/templates/administrator/atum/scss/vendor/choicesjs/choices.scss @@ -60,7 +60,7 @@ position: relative; margin: 2px; color: $choices-list-multiple-item; //$white; - background-color: $choices-list-multiple-item-bg; // var(--template-bg-dark); + background-color: $choices-list-multiple-item-bg !important; // var(--template-bg-dark); margin-inline-end: 2px; border: 0; border-radius: $border-radius; diff --git a/components/com_config/tmpl/config/default.php b/components/com_config/tmpl/config/default.php index 9b2fe2e561db4..7d1f5abde0863 100644 --- a/components/com_config/tmpl/config/default.php +++ b/components/com_config/tmpl/config/default.php @@ -34,7 +34,7 @@ -
+
- + form->getFieldsets() as $fieldset) : ?>
label)) : ?> diff --git a/components/com_privacy/tmpl/request/default.php b/components/com_privacy/tmpl/request/default.php index 6604e14b88bc0..c40ca0808eecb 100644 --- a/components/com_privacy/tmpl/request/default.php +++ b/components/com_privacy/tmpl/request/default.php @@ -31,7 +31,7 @@ sendMailEnabled) : ?> - + form->getFieldsets() as $fieldset) : ?>
label)) : ?> diff --git a/components/com_users/tmpl/captive/default.php b/components/com_users/tmpl/captive/default.php index 231a448392239..d99864080ce8f 100644 --- a/components/com_users/tmpl/captive/default.php +++ b/components/com_users/tmpl/captive/default.php @@ -58,9 +58,9 @@ class="btn btn-sm btn-secondary" - diff --git a/components/com_users/tmpl/login/default_login.php b/components/com_users/tmpl/login/default_login.php index 0baf4b3c2b068..f066d6c72734d 100644 --- a/components/com_users/tmpl/login/default_login.php +++ b/components/com_users/tmpl/login/default_login.php @@ -52,7 +52,7 @@ - +
form->renderFieldset('credentials', ['class' => 'com-users-login__input']); ?> diff --git a/components/com_users/tmpl/login/default_logout.php b/components/com_users/tmpl/login/default_logout.php index a83fbabab9810..31c4681d0d7a9 100644 --- a/components/com_users/tmpl/login/default_logout.php +++ b/components/com_users/tmpl/login/default_logout.php @@ -41,7 +41,7 @@ - +
- + form->getFieldsets() as $fieldset) : ?> name === 'captcha' && $this->captchaEnabled) : ?> diff --git a/components/com_users/tmpl/remind/default.php b/components/com_users/tmpl/remind/default.php index 7f0eafb1a3989..a0d4453f7a180 100644 --- a/components/com_users/tmpl/remind/default.php +++ b/components/com_users/tmpl/remind/default.php @@ -29,7 +29,7 @@
- + form->getFieldsets() as $fieldset) : ?>
label)) : ?> diff --git a/components/com_users/tmpl/reset/complete.php b/components/com_users/tmpl/reset/complete.php index 3e8ef3a1f14d3..13fe66290e8dc 100644 --- a/components/com_users/tmpl/reset/complete.php +++ b/components/com_users/tmpl/reset/complete.php @@ -29,7 +29,7 @@ - + form->getFieldsets() as $fieldset) : ?>
label)) : ?> diff --git a/components/com_users/tmpl/reset/confirm.php b/components/com_users/tmpl/reset/confirm.php index b798d9e4dc770..755240c7896c7 100644 --- a/components/com_users/tmpl/reset/confirm.php +++ b/components/com_users/tmpl/reset/confirm.php @@ -29,7 +29,7 @@ - + form->getFieldsets() as $fieldset) : ?>
label)) : ?> diff --git a/components/com_users/tmpl/reset/default.php b/components/com_users/tmpl/reset/default.php index 3d294e6e622f0..7fa7267a5c047 100644 --- a/components/com_users/tmpl/reset/default.php +++ b/components/com_users/tmpl/reset/default.php @@ -29,7 +29,7 @@ - + form->getFieldsets() as $fieldset) : ?>
label)) : ?> diff --git a/libraries/src/HTML/Helpers/StringHelper.php b/libraries/src/HTML/Helpers/StringHelper.php index 4b521cf2f6445..2ee5da493fc33 100644 --- a/libraries/src/HTML/Helpers/StringHelper.php +++ b/libraries/src/HTML/Helpers/StringHelper.php @@ -40,7 +40,7 @@ abstract class StringHelper */ public static function truncate($text, $length = 0, $noSplit = true, $allowHtml = true) { - // Assume a lone open tag is invalid HTML. + // Assume a lone open tag is invalid HTML if ($length === 1 && $text[0] === '<') { return '...'; } @@ -156,87 +156,80 @@ public static function truncate($text, $length = 0, $noSplit = true, $allowHtml */ public static function truncateComplex($html, $maxLength = 0, $noSplit = true) { - // Start with some basic rules. $baseLength = \strlen($html); - // If the original HTML string is shorter than the $maxLength do nothing and return that. - if ($baseLength <= $maxLength || $maxLength === 0) { + // Early return for trivial cases + if ($maxLength === 0 || $baseLength <= $maxLength) { return $html; } - // Take care of short simple cases. - if ($maxLength <= 3 && $html[0] !== '<' && !str_contains(substr($html, 0, $maxLength - 1), '<') && $baseLength > $maxLength) { + // Special case: very short cutoff, plain text. + if ($maxLength <= 3 && $html[0] !== '<' && !str_contains(substr($html, 0, max(0, $maxLength - 1)), '<')) { return '...'; } - // Deal with maximum length of 1 where the string starts with a tag. + // Special case: string starts with a tag and maxLength is 1 if ($maxLength === 1 && $html[0] === '<') { - $endTagPos = \strlen(strstr($html, '>', true)); - $tag = substr($html, 1, $endTagPos); - - $l = $endTagPos + 1; - - if ($noSplit) { - return substr($html, 0, $l) . ''); + if ($endTagPos === false) { + return '...'; } - - // @todo: $character doesn't seem to be used... - $character = substr(strip_tags($html), 0, 1); - - return substr($html, 0, $l) . '..."; } - // First get the truncated plain text string. This is the rendered text we want to end up with. - $ptString = HTMLHelper::_('string.truncate', $html, $maxLength, $noSplit, $allowHtml = false); + // Get a plain text truncated string + $ptString = HTMLHelper::_('string.truncate', $html, $maxLength, $noSplit, false); - // It's all HTML, just return it. if ($ptString === '') { return $html; } - - // If the plain text is shorter than the max length the variable will not end in ... - // In that case we use the whole string. if (!str_ends_with($ptString, '...')) { return $html; } - - // Regular truncate gives us the ellipsis but we want to go back for text and tags. if ($ptString === '...') { $stripped = substr(strip_tags($html), 0, $maxLength); - $ptString = HTMLHelper::_('string.truncate', $stripped, $maxLength, $noSplit, $allowHtml = false); + $ptString = HTMLHelper::_('string.truncate', $stripped, $maxLength, $noSplit, false); } - - // We need to trim the ellipsis that truncate adds. $ptString = rtrim($ptString, '.'); - // Now deal with more complex truncation. while ($maxLength <= $baseLength) { - // Get the truncated string assuming HTML is allowed. - $htmlString = HTMLHelper::_('string.truncate', $html, $maxLength, $noSplit, $allowHtml = true); + $htmlString = HTMLHelper::_('string.truncate', $html, $maxLength, $noSplit, true); if ($htmlString === '...' && \strlen($ptString) + 3 > $maxLength) { - return $htmlString; + return '...'; } $htmlString = rtrim($htmlString, '.'); - // Now get the plain text from the HTML string and trim it. - $htmlStringToPtString = HTMLHelper::_('string.truncate', $htmlString, $maxLength, $noSplit, $allowHtml = false); + // Get the plain text version of the truncated HTML string + $htmlStringToPtString = HTMLHelper::_('string.truncate', $htmlString, $maxLength, $noSplit, false); $htmlStringToPtString = rtrim($htmlStringToPtString, '.'); - // If the new plain text string matches the original plain text string we are done. + // If plain text matches, we're done if ($ptString === $htmlStringToPtString) { + // Remove whitespace, non-breaking spaces, and trailing tags before the ellipsis + $htmlString = preg_replace('/( |\s)+(<\/[^>]+>)?$/u', '', $htmlString); + + // If it ends with a closing tag, try to inject the ellipsis before the last closing tag + if (preg_match('/(<\/[^>]+>)$/', $htmlString, $matches)) { + return preg_replace('/(<\/[^>]+>)$/', '...$1', $htmlString); + } return $htmlString . '...'; } - // Get the number of HTML tag characters in the first $maxLength characters + // Adjust length for HTML tags $diffLength = \strlen($ptString) - \strlen($htmlStringToPtString); - if ($diffLength <= 0) { + // Remove whitespace, non-breaking spaces, and trailing tags before the ellipsis + $htmlString = preg_replace('/( |\s)+(<\/[^>]+>)?$/u', '', $htmlString); + + // If it ends with a closing tag, inject the ellipsis before the last closing tag + if (preg_match('/(<\/[^>]+>)$/', $htmlString, $matches)) { + return preg_replace('/(<\/[^>]+>)$/', '...$1', $htmlString); + } return $htmlString . '...'; } - - // Set new $maxlength that adjusts for the HTML tags $maxLength += $diffLength; } diff --git a/libraries/src/MVC/Controller/FormController.php b/libraries/src/MVC/Controller/FormController.php index 231fce043c985..2da2087177f46 100644 --- a/libraries/src/MVC/Controller/FormController.php +++ b/libraries/src/MVC/Controller/FormController.php @@ -566,7 +566,7 @@ public function save($key = null, $urlVar = null) $urlVar = $key; } - $recordId = $this->input->getInt($urlVar); + $recordId = (int) $this->input->getInt($urlVar); // Populate the row id from the session. $data[$key] = $recordId; diff --git a/modules/mod_breadcrumbs/tmpl/default.php b/modules/mod_breadcrumbs/tmpl/default.php index df9b5f81cb8d1..b8448d058a925 100644 --- a/modules/mod_breadcrumbs/tmpl/default.php +++ b/modules/mod_breadcrumbs/tmpl/default.php @@ -121,7 +121,7 @@ $wa->addInline( 'script', json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | $prettyPrint), - ['name' => 'inline.mod_breadcrumbs-schemaorg'], + ['name' => 'inline.breadcrumbs-schemaorg'], ['type' => 'application/ld+json'] ); } diff --git a/plugins/quickicon/eos/src/Extension/Eos.php b/plugins/quickicon/eos/src/Extension/Eos.php index f87c5fccbaf0e..3e3505318b45a 100644 --- a/plugins/quickicon/eos/src/Extension/Eos.php +++ b/plugins/quickicon/eos/src/Extension/Eos.php @@ -38,7 +38,7 @@ final class Eos extends CMSPlugin implements SubscriberInterface * @var string * @since 4.4.0 */ - private const EOS_DATE = '2027-10-19'; + private const EOS_DATE = '2027-10-12'; /** * Load the language file on instantiation. @@ -189,7 +189,7 @@ private function getMessageInfo(int $monthsUntilEOS, int $inverted): array 'id' => 5, 'messageText' => 'PLG_QUICKICON_EOS_MESSAGE_ERROR_SUPPORT_ENDED', 'messageType' => 'error', - 'messageLink' => 'https://docs.joomla.org/Special:MyLanguage/Joomla_4.4.x_to_5.x_Planning_and_Upgrade_Step_by_Step', + 'messageLink' => 'https://docs.joomla.org/Special:MyLanguage/Joomla_5.4.x_to_6.x_Planning_and_Upgrade_Step_by_Step', 'snoozable' => false, ]; } @@ -200,7 +200,7 @@ private function getMessageInfo(int $monthsUntilEOS, int $inverted): array 'id' => 4, 'messageText' => 'PLG_QUICKICON_EOS_MESSAGE_WARNING_SUPPORT_ENDING', 'messageType' => 'warning', - 'messageLink' => 'https://docs.joomla.org/Special:MyLanguage/Joomla_4.4.x_to_5.x_Planning_and_Upgrade_Step_by_Step', + 'messageLink' => 'https://docs.joomla.org/Special:MyLanguage/Joomla_5.4.x_to_6.x_Planning_and_Upgrade_Step_by_Step', 'snoozable' => true, ]; } @@ -211,7 +211,7 @@ private function getMessageInfo(int $monthsUntilEOS, int $inverted): array 'id' => 3, 'messageText' => 'PLG_QUICKICON_EOS_MESSAGE_WARNING_SECURITY_ONLY', 'messageType' => 'warning', - 'messageLink' => 'https://docs.joomla.org/Special:MyLanguage/Joomla_4.4.x_to_5.x_Planning_and_Upgrade_Step_by_Step', + 'messageLink' => 'https://docs.joomla.org/Special:MyLanguage/Joomla_5.4.x_to_6.x_Planning_and_Upgrade_Step_by_Step', 'snoozable' => true, ]; } @@ -233,7 +233,7 @@ private function getMessageInfo(int $monthsUntilEOS, int $inverted): array 'id' => 1, 'messageText' => 'PLG_QUICKICON_EOS_MESSAGE_INFO_01', 'messageType' => 'info', - 'messageLink' => 'https://joomla.org/5', + 'messageLink' => 'https://joomla.org/6', 'snoozable' => true, ]; } diff --git a/plugins/system/schemaorg/src/Extension/Schemaorg.php b/plugins/system/schemaorg/src/Extension/Schemaorg.php index e2269aa6dbe95..5aa1a33b61b6e 100644 --- a/plugins/system/schemaorg/src/Extension/Schemaorg.php +++ b/plugins/system/schemaorg/src/Extension/Schemaorg.php @@ -10,6 +10,7 @@ namespace Joomla\Plugin\System\Schemaorg\Extension; +use Joomla\CMS\Event\Application\BeforeCompileHeadEvent as BeforeCompileHeadApplicationEvent; use Joomla\CMS\Event\Model; use Joomla\CMS\Event\Plugin\System\Schemaorg\BeforeCompileHeadEvent; use Joomla\CMS\Event\Plugin\System\Schemaorg\PrepareDataEvent; @@ -26,6 +27,7 @@ use Joomla\CMS\Schemaorg\SchemaorgServiceInterface; use Joomla\CMS\Uri\Uri; use Joomla\CMS\User\UserFactoryAwareTrait; +use Joomla\CMS\WebAsset\Exception\UnknownAssetException; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\ParameterType; use Joomla\Event\DispatcherAwareInterface; @@ -268,9 +270,11 @@ public function onContentAfterSave(Model\AfterSaveEvent $event) * * @since 5.0.0 */ - public function onBeforeCompileHead(): void + public function onBeforeCompileHead(BeforeCompileHeadApplicationEvent $event): void { - $app = $this->getApplication(); + $app = $event->getApplication(); + $doc = $event->getDocument(); + $wa = $doc->getWebAssetManager(); $baseType = $this->params->get('baseType', 'organization'); $itemId = (int) $app->getInput()->getInt('id'); @@ -383,11 +387,30 @@ public function onBeforeCompileHead(): void $webPageSchema['about'] = ['@id' => $baseId]; $webPageSchema['inLanguage'] = $app->getLanguage()->getTag(); - // We support Breadcrumb linking - $breadcrumbs = ModuleHelper::getModule('mod_breadcrumbs'); + // Support Breadcrumb Schema linking + try { + try { + $breadcrumbsAsset = $wa->getRegistry()->get('script', 'inline.breadcrumbs-schemaorg'); + } catch (UnknownAssetException $e) { + // Fallback for older versions of the breadcrumbs module + $breadcrumbsAsset = $wa->getRegistry()->get('script', 'inline.mod_breadcrumbs-schemaorg'); + trigger_deprecation( + 'joomla/schemaorg', + '5.4', + 'The inline.mod_breadcrumbs-schemaorg asset name is deprecated. Please use the generic inline.breadcrumbs-schemaorg asset name instead.' + ); + } + + $breadcrumbs = json_decode($breadcrumbsAsset->getOption('content'), true, 512, JSON_THROW_ON_ERROR); + + if ($breadcrumbs['@type'] !== 'BreadcrumbList') { + trigger_error('The breadcrumbs schema is not of type BreadcrumbList', E_USER_WARNING); + throw new UnknownAssetException(); + } - if (!empty($breadcrumbs->id)) { - $webPageSchema['breadcrumb'] = ['@id' => $domain . '#/schema/BreadcrumbList/' . (int) $breadcrumbs->id]; + $webPageSchema['breadcrumbs'] = ['@id' => $breadcrumbs['@id']]; + } catch (UnknownAssetException $e) { + // No Breadcrumbs Schema found, so we don't add it } $baseSchema['@graph'][] = $webPageSchema; @@ -440,7 +463,6 @@ public function onBeforeCompileHead(): void $schemaString = $schema->toString('JSON', ['bitmask' => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | $prettyPrint]); if ($schemaString !== '{}') { - $wa = $this->getApplication()->getDocument()->getWebAssetManager(); $wa->addInlineScript($schemaString, ['name' => 'inline.schemaorg'], ['type' => 'application/ld+json']); } } diff --git a/tests/Unit/Libraries/Cms/Html/HtmlStringTest.php b/tests/Unit/Libraries/Cms/Html/HtmlStringTest.php index 97f50b743f4d1..4d41bbace1714 100644 --- a/tests/Unit/Libraries/Cms/Html/HtmlStringTest.php +++ b/tests/Unit/Libraries/Cms/Html/HtmlStringTest.php @@ -328,7 +328,7 @@ public function getTestTruncateComplexData(): array 'Plain text', 8, true, - 'Plain...', + 'Plain...', ], /* * @todo: Check these tests: 'Plain html over the limit splitting first word' @@ -366,13 +366,13 @@ public function getTestTruncateComplexData(): array '
Plain text foo
', 8, false, - '
Plain te
...', + '
Plain te...
', ], 'No split' => [ '
Plain text foo
', 8, true, - '
Plain
...', + '
Plain...
', ], 'First character is < with a maximum length of 1, no split' => [ '
Plain text foo
',