diff --git a/.github/workflows/create-translation-pull-request-v5.yml b/.github/workflows/create-translation-pull-request-v5.yml index 673410d759b..44d0d574dd8 100644 --- a/.github/workflows/create-translation-pull-request-v5.yml +++ b/.github/workflows/create-translation-pull-request-v5.yml @@ -60,7 +60,7 @@ jobs: - name: Update static error pages run: | - npm ci --ignore-scripts && node build/build.js --build-pages + npm ci --ignore-scripts && node build/build.mjs --build-pages - name: Create commit continue-on-error: true diff --git a/README.md b/README.md index 0f1f11076e1..7e5c3252c7f 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,14 @@ Build Status | Drone-CI | AppVeyor | PHP | Node | npm | |------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|---------------------------------------------------------------------------------| -| [![Build Status](https://ci.joomla.org/api/badges/joomla/joomla-cms/status.svg?branch=5.3-dev)](https://ci.joomla.org/joomla/joomla-cms) | [![Build status](https://ci.appveyor.com/api/projects/status/ru6sxal8jmfckvjc/branch/5.3-dev?svg=true)](https://ci.appveyor.com/project/release-joomla/joomla-cms) | [![PHP](https://img.shields.io/badge/PHP-V8.1.0-green)](https://www.php.net/) | [![node-lts](https://img.shields.io/badge/Node-V20.0-green)](https://nodejs.org/en/) | [![npm](https://img.shields.io/badge/npm-v10.1.0-green)](https://nodejs.org/en/) | +| [![Build Status](https://ci.joomla.org/api/badges/joomla/joomla-cms/status.svg?branch=5.4-dev)](https://ci.joomla.org/joomla/joomla-cms) | [![Build status](https://ci.appveyor.com/api/projects/status/ru6sxal8jmfckvjc/branch/5.4-dev?svg=true)](https://ci.appveyor.com/project/release-joomla/joomla-cms) | [![PHP](https://img.shields.io/badge/PHP-V8.1.0-green)](https://www.php.net/) | [![node-lts](https://img.shields.io/badge/Node-V20.0-green)](https://nodejs.org/en/) | [![npm](https://img.shields.io/badge/npm-v10.1.0-green)](https://nodejs.org/en/) | Overview --------------------- * This is the source of Joomla! 5.x. * Joomla's [Official website](https://www.joomla.org). -* Joomla! 5.3 [version history](https://docs.joomla.org/Special:MyLanguage/Joomla_5.3_version_history). -* Detailed changes are in the [changelog](https://github.com/joomla/joomla-cms/commits/5.3-dev). +* Joomla! 5.4 [version history](https://docs.joomla.org/Special:MyLanguage/Joomla_5.4_version_history). +* Detailed changes are in the [changelog](https://github.com/joomla/joomla-cms/commits/5.4-dev). What is Joomla? --------------------- @@ -45,9 +45,9 @@ git clone https://github.com/joomla/joomla-cms.git ```bash cd joomla-cms ``` -- Go to the 5.3-dev branch: +- Go to the 5.4-dev branch: ```bash -git checkout 5.3-dev +git checkout 5.4-dev ``` - Install all the needed composer packages: ```bash diff --git a/README.txt b/README.txt index 6c7e6e9c7fb..bf42f84392a 100644 --- a/README.txt +++ b/README.txt @@ -3,8 +3,8 @@ Joomla! CMS™ 1- Overview * This is a Joomla! 5.x installation/upgrade package. * Joomla! Official site: https://www.joomla.org - * Joomla! 5.3 version history - https://docs.joomla.org/Special:MyLanguage/Joomla_5.3_version_history - * Detailed changes in the Changelog: https://github.com/joomla/joomla-cms/commits/5.3-dev + * Joomla! 5.4 version history - https://docs.joomla.org/Special:MyLanguage/Joomla_5.4_version_history + * Detailed changes in the Changelog: https://github.com/joomla/joomla-cms/commits/5.4-dev 2- What is Joomla? * Joomla! is a Content Management System (CMS) which enables you to build websites and powerful online applications. diff --git a/administrator/components/com_actionlogs/src/Field/LogcreatorField.php b/administrator/components/com_actionlogs/src/Field/LogcreatorField.php index 1426434f074..d7d5c516dba 100644 --- a/administrator/components/com_actionlogs/src/Field/LogcreatorField.php +++ b/administrator/components/com_actionlogs/src/Field/LogcreatorField.php @@ -21,9 +21,7 @@ * * @since 3.9.0 * - * No longer used, will be removed without replacement - * - * @deprecated 7.0 will be removed in 7.0 + * @deprecated 5.3 will be removed in 7.0 without replacement */ class LogcreatorField extends ListField { diff --git a/administrator/components/com_actionlogs/src/Model/ActionlogModel.php b/administrator/components/com_actionlogs/src/Model/ActionlogModel.php index 0a1ac8b1cd9..0f7e5dafa86 100644 --- a/administrator/components/com_actionlogs/src/Model/ActionlogModel.php +++ b/administrator/components/com_actionlogs/src/Model/ActionlogModel.php @@ -163,7 +163,8 @@ protected function sendNotificationEmails($messages, $username, $context) $m = []; $m['extension'] = Text::_($extension); $m['message'] = ActionlogsHelper::getHumanReadableLogMessage($message); - $m['date'] = HTMLHelper::_('date', $message->log_date, 'Y-m-d H:i:s T', 'UTC'); + $tzOffset = Factory::getApplication()->get('offset'); + $m['date'] = HTMLHelper::_('date', $message->log_date, 'Y-m-d H:i:s T', $tzOffset); $m['username'] = $username; $temp[] = $m; diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index ef787411d94..08d257de79b 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -2450,6 +2450,26 @@ public function deleteUnexistingFiles($dryRun = false, $suppressOutput = false) '/libraries/vendor/maximebf/debugbar/src/DebugBar/Storage/PdoStorage.php', '/libraries/vendor/maximebf/debugbar/src/DebugBar/Storage/RedisStorage.php', '/libraries/vendor/maximebf/debugbar/src/DebugBar/Storage/StorageInterface.php', + // From 5.3.0-beta3 to 5.3.0-rc1 + '/libraries/vendor/joomla/http/.drone.jsonnet', + '/libraries/vendor/joomla/http/.drone.yml', + '/libraries/vendor/joomla/oauth1/.drone.jsonnet', + '/libraries/vendor/joomla/oauth1/.drone.yml', + '/libraries/vendor/joomla/oauth2/.drone.jsonnet', + '/libraries/vendor/joomla/oauth2/.drone.yml', + '/libraries/vendor/joomla/router/.drone.jsonnet', + '/libraries/vendor/joomla/router/.drone.yml', + '/libraries/vendor/joomla/string/.drone.jsonnet', + '/libraries/vendor/joomla/string/.drone.yml', + '/libraries/vendor/joomla/uri/.drone.jsonnet', + '/libraries/vendor/joomla/uri/.drone.yml', + '/libraries/vendor/joomla/utilities/.drone.jsonnet', + '/libraries/vendor/joomla/utilities/.drone.yml', + // From 5.3.0-rc1 to 5.3.0-rc2 + '/libraries/vendor/algo26-matthias/idna-convert/Dockerfile', + '/libraries/vendor/algo26-matthias/idna-convert/compose.yml', + '/libraries/vendor/algo26-matthias/idna-convert/src/Exception/Std3AsciiRulesViolationException.php', + '/libraries/vendor/algo26-matthias/idna-convert/src/TranscodeUnicode/ByteLengthTrait.php', ]; $folders = [ diff --git a/administrator/components/com_admin/sql/updates/mysql/5.4.0-2025-04-23.sql b/administrator/components/com_admin/sql/updates/mysql/5.4.0-2025-04-23.sql new file mode 100644 index 00000000000..a806087e70d --- /dev/null +++ b/administrator/components/com_admin/sql/updates/mysql/5.4.0-2025-04-23.sql @@ -0,0 +1,2 @@ +INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `locked`, `manifest_cache`, `params`, `custom_data`, `ordering`, `state`) VALUES +(0, 'plg_behaviour_compat6', 'plugin', 'compat6', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', -1, 0); diff --git a/administrator/components/com_admin/sql/updates/mysql/5.4.0-2025-05-10.sql b/administrator/components/com_admin/sql/updates/mysql/5.4.0-2025-05-10.sql new file mode 100644 index 00000000000..e3d6994d83b --- /dev/null +++ b/administrator/components/com_admin/sql/updates/mysql/5.4.0-2025-05-10.sql @@ -0,0 +1,26 @@ +INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `locked`, `manifest_cache`, `params`, `custom_data`, `ordering`, `state`) +SELECT 0, 'plg_quickicon_autoupdate', 'plugin', 'autoupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', -1, 0 +WHERE NOT EXISTS (SELECT * FROM `#__extensions` e WHERE e.`type` = 'plugin' AND e.`element` = 'autoupdate' AND e.`folder` = 'quickicon' AND e.`client_id` = 0); + +INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `locked`, `manifest_cache`, `params`, `custom_data`, `ordering`, `state`) +SELECT 0, 'plg_webservices_joomlaupdate', 'plugin', 'joomlaupdate', 'webservices', 0, 1, 1, 0, 1, '', '', '', -1, 0 +WHERE NOT EXISTS (SELECT * FROM `#__extensions` e WHERE e.`type` = 'plugin' AND e.`element` = 'joomlaupdate' AND e.`folder` = 'webservices' AND e.`client_id` = 0); + +INSERT INTO `#__mail_templates` (`template_id`, `extension`, `language`, `subject`, `body`, `htmlbody`, `attachments`, `params`) VALUES +('com_joomlaupdate.update.success', 'com_joomlaupdate', '', 'COM_JOOMLAUPDATE_UPDATE_SUCCESS_MAIL_SUBJECT', 'COM_JOOMLAUPDATE_UPDATE_SUCCESS_MAIL_BODY', '', '', '{"tags":["newversion","oldversion","sitename","url"]}'), +('com_joomlaupdate.update.failed', 'com_joomlaupdate', '', 'COM_JOOMLAUPDATE_UPDATE_FAILED_MAIL_SUBJECT', 'COM_JOOMLAUPDATE_UPDATE_FAILED_MAIL_BODY', '', '', '{"tags":["newversion","oldversion","sitename","url"]}'); + +-- add post-installation message for automated updates +INSERT INTO `#__postinstall_messages` (`extension_id`, `title_key`, `description_key`, `action_key`, `language_extension`, `language_client_id`, `type`, `action_file`, `action`, `condition_file`, `condition_method`, `version_introduced`, `enabled`) +SELECT `extension_id`, 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_TITLE', 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_DESCRIPTION', 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_ACTION', 'com_joomlaupdate', 1, 'action', 'admin://components/com_joomlaupdate/postinstall/autoupdate.php', 'com_joomlaupdate_postinstall_autoupdate_action', 'admin://components/com_joomlaupdate/postinstall/autoupdate.php', 'com_joomlaupdate_postinstall_autoupdate_condition', '5.4.0', 1 FROM `#__extensions` WHERE `name` = 'files_joomla'; + +-- disable autostart for the previous tour +UPDATE `#__guidedtours` SET `autostart` = 0 WHERE `uid` = 'joomla-whatsnew-5-3'; + +INSERT INTO `#__guidedtours` (`title`, `description`, `extensions`, `url`, `published`, `language`, `note`, `access`, `uid`, `autostart`, `created`, `created_by`, `modified`, `modified_by`) VALUES + ('COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_TITLE', 'COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_DESCRIPTION', '["com_cpanel"]', 'administrator/index.php', 1, '*', '', 1, 'joomla-whatsnew-5-4', 1, CURRENT_TIMESTAMP(), 0, CURRENT_TIMESTAMP(), 0); + +INSERT INTO `#__guidedtour_steps` (`title`, `description`, `position`, `target`, `type`, `interactive_type`, `url`, `published`, `language`, `note`, `params`, `tour_id`, `created`, `created_by`, `modified`, `modified_by`) +SELECT 'COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_STEP_0_TITLE', 'COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_STEP_0_DESCRIPTION', 'right', '#sidebarmenu nav > ul:first-of-type > li:last-child', 0, 1, '', 1, '*', '', '"{\"required\":1,\"requiredvalue\":\"\"}"', MAX(`id`), CURRENT_TIMESTAMP(), 0, CURRENT_TIMESTAMP(), 0 +FROM `#__guidedtours` +WHERE `uid` = 'joomla-whatsnew-5-4'; diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.2.3-2025-01-09.sql b/administrator/components/com_admin/sql/updates/postgresql/5.2.3-2025-01-09.sql index d2b74690080..27f126404b0 100644 --- a/administrator/components/com_admin/sql/updates/postgresql/5.2.3-2025-01-09.sql +++ b/administrator/components/com_admin/sql/updates/postgresql/5.2.3-2025-01-09.sql @@ -1,3 +1,3 @@ UPDATE "#__mail_templates" -SET "params" = '"tags":["messages","message","date","extension","username"]}' +SET "params" = '{"tags":["messages","message","date","extension","username"]}' WHERE "template_id" = 'com_actionlogs.notification'; diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.3.1-2025-04-27.sql b/administrator/components/com_admin/sql/updates/postgresql/5.3.1-2025-04-27.sql new file mode 100644 index 00000000000..9cc623fb217 --- /dev/null +++ b/administrator/components/com_admin/sql/updates/postgresql/5.3.1-2025-04-27.sql @@ -0,0 +1,4 @@ +UPDATE "#__mail_templates" +SET "params" = '{"tags":["messages","message","date","extension","username"]}' +WHERE "template_id" = 'com_actionlogs.notification' +AND "params" = '"tags":["messages","message","date","extension","username"]}'; diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.4.0-2025-04-23.sql b/administrator/components/com_admin/sql/updates/postgresql/5.4.0-2025-04-23.sql new file mode 100644 index 00000000000..7252ac4a984 --- /dev/null +++ b/administrator/components/com_admin/sql/updates/postgresql/5.4.0-2025-04-23.sql @@ -0,0 +1,2 @@ +INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "locked", "manifest_cache", "params", "custom_data", "ordering", "state") VALUES +(0, 'plg_behaviour_compat6', 'plugin', 'compat6', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', -1, 0); diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.4.0-2025-05-10.sql b/administrator/components/com_admin/sql/updates/postgresql/5.4.0-2025-05-10.sql new file mode 100644 index 00000000000..847a14d4cc3 --- /dev/null +++ b/administrator/components/com_admin/sql/updates/postgresql/5.4.0-2025-05-10.sql @@ -0,0 +1,26 @@ +INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "locked", "manifest_cache", "params", "custom_data", "ordering", "state") +SELECT 0, 'plg_quickicon_autoupdate', 'plugin', 'autoupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', -1, 0 +WHERE NOT EXISTS (SELECT * FROM "#__extensions" e WHERE e."type" = 'plugin' AND e."element" = 'autoupdate' AND e."folder" = 'quickicon' AND e."client_id" = 0); + +INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "locked", "manifest_cache", "params", "custom_data", "ordering", "state") +SELECT 0, 'plg_webservices_joomlaupdate', 'plugin', 'joomlaupdate', 'webservices', 0, 1, 1, 0, 1, '', '', '', -1, 0 +WHERE NOT EXISTS (SELECT * FROM "#__extensions" e WHERE e."type" = 'plugin' AND e."element" = 'joomlaupdate' AND e."folder" = 'webservices' AND e."client_id" = 0); + +INSERT INTO "#__mail_templates" ("template_id", "extension", "language", "subject", "body", "htmlbody", "attachments", "params") VALUES +('com_joomlaupdate.update.success', 'com_joomlaupdate', '', 'COM_JOOMLAUPDATE_UPDATE_SUCCESS_MAIL_SUBJECT', 'COM_JOOMLAUPDATE_UPDATE_SUCCESS_MAIL_BODY', '', '', '{"tags":["newversion","oldversion","sitename","url"]}'), +('com_joomlaupdate.update.failed', 'com_joomlaupdate', '', 'COM_JOOMLAUPDATE_UPDATE_FAILED_MAIL_SUBJECT', 'COM_JOOMLAUPDATE_UPDATE_FAILED_MAIL_BODY', '', '', '{"tags":["newversion","oldversion","sitename","url"]}'); + +-- add post-installation message for automated updates +INSERT INTO "#__postinstall_messages" ("extension_id", "title_key", "description_key", "action_key", "language_extension", "language_client_id", "type", "action_file", "action", "condition_file", "condition_method", "version_introduced", "enabled") +SELECT "extension_id", 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_TITLE', 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_DESCRIPTION', 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_ACTION', 'com_joomlaupdate', 1, 'action', 'admin://components/com_joomlaupdate/postinstall/autoupdate.php', 'com_joomlaupdate_postinstall_autoupdate_action', 'admin://components/com_joomlaupdate/postinstall/autoupdate.php', 'com_joomlaupdate_postinstall_autoupdate_condition', '5.4.0', 1 FROM "#__extensions" WHERE "name" = 'files_joomla'; + +-- disable autostart for the previous tour +UPDATE "#__guidedtours" SET "autostart" = 0 WHERE "uid" = 'joomla-whatsnew-5-3'; + +INSERT INTO "#__guidedtours" ("title", "description", "extensions", "url", "published", "language", "note", "access", "uid", "autostart", "created", "created_by", "modified", "modified_by") VALUES + ('COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_TITLE', 'COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_DESCRIPTION', '["com_cpanel"]', 'administrator/index.php', 1, '*', '', 1, 'joomla-whatsnew-5-4', 1, CURRENT_TIMESTAMP, 0, CURRENT_TIMESTAMP, 0); + +INSERT INTO "#__guidedtour_steps" ("title", "description", "position", "target", "type", "interactive_type", "url", "published", "language", "note", "params", "tour_id", "created", "created_by", "modified", "modified_by") +SELECT 'COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_STEP_0_TITLE', 'COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_STEP_0_DESCRIPTION', 'right', '#sidebarmenu nav > ul:first-of-type > li:last-child', 0, 1, '', 1, '*', '', '"{\"required\":1,\"requiredvalue\":\"\"}"', MAX("id"), CURRENT_TIMESTAMP, 0, CURRENT_TIMESTAMP, 0 +FROM "#__guidedtours" +WHERE "uid" = 'joomla-whatsnew-5-4'; diff --git a/administrator/components/com_associations/src/Model/AssociationsModel.php b/administrator/components/com_associations/src/Model/AssociationsModel.php index 8748cc4f3ba..c0cffcc55f7 100644 --- a/administrator/components/com_associations/src/Model/AssociationsModel.php +++ b/administrator/components/com_associations/src/Model/AssociationsModel.php @@ -328,7 +328,7 @@ protected function getListQuery() } // If component item type supports access level, select the access level also. - if (\array_key_exists('acl', $support) && $support['acl'] == true && !empty($fields['access'])) { + if (\array_key_exists('acl', $support) && $support['acl'] && !empty($fields['access'])) { $query->select($db->quoteName($fields['access'], 'access')); // Join over the access levels. diff --git a/administrator/components/com_categories/src/Model/CategoriesModel.php b/administrator/components/com_categories/src/Model/CategoriesModel.php index defbc31c0aa..a2e7a2c6a0d 100644 --- a/administrator/components/com_categories/src/Model/CategoriesModel.php +++ b/administrator/components/com_categories/src/Model/CategoriesModel.php @@ -455,7 +455,7 @@ public function getItems() { $items = parent::getItems(); - if ($items != false) { + if ($items) { $extension = $this->getState('filter.extension'); $this->countItems($items, $extension); diff --git a/administrator/components/com_config/src/Model/ApplicationModel.php b/administrator/components/com_config/src/Model/ApplicationModel.php index 8100c81dca1..88f218cd156 100644 --- a/administrator/components/com_config/src/Model/ApplicationModel.php +++ b/administrator/components/com_config/src/Model/ApplicationModel.php @@ -585,11 +585,11 @@ public function save($data) } // Give a warning if the cache-folder can not be opened - if ($data['caching'] > 0 && $data['cache_handler'] == 'file' && @opendir($path) == false) { + if ($data['caching'] > 0 && $data['cache_handler'] == 'file' && @opendir($path) === false) { $error = true; // If a custom path is in use, try using the system default instead of disabling cache - if ($path !== JPATH_CACHE && @opendir(JPATH_CACHE) != false) { + if ($path !== JPATH_CACHE && @opendir(JPATH_CACHE) !== false) { try { Log::add( Text::sprintf('COM_CONFIG_ERROR_CUSTOM_CACHE_PATH_NOTWRITABLE_USING_DEFAULT', $path, JPATH_CACHE), diff --git a/administrator/components/com_content/forms/filter_articles.xml b/administrator/components/com_content/forms/filter_articles.xml index a79e0f62f4c..9c14f781e37 100644 --- a/administrator/components/com_content/forms/filter_articles.xml +++ b/administrator/components/com_content/forms/filter_articles.xml @@ -75,7 +75,7 @@ hint="JOPTION_SELECT_AUTHOR" class="js-select-submit-on-change" > - + @@ -92,13 +92,16 @@ + default="" + > + + - + + + default="" + > + + set('catid', $app->getInput()->getInt('catid', (!empty($filters['category_id']) ? $filters['category_id'] : null))); + + // If multiple categories are filtered, pick the first one to avoid loading all fields + $filteredCategories = $filters['category_id'] ?? null; + $selectedCatId = null; + + if (\is_array($filteredCategories)) { + $selectedCatId = (int) reset($filteredCategories); + } elseif (!empty($filteredCategories)) { + $selectedCatId = (int) $filteredCategories; + } + + $data->set('catid', $app->getInput()->getInt('catid', $selectedCatId)); if ($app->isClient('administrator')) { $data->set('language', $app->getInput()->getString('language', (!empty($filters['language']) ? $filters['language'] : null))); @@ -708,7 +719,7 @@ public function save($data) $check = $input->post->get('jform', [], 'array'); foreach ($data['urls'] as $i => $url) { - if ($url != false && ($i == 'urla' || $i == 'urlb' || $i == 'urlc')) { + if (trim($url) !== '' && ($i == 'urla' || $i == 'urlb' || $i == 'urlc')) { if (preg_match('~^#[a-zA-Z]{1}[a-zA-Z0-9-_:.]*$~', $check['urls'][$i]) == 1) { $data['urls'][$i] = $check['urls'][$i]; } else { @@ -751,6 +762,12 @@ public function save($data) } elseif ($data['alias'] == $origTable->alias) { $data['alias'] = ''; } + + if (!$this->workflowEnabled) { + $data['state'] = 0; + } else { + $data['transition'] = ''; + } } // Automatic handling of alias for empty fields diff --git a/administrator/components/com_content/src/Model/ArticlesModel.php b/administrator/components/com_content/src/Model/ArticlesModel.php index ac02ff42991..3bb19a30431 100644 --- a/administrator/components/com_content/src/Model/ArticlesModel.php +++ b/administrator/components/com_content/src/Model/ArticlesModel.php @@ -400,18 +400,49 @@ protected function getListQuery() if (is_numeric($authorId)) { $authorId = (int) $authorId; - $type = $this->getState('filter.author_id.include', true) ? ' = ' : ' <> '; - $query->where($db->quoteName('a.created_by') . $type . ':authorId') - ->bind(':authorId', $authorId, ParameterType::INTEGER); + + if ($authorId === 0) { + // Only show deleted authors' articles + $subQuery = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__users')); + + $query->where($db->quoteName('a.created_by') . ' NOT IN (' . $subQuery . ')'); + } else { + $type = $this->getState('filter.author_id.include', true) ? ' = ' : ' <> '; + $query->where($db->quoteName('a.created_by') . $type . ':authorId') + ->bind(':authorId', $authorId, ParameterType::INTEGER); + } } elseif (\is_array($authorId)) { // Check to see if by_me is in the array - if (\in_array('by_me', $authorId)) { + $keyByMe = array_search('by_me', $authorId); + + if ($keyByMe !== false) { // Replace by_me with the current user id in the array - $authorId['by_me'] = $user->id; + $authorId[$keyByMe] = $user->id; } $authorId = ArrayHelper::toInteger($authorId); - $query->whereIn($db->quoteName('a.created_by'), $authorId); + + if (\in_array(0, $authorId)) { + // Remove 0 from array and handle deleted users with OR condition + $authorId = array_filter($authorId); + + // Subquery for deleted users + $subQuery = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__users')); + + // Build WHERE with both conditions + $query->where('(' . + $db->quoteName('a.created_by') . ' NOT IN (' . $subQuery . ')' . + (!empty($authorId) + ? ' OR ' . $db->quoteName('a.created_by') . ' IN (' . implode(',', $query->bindArray($authorId)) . ')' + : '') . + ')'); + } else { + $query->whereIn($db->quoteName('a.created_by'), $authorId); + } } // Filter by search in title. @@ -460,7 +491,13 @@ protected function getListQuery() } if ($tag && \is_array($tag)) { - $tag = ArrayHelper::toInteger($tag); + $tag = ArrayHelper::toInteger($tag); + $includeNone = false; + + if (\in_array(0, $tag)) { + $tag = array_filter($tag); + $includeNone = true; + } $subQuery = $db->getQuery(true) ->select('DISTINCT ' . $db->quoteName('content_item_id')) @@ -473,16 +510,48 @@ protected function getListQuery() ); $query->join( - 'INNER', + $includeNone ? 'LEFT' : 'INNER', '(' . $subQuery . ') AS ' . $db->quoteName('tagmap'), $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id') ); - } elseif ($tag = (int) $tag) { - $query->join( - 'INNER', - $db->quoteName('#__contentitem_tag_map', 'tagmap'), - $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id') - ) + + if ($includeNone) { + $subQuery2 = $db->getQuery(true) + ->select('DISTINCT ' . $db->quoteName('content_item_id')) + ->from($db->quoteName('#__contentitem_tag_map')) + ->where($db->quoteName('type_alias') . ' = ' . $db->quote('com_content.article')); + $query->join( + 'LEFT', + '(' . $subQuery2 . ') AS ' . $db->quoteName('tagmap2'), + $db->quoteName('tagmap2.content_item_id') . ' = ' . $db->quoteName('a.id') + ) + ->where( + '(' . $db->quoteName('tagmap.content_item_id') . ' IS NOT NULL OR ' + . $db->quoteName('tagmap2.content_item_id') . ' IS NULL)' + ); + } + } elseif (is_numeric($tag)) { + $tag = (int) $tag; + + if ($tag === 0) { + $subQuery = $db->getQuery(true) + ->select('DISTINCT ' . $db->quoteName('content_item_id')) + ->from($db->quoteName('#__contentitem_tag_map')) + ->where($db->quoteName('type_alias') . ' = ' . $db->quote('com_content.article')); + + // Only show articles without tags + $query->join( + 'LEFT', + '(' . $subQuery . ') AS ' . $db->quoteName('tagmap'), + $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id') + ) + ->where($db->quoteName('tagmap.content_item_id') . ' IS NULL'); + } else { + $query->join( + 'INNER', + $db->quoteName('#__contentitem_tag_map', 'tagmap'), + $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id') + ) ->where( [ $db->quoteName('tagmap.tag_id') . ' = :tag', @@ -490,6 +559,7 @@ protected function getListQuery() ] ) ->bind(':tag', $tag, ParameterType::INTEGER); + } } // Add the list ordering clause. diff --git a/administrator/components/com_content/tmpl/articles/default.php b/administrator/components/com_content/tmpl/articles/default.php index 862896a1005..60a826de318 100644 --- a/administrator/components/com_content/tmpl/articles/default.php +++ b/administrator/components/com_content/tmpl/articles/default.php @@ -321,12 +321,12 @@ escape($item->access_level); ?> - created_by != 0) : ?> + author_name)) : ?> escape($item->author_name); ?> - + [ ] created_by_alias) : ?>
escape($item->created_by_alias)); ?>
diff --git a/administrator/components/com_content/tmpl/articles/default.xml b/administrator/components/com_content/tmpl/articles/default.xml index a8f618eb70d..424d988d71c 100644 --- a/administrator/components/com_content/tmpl/articles/default.xml +++ b/administrator/components/com_content/tmpl/articles/default.xml @@ -46,7 +46,7 @@ class="multipleAuthors" filter="array" > - +
diff --git a/administrator/components/com_content/tmpl/featured/default.php b/administrator/components/com_content/tmpl/featured/default.php index df58b6aa66b..a6541223ffe 100644 --- a/administrator/components/com_content/tmpl/featured/default.php +++ b/administrator/components/com_content/tmpl/featured/default.php @@ -315,12 +315,12 @@ escape($item->access_level); ?> - created_by != 0) : ?> + author_name)) : ?> escape($item->author_name); ?> - + [ ] created_by_alias) : ?>
escape($item->created_by_alias)); ?>
diff --git a/administrator/components/com_fields/tmpl/fields/default.php b/administrator/components/com_fields/tmpl/fields/default.php index 77b27cce1aa..0c237e1ddde 100644 --- a/administrator/components/com_fields/tmpl/fields/default.php +++ b/administrator/components/com_fields/tmpl/fields/default.php @@ -179,7 +179,7 @@ escape($item->type); ?> - escape($item->group_title); ?> + group_title) ? $this->escape($item->group_title) : '[ ' . Text::_('JNONE') . ' ]'; ?> escape($item->access_level); ?> diff --git a/administrator/components/com_finder/src/Indexer/Parser/Html.php b/administrator/components/com_finder/src/Indexer/Parser/Html.php index ac569bbe451..67c6561c196 100644 --- a/administrator/components/com_finder/src/Indexer/Parser/Html.php +++ b/administrator/components/com_finder/src/Indexer/Parser/Html.php @@ -71,9 +71,9 @@ public function parse($input) // Add a space before both the OPEN and CLOSE tags of BLOCK and LINE BREAKING elements, // e.g. 'all

mobile List

' will become 'all mobile List' $input = preg_replace('/(<|<\/)(' . - 'address|article|aside|blockquote|br|canvas|dd|div|dl|dt|' . - 'fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|li|' . - 'main|nav|noscript|ol|output|p|pre|section|table|tfoot|ul|video' . + 'address|article|aside|blockquote|br|canvas|cite|code|data|details|dd|div|dl|dt|' . + 'fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|li|label|' . + 'main|nav|noscript|ol|option|output|p|pre|section|table|td|tfoot|th|ul|video' . ')\b/i', ' $1$2', $input); // Strip HTML tags. diff --git a/administrator/components/com_finder/src/Service/HTML/Query.php b/administrator/components/com_finder/src/Service/HTML/Query.php index 2f2a6ffaafa..bdb1c1f99ee 100644 --- a/administrator/components/com_finder/src/Service/HTML/Query.php +++ b/administrator/components/com_finder/src/Service/HTML/Query.php @@ -41,14 +41,14 @@ public static function explained(IndexerQuery $query) // Process the required tokens. foreach ($query->included as $token) { - if ($token->required && (!isset($token->derived) || $token->derived == false)) { + if ($token->required && (!isset($token->derived) || !$token->derived)) { $parts[] = '' . Text::sprintf('COM_FINDER_QUERY_TOKEN_REQUIRED', $token->term) . ''; } } // Process the optional tokens. foreach ($query->included as $token) { - if (!$token->required && (!isset($token->derived) || $token->derived == false)) { + if (!$token->required && (!isset($token->derived) || !$token->derived)) { $parts[] = '' . Text::sprintf('COM_FINDER_QUERY_TOKEN_OPTIONAL', $token->term) . ''; } } diff --git a/administrator/components/com_guidedtours/src/Helper/GuidedtoursHelper.php b/administrator/components/com_guidedtours/src/Helper/GuidedtoursHelper.php index 004b94dac33..41a950c34fb 100644 --- a/administrator/components/com_guidedtours/src/Helper/GuidedtoursHelper.php +++ b/administrator/components/com_guidedtours/src/Helper/GuidedtoursHelper.php @@ -24,7 +24,7 @@ class GuidedtoursHelper { /** - * Load the translation files for an Guided Tour + * Load the translation files for a Guided Tour * * @param string $uid Guided Tour Unique Identifier * @param boolean $steps Should tour steps language file be loaded diff --git a/administrator/components/com_guidedtours/src/View/Step/HtmlView.php b/administrator/components/com_guidedtours/src/View/Step/HtmlView.php index a25ee5be05f..f8893688b99 100644 --- a/administrator/components/com_guidedtours/src/View/Step/HtmlView.php +++ b/administrator/components/com_guidedtours/src/View/Step/HtmlView.php @@ -23,7 +23,7 @@ // phpcs:enable PSR1.Files.SideEffects /** - * View to edit an Step + * View to edit a Step * * @since 4.3.0 */ diff --git a/administrator/components/com_guidedtours/src/View/Tour/HtmlView.php b/administrator/components/com_guidedtours/src/View/Tour/HtmlView.php index a67a3e88bf0..064f7df2a36 100644 --- a/administrator/components/com_guidedtours/src/View/Tour/HtmlView.php +++ b/administrator/components/com_guidedtours/src/View/Tour/HtmlView.php @@ -23,7 +23,7 @@ // phpcs:enable PSR1.Files.SideEffects /** - * View to edit an tour. + * View to edit a tour. * * @since 4.3.0 */ diff --git a/administrator/components/com_installer/src/Model/InstallModel.php b/administrator/components/com_installer/src/Model/InstallModel.php index 94784353b08..42163fe5c43 100644 --- a/administrator/components/com_installer/src/Model/InstallModel.php +++ b/administrator/components/com_installer/src/Model/InstallModel.php @@ -329,7 +329,7 @@ protected function _getPackageFromUpload() // Move uploaded file. try { - File::upload($tmp_src, $tmp_dest, false, true); + File::upload($tmp_src, $tmp_dest); } catch (FilesystemException) { Factory::getApplication()->enqueueMessage(Text::_('COM_INSTALLER_MSG_INSTALL_WARNINSTALLUPLOADERROR'), 'error'); diff --git a/administrator/components/com_installer/src/Model/UpdateModel.php b/administrator/components/com_installer/src/Model/UpdateModel.php index 46fb9900603..208bb14d18b 100644 --- a/administrator/components/com_installer/src/Model/UpdateModel.php +++ b/administrator/components/com_installer/src/Model/UpdateModel.php @@ -533,13 +533,6 @@ public function getForm($data = [], $loadData = true) Form::addFieldPath(JPATH_COMPONENT . '/models/fields'); $form = Form::getInstance('com_installer.update', 'update', ['load_data' => $loadData]); - // Check for an error. - if ($form == false) { - $this->setError($form->getMessage()); - - return false; - } - // Check the session for previously entered form data. $data = $this->loadFormData(); diff --git a/administrator/components/com_joomlaupdate/config.xml b/administrator/components/com_joomlaupdate/config.xml index feb77e4a5ab..4d8bcbfcdc7 100644 --- a/administrator/components/com_joomlaupdate/config.xml +++ b/administrator/components/com_joomlaupdate/config.xml @@ -72,4 +72,60 @@ +
+ + + + + + + + + + + +
diff --git a/administrator/components/com_joomlaupdate/postinstall/autoupdate.php b/administrator/components/com_joomlaupdate/postinstall/autoupdate.php new file mode 100644 index 00000000000..f44aa08ef90 --- /dev/null +++ b/administrator/components/com_joomlaupdate/postinstall/autoupdate.php @@ -0,0 +1,63 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\Table\Extension; +use Joomla\Component\Joomlaupdate\Administrator\Enum\AutoupdateRegisterState; +use Joomla\Component\Joomlaupdate\Administrator\Enum\AutoupdateState; +use Joomla\Database\DatabaseInterface; +use Joomla\Registry\Registry; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Post-installation message about the new Automated Update: condition check. + * + * Returns true it is disabled. + * + * @return bool + * @since __DEPLOY_VERSION__ + */ +function com_joomlaupdate_postinstall_autoupdate_condition(): bool +{ + return AutoupdateState::tryFrom(ComponentHelper::getParams('com_joomlaupdate')->get('autoupdate', '0')) === AutoupdateState::Disabled; +} + +/** + * Post-installation message about the new Automated Update: action. + * + * Enables the Automated Update. + * + * @return void + * @since __DEPLOY_VERSION__ + */ +function com_joomlaupdate_postinstall_autoupdate_action(): void +{ + $db = Factory::getContainer()->get(DatabaseInterface::class); + + // Get extension row + $extension = new Extension($db); + $extensionId = $extension->find(['element' => 'com_joomlaupdate']); + $extension->load($extensionId); + + // Set new update registration state + $params = new Registry($extension->params); + $params->set('autoupdate', AutoupdateState::Enabled); + $params->set('autoupdate_status', AutoupdateRegisterState::Subscribe); + + $extension->params = $params->toString(); + + if (!$extension->store()) { + throw new \RuntimeException($extension->getError()); + } +} diff --git a/administrator/components/com_joomlaupdate/src/Controller/UpdateController.php b/administrator/components/com_joomlaupdate/src/Controller/UpdateController.php index a53cf2e5f40..73dd6f51eb0 100644 --- a/administrator/components/com_joomlaupdate/src/Controller/UpdateController.php +++ b/administrator/components/com_joomlaupdate/src/Controller/UpdateController.php @@ -10,6 +10,7 @@ namespace Joomla\Component\Joomlaupdate\Administrator\Controller; +use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Language\Text; @@ -17,6 +18,8 @@ use Joomla\CMS\MVC\Controller\BaseController; use Joomla\CMS\Response\JsonResponse; use Joomla\CMS\Session\Session; +use Joomla\CMS\Updater\Updater; +use Joomla\Component\Joomlaupdate\Administrator\Enum\AutoupdateRegisterState; use Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel; // phpcs:disable PSR1.Files.SideEffects @@ -712,4 +715,60 @@ public function ajax() $this->app->close(); } + + /** + * Fetch and report health status of the automated updates in \JSON format, for AJAX requests + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function healthstatus() + { + if (!Session::checkToken('get')) { + $this->app->setHeader('status', 403, true); + $this->app->sendHeaders(); + echo Text::_('JINVALID_TOKEN_NOTICE'); + $this->app->close(); + } + + $params = ComponentHelper::getParams('com_joomlaupdate'); + + // Edge case: the current state requires the registration, i.e. because it's a new installation + $registrationState = AutoupdateRegisterState::tryFrom($params->get('autoupdate_status', 0)); + + if ( + $this->app->getIdentity()->authorise('core.admin', 'com_joomlaupdate') + && $registrationState === AutoupdateRegisterState::Subscribe + ) { + /** @var UpdateModel $model */ + $model = $this->getModel('Update'); + $result = $model->changeAutoUpdateRegistration($registrationState); + + $result = [ + 'active' => true, + 'healthy' => $result, + ]; + + echo json_encode($result); + + $this->app->close(); + } + + // Default case: connection already configured, check update source and date + $lastCheck = date_create_from_format('Y-m-d H:i:s', $params->get('update_last_check', '')); + + $result = [ + 'active' => ( + (int) $params->get('autoupdate') + && $params->get('updatesource', 'default') === 'default' + && (int) $params->get('minimum_stability', Updater::STABILITY_STABLE) === Updater::STABILITY_STABLE + ), + 'healthy' => $lastCheck !== false && $lastCheck->diff(new \DateTime())->days < 4, + ]; + + echo json_encode($result); + + $this->app->close(); + } } diff --git a/administrator/components/com_joomlaupdate/src/Enum/AutoupdateRegisterState.php b/administrator/components/com_joomlaupdate/src/Enum/AutoupdateRegisterState.php new file mode 100644 index 00000000000..ba649720383 --- /dev/null +++ b/administrator/components/com_joomlaupdate/src/Enum/AutoupdateRegisterState.php @@ -0,0 +1,26 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Component\Joomlaupdate\Administrator\Enum; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Autoupdate State Enum + */ +enum AutoupdateRegisterState: int +{ + case Unsubscribe = -1; + case Unsubscribed = 0; + case Subscribe = 1; + case Subscribed = 2; +} diff --git a/administrator/components/com_joomlaupdate/src/Enum/AutoupdateState.php b/administrator/components/com_joomlaupdate/src/Enum/AutoupdateState.php new file mode 100644 index 00000000000..32abd7bbbb5 --- /dev/null +++ b/administrator/components/com_joomlaupdate/src/Enum/AutoupdateState.php @@ -0,0 +1,24 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Component\Joomlaupdate\Administrator\Enum; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Autoupdate State Enum + */ +enum AutoupdateState: int +{ + case Disabled = 0; + case Enabled = 1; +} diff --git a/administrator/components/com_joomlaupdate/src/Model/NotificationModel.php b/administrator/components/com_joomlaupdate/src/Model/NotificationModel.php new file mode 100644 index 00000000000..aa9371b49d0 --- /dev/null +++ b/administrator/components/com_joomlaupdate/src/Model/NotificationModel.php @@ -0,0 +1,189 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Component\Joomlaupdate\Administrator\Model; + +use Joomla\CMS\Access\Access; +use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\Mail\MailHelper; +use Joomla\CMS\Mail\MailTemplate; +use Joomla\CMS\MVC\Model\BaseDatabaseModel; +use Joomla\CMS\Table\Asset; +use Joomla\CMS\Uri\Uri; +use Joomla\CMS\Version; +use Joomla\Database\ParameterType; +use Joomla\Registry\Registry; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Joomla! Notification Model + * + * @internal + * @since __DEPLOY_VERSION__ + */ +final class NotificationModel extends BaseDatabaseModel +{ + /** + * Sends the update notification to the specifically configured emails and superusers + * + * @param string $type The type of notification to send. This is the last key for the mail template + * @param string $oldVersion The old version from before the update + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function sendNotification($type, $oldVersion): void + { + $params = ComponentHelper::getParams('com_joomlaupdate'); + + // Load the parameters. + $specificEmail = $params->get('automated_updates_email'); + + // Let's find out the email addresses to notify + $superUsers = []; + + if (!empty($specificEmail)) { + $superUsers = $this->getSuperUsers($specificEmail); + } + + if (empty($superUsers)) { + $superUsers = $this->getSuperUsers(); + } + + if (empty($superUsers)) { + throw new \RuntimeException(); + } + + $app = Factory::getApplication(); + $jLanguage = $app->getLanguage(); + $sitename = $app->get('sitename'); + $newVersion = (new Version())->getShortVersion(); + + $substitutions = [ + 'oldversion' => $oldVersion, + 'newversion' => $newVersion, + 'sitename' => $sitename, + 'url' => Uri::root(), + ]; + + // Send the emails to the Super Users + foreach ($superUsers as $superUser) { + $params = new Registry($superUser->params); + $jLanguage->load('com_joomlaupdate', JPATH_ADMINISTRATOR, 'en-GB', true, true); + $jLanguage->load('com_joomlaupdate', JPATH_ADMINISTRATOR, $params->get('admin_language', null), true, true); + + $mailer = new MailTemplate('com_joomlaupdate.update.' . $type, $jLanguage->getTag()); + $mailer->addRecipient($superUser->email); + $mailer->addTemplateData($substitutions); + $mailer->send(); + } + } + + /** + * Returns the Super Users email information. If you provide a comma separated $email list + * we will check that these emails do belong to Super Users and that they have not blocked + * system emails. + * + * @param null|string $email A list of Super Users to email + * + * @return array The list of Super User emails + * + * @since __DEPLOY_VERSION__ + */ + private function getSuperUsers($email = null): array + { + $db = $this->getDatabase(); + $emails = []; + + // Convert the email list to an array + if (!empty($email)) { + $temp = explode(',', $email); + + foreach ($temp as $entry) { + if (!MailHelper::isEmailAddress(trim($entry))) { + continue; + } + + $emails[] = trim($entry); + } + + $emails = array_unique($emails); + } + + // Get a list of groups which have Super User privileges + $ret = []; + + try { + $rootId = (new Asset($db))->getRootId(); + $rules = Access::getAssetRules($rootId)->getData(); + $rawGroups = $rules['core.admin']->getData(); + $groups = []; + + if (empty($rawGroups)) { + return $ret; + } + + foreach ($rawGroups as $g => $enabled) { + if ($enabled) { + $groups[] = $g; + } + } + + if (empty($groups)) { + return $ret; + } + } catch (\Exception $exc) { + return $ret; + } + + // Get the user IDs of users belonging to the SA groups + try { + $query = $db->getQuery(true) + ->select($db->quoteName('user_id')) + ->from($db->quoteName('#__user_usergroup_map')) + ->whereIn($db->quoteName('group_id'), $groups); + + $db->setQuery($query); + $userIDs = $db->loadColumn(0); + + if (empty($userIDs)) { + return $ret; + } + } catch (\Exception $exc) { + return $ret; + } + + // Get the user information for the Super Administrator users + try { + $query = $db->getQuery(true) + ->select($db->quoteName(['id', 'username', 'email', 'params'])) + ->from($db->quoteName('#__users')) + ->whereIn($db->quoteName('id'), $userIDs) + ->where($db->quoteName('block') . ' = 0') + ->where($db->quoteName('sendEmail') . ' = 1'); + + if (!empty($emails)) { + $lowerCaseEmails = array_map('strtolower', $emails); + $query->whereIn('LOWER(' . $db->quoteName('email') . ')', $lowerCaseEmails, ParameterType::STRING); + } + + $ret = $db->setQuery($query)->loadObjectList(); + } catch (\Exception) { + return $ret; + } + + return $ret; + } +} diff --git a/administrator/components/com_joomlaupdate/src/Model/UpdateModel.php b/administrator/components/com_joomlaupdate/src/Model/UpdateModel.php index c375fb03764..337195d7639 100644 --- a/administrator/components/com_joomlaupdate/src/Model/UpdateModel.php +++ b/administrator/components/com_joomlaupdate/src/Model/UpdateModel.php @@ -25,11 +25,15 @@ use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\Plugin\PluginHelper; +use Joomla\CMS\Table\Extension; +use Joomla\CMS\Table\Table; use Joomla\CMS\Table\Tuf as TufMetadata; use Joomla\CMS\Updater\Update; use Joomla\CMS\Updater\Updater; +use Joomla\CMS\Uri\Uri; use Joomla\CMS\User\UserHelper; use Joomla\CMS\Version; +use Joomla\Component\Joomlaupdate\Administrator\Enum\AutoupdateRegisterState; use Joomla\Database\ParameterType; use Joomla\Filesystem\Exception\FilesystemException; use Joomla\Filesystem\File; @@ -47,6 +51,8 @@ */ class UpdateModel extends BaseDatabaseModel { + private const AUTOUPDATE_URL = 'https://autoupdate.joomla.org/api/v1'; + /** * @var array $updateInformation null * Holds the update information evaluated in getUpdateInformation. @@ -455,6 +461,206 @@ public function download() return $response; } + /** + * Update the datetime for the last health check run + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function updateLastHealthCheck() + { + $extension = new Extension($this->getDatabase()); + + $extensionId = $extension->find(['element' => 'com_joomlaupdate']); + + $extension->load($extensionId); + + $params = new Registry($extension->params); + $params->set('update_last_check', Factory::getDate()->toSql()); + + $extension->params = (string) $params; + + $extension->store(); + } + + /** + * Get the latest version for the automated update + * + * @return string|null + * + * @since __DEPLOY_VERSION__ + */ + public function getAutoUpdateVersion(): ?string + { + $this->refreshUpdates(true); + + $updateInfo = $this->getUpdateInformation(); + + return $updateInfo['latest'] ?? null; + } + + /** + * Check if the hard conditions for an update are met + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + public function getAutoUpdateRequirementsState(): bool + { + foreach ($this->getPhpOptions() as $option) { + if ($option->state !== true) { + return false; + } + } + + return true; + } + + /** + * Download file and request password/filesize information + * + * @param string $targetVersion + * + * @return array + * + * @since __DEPLOY_VERSION__ + */ + public function prepareAutoUpdate(string $targetVersion): array + { + $fileInformation = $this->download(); + + if ($fileInformation['version'] !== $targetVersion) { + throw new \Exception(Text::_('COM_JOOMLAUPDATE_VIEW_UPDATE_VERSION_WRONG'), 410); + } + + if ($fileInformation['check'] === false) { + throw new \Exception(Text::_('COM_JOOMLAUPDATE_VIEW_UPDATE_CHECKSUM_WRONG'), 410); + } + + if (!$this->createUpdateFile($fileInformation['basename'])) { + throw new \Exception('Could not write update file', 410); + } + + $app = Factory::getApplication(); + + return [ + 'password' => $app->getUserState('com_joomlaupdate.password'), + 'filesize' => $app->getUserState('com_joomlaupdate.filesize'), + ]; + } + + /** + * Change the registration state of a site in the update service + * + * @return bool + * + * @since __DEPLOY_VERSION__ + */ + public function changeAutoUpdateRegistration(AutoupdateRegisterState $targetState) + { + // Purge cache - this makes sure that the changed status will already be available if the health check is performed + $this->cleanCache('_system'); + + // Prepare connection + $http = HttpFactory::getHttp(); + + $url = self::AUTOUPDATE_URL; + $url .= ($targetState === AutoupdateRegisterState::Subscribe) ? '/register' : '/delete'; + + $requestData = [ + 'url' => Uri::root(), + 'key' => $this->getAutoUpdateToken(), + ]; + + // JHttp transport throws an exception when there's no response. + try { + $response = $http->post($url, json_encode($requestData), [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ], 20); + } catch (\RuntimeException $e) { + Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); + + return false; + } + + // Decode response + $result = json_decode((string)$response->getBody(), true); + + // Handle non-success response + if ($response->getStatusCode() !== 200) { + Factory::getApplication()->enqueueMessage( + Text::sprintf( + 'COM_JOOMLAUPDATE_AUTOUPDATE_REGISTER_ERROR', + $result['message'], + $result['status'] + ), + 'error' + ); + + return false; + } + + // Get extension row + $extension = new Extension($this->getDatabase()); + $extensionId = $extension->find(['element' => 'com_joomlaupdate']); + $extension->load($extensionId); + + // Set new update registration state + $params = new Registry($extension->params); + $params->set( + 'autoupdate_status', + ($targetState === AutoupdateRegisterState::Subscribe) + ? AutoupdateRegisterState::Subscribed->value + : AutoupdateRegisterState::Unsubscribed->value + ); + + $extension->params = $params->toString(); + + if (!$extension->store()) { + throw new \RuntimeException($extension->getError()); + } + + return true; + } + + /** + * Get the current autoupdate token, set one if none is set + * + * @return string + * + * @since __DEPLOY_VERSION__ + */ + protected function getAutoUpdateToken(): string + { + // Get extension row + $extension = new Extension($this->getDatabase()); + $extensionId = $extension->find(['element' => 'com_joomlaupdate']); + $extension->load($extensionId); + + $params = new Registry($extension->params); + + if ($params->get('update_token')) { + return $params->get('update_token'); + } + + // Set new token + $params->set( + 'update_token', + UserHelper::genRandomPassword(40) + ); + + $extension->params = $params->toString(); + + if (!$extension->store()) { + throw new \RuntimeException($extension->getError()); + } + + return $params->get('update_token'); + } + /** * Return the result of the checksum of a package with the SHA256/SHA384/SHA512 tags in the update server manifest * @@ -994,7 +1200,7 @@ public function upload() // Move uploaded file. try { - File::upload($tmp_src, $tmp_dest, false); + File::upload($tmp_src, $tmp_dest); } catch (FilesystemException $exception) { throw new \RuntimeException(Text::_('COM_INSTALLER_MSG_INSTALL_WARNINSTALLUPLOADERROR'), 500, $exception); } diff --git a/administrator/components/com_media/resources/scripts/components/browser/browser.vue b/administrator/components/com_media/resources/scripts/components/browser/browser.vue index b882a71f5b9..b783f7bcb87 100644 --- a/administrator/components/com_media/resources/scripts/components/browser/browser.vue +++ b/administrator/components/com_media/resources/scripts/components/browser/browser.vue @@ -30,7 +30,7 @@
element['menu_type']; if (!$menuType) { diff --git a/administrator/components/com_menus/src/Model/ItemModel.php b/administrator/components/com_menus/src/Model/ItemModel.php index 3b4d4ba88d0..2d46b9df704 100644 --- a/administrator/components/com_menus/src/Model/ItemModel.php +++ b/administrator/components/com_menus/src/Model/ItemModel.php @@ -1146,7 +1146,7 @@ protected function preprocessForm(Form $form, $data, $group = 'content') // If an XML file was found in the component, load it first. // We need to qualify the full path to avoid collisions with component file names. - if ($form->loadFile($formFile, true, '/metadata') == false) { + if (!$form->loadFile($formFile, true, '/metadata')) { throw new \Exception(Text::_('JERROR_LOADFILE_FAILED')); } diff --git a/administrator/components/com_redirect/src/Model/LinkModel.php b/administrator/components/com_redirect/src/Model/LinkModel.php index c982c0bf1c4..69af0758524 100644 --- a/administrator/components/com_redirect/src/Model/LinkModel.php +++ b/administrator/components/com_redirect/src/Model/LinkModel.php @@ -72,7 +72,7 @@ public function getForm($data = [], $loadData = true) } // Modify the form based on access controls. - if ($this->canEditState((object) $data) != true) { + if (!$this->canEditState((object)$data)) { // Disable fields for display. $form->setFieldAttribute('published', 'disabled', 'true'); @@ -83,7 +83,7 @@ public function getForm($data = [], $loadData = true) // If in advanced mode then we make sure the new URL field is not compulsory and the header // field compulsory in case people select non-3xx redirects - if (ComponentHelper::getParams('com_redirect')->get('mode', 0) == true) { + if (ComponentHelper::getParams('com_redirect')->get('mode', 0)) { $form->setFieldAttribute('new_url', 'required', 'false'); $form->setFieldAttribute('header', 'required', 'true'); } diff --git a/administrator/components/com_redirect/src/Table/LinkTable.php b/administrator/components/com_redirect/src/Table/LinkTable.php index 926d352fea1..067f7ffad03 100644 --- a/administrator/components/com_redirect/src/Table/LinkTable.php +++ b/administrator/components/com_redirect/src/Table/LinkTable.php @@ -74,13 +74,13 @@ public function check() } // Check for valid name if not in advanced mode. - if (empty($this->new_url) && ComponentHelper::getParams('com_redirect')->get('mode', 0) == false) { + if (empty($this->new_url) && !ComponentHelper::getParams('com_redirect')->get('mode', 0)) { $this->setError(Text::_('COM_REDIRECT_ERROR_DESTINATION_URL_REQUIRED')); return false; } - if (empty($this->new_url) && ComponentHelper::getParams('com_redirect')->get('mode', 0) == true) { + if (empty($this->new_url) && ComponentHelper::getParams('com_redirect')->get('mode', 0)) { // Else if an empty URL and in redirect mode only throw the same error if the code is a 3xx status code if ($this->header < 400 && $this->header >= 300) { $this->setError(Text::_('COM_REDIRECT_ERROR_DESTINATION_URL_REQUIRED')); diff --git a/administrator/components/com_scheduler/src/Task/TaskOption.php b/administrator/components/com_scheduler/src/Task/TaskOption.php index 2185aa4edfd..1612a4799f8 100644 --- a/administrator/components/com_scheduler/src/Task/TaskOption.php +++ b/administrator/components/com_scheduler/src/Task/TaskOption.php @@ -83,7 +83,7 @@ public function __construct(string $type, string $langConstPrefix) * * @return string The type title. * - * @since __DEPLOY_VERSION__ + * @since 5.3.0 */ public function getTitle(): string { diff --git a/administrator/components/com_tags/src/Model/TagsModel.php b/administrator/components/com_tags/src/Model/TagsModel.php index b80b86fc8cd..2dde7f9b3a5 100644 --- a/administrator/components/com_tags/src/Model/TagsModel.php +++ b/administrator/components/com_tags/src/Model/TagsModel.php @@ -275,7 +275,7 @@ public function getItems() { $items = parent::getItems(); - if ($items != false) { + if ($items) { $extension = $this->getState('filter.extension', ''); $this->countItems($items, $extension); diff --git a/administrator/components/com_templates/src/Controller/TemplateController.php b/administrator/components/com_templates/src/Controller/TemplateController.php index 6a45658344b..619aaa6b65f 100644 --- a/administrator/components/com_templates/src/Controller/TemplateController.php +++ b/administrator/components/com_templates/src/Controller/TemplateController.php @@ -630,7 +630,7 @@ public function deleteFolder() } elseif ($model->deleteFolder($location)) { $this->setMessage(Text::_('COM_TEMPLATES_FOLDER_DELETE_SUCCESS')); - if (stristr(base64_decode($file), $location) != false) { + if (stristr(base64_decode($file), $location)) { $file = base64_encode('home'); } diff --git a/administrator/components/com_templates/src/Helper/TemplateHelper.php b/administrator/components/com_templates/src/Helper/TemplateHelper.php index 9f30dcf6c85..2b1297e2c11 100644 --- a/administrator/components/com_templates/src/Helper/TemplateHelper.php +++ b/administrator/components/com_templates/src/Helper/TemplateHelper.php @@ -98,7 +98,7 @@ public static function canUpload($file, $err = '') $allowable = array_merge($imageTypes, $sourceTypes, $fontTypes, $archiveTypes); - if ($format == '' || $format == false || (!\in_array($format, $allowable))) { + if ($format === '' || !\in_array($format, $allowable)) { $app = Factory::getApplication(); $app->enqueueMessage(Text::_('COM_TEMPLATES_ERROR_WARNFILETYPE'), 'error'); diff --git a/administrator/components/com_templates/src/Model/TemplateModel.php b/administrator/components/com_templates/src/Model/TemplateModel.php index 4f0a1724995..23eb1bbe12a 100644 --- a/administrator/components/com_templates/src/Model/TemplateModel.php +++ b/administrator/components/com_templates/src/Model/TemplateModel.php @@ -1174,9 +1174,9 @@ public function createOverride($override) $name = end($explodeArray); $client = ApplicationHelper::getClientInfo($template->client_id); - if (stristr($name, 'mod_') != false) { + if (stristr($name, 'mod_') !== false) { $htmlPath = Path::clean($client->path . '/templates/' . $template->element . '/html/' . $name); - } elseif (stristr($override, 'com_') != false) { + } elseif (stristr($override, 'com_') !== false) { $size = \count($explodeArray); $url = Path::clean($explodeArray[$size - 3] . '/' . $explodeArray[$size - 1]); @@ -1202,9 +1202,9 @@ public function createOverride($override) return false; } - if (stristr($name, 'mod_') != false) { + if (stristr($name, 'mod_') !== false) { $return = $this->createTemplateOverride(Path::clean($override . '/tmpl'), $htmlPath); - } elseif (stristr($override, 'com_') != false && stristr($override, 'layouts') == false) { + } elseif (stristr($override, 'com_') !== false && stristr($override, 'layouts') === false) { $path = $override . '/tmpl'; // View can also be in the top level folder @@ -1703,7 +1703,7 @@ public function getFont() $fileName = end($explodeArray); $path = $this->getBasePath() . base64_decode($app->getInput()->get('file')); - if (stristr($client->path, 'administrator') == false) { + if (stristr($client->path, 'administrator') === false) { $folder = '/templates/'; } else { $folder = '/administrator/templates/'; diff --git a/administrator/components/com_templates/src/View/Templates/HtmlView.php b/administrator/components/com_templates/src/View/Templates/HtmlView.php index ffe907ba601..9e11c302777 100644 --- a/administrator/components/com_templates/src/View/Templates/HtmlView.php +++ b/administrator/components/com_templates/src/View/Templates/HtmlView.php @@ -142,7 +142,7 @@ protected function addToolbar() $toolbar = $this->getDocument()->getToolbar(); // Add a shortcut to the styles list view. - $toolbar->linkButton('', 'COM_TEMPLATES_MANAGER_STYLES_BUTTON') + $toolbar->linkButton('styles', 'COM_TEMPLATES_MANAGER_STYLES_BUTTON') ->url('index.php?option=com_templates&view=styles&client_id=' . $clientId) ->icon('icon-brush thememanager'); diff --git a/administrator/components/com_users/src/Model/BackupcodesModel.php b/administrator/components/com_users/src/Model/BackupcodesModel.php index cae3ab13319..33271b4f73c 100644 --- a/administrator/components/com_users/src/Model/BackupcodesModel.php +++ b/administrator/components/com_users/src/Model/BackupcodesModel.php @@ -252,7 +252,7 @@ public function isBackupCode($code, ?User $user = null): bool */ $otherResult = false; - $temp1 = ''; + $temp1 = []; for ($i = 0; $i < 10; $i++) { $temp1[$i] = random_int(0, 99999999); diff --git a/administrator/components/com_users/src/Model/CaptiveModel.php b/administrator/components/com_users/src/Model/CaptiveModel.php index 9cbe58c3679..358d2a15e0a 100644 --- a/administrator/components/com_users/src/Model/CaptiveModel.php +++ b/administrator/components/com_users/src/Model/CaptiveModel.php @@ -426,7 +426,7 @@ public function checkTryLimit(MfaTable $method) $maxTries = (int) $params->get('mfatrycount', 10); $blockHours = (int) $params->get('mfatrytime', 1); - $lastTryTime = strtotime($method->last_try) ?: 0; + $lastTryTime = $method->last_try !== null ? strtotime($method->last_try) : 0; $hoursSinceLastTry = (strtotime(Factory::getDate()->toSql()) - $lastTryTime) / 3600; if ($method->last_try !== null && $hoursSinceLastTry > $blockHours) { diff --git a/administrator/components/com_users/tmpl/notes/default.php b/administrator/components/com_users/tmpl/notes/default.php index fa64f4e5722..d1665b2b956 100644 --- a/administrator/components/com_users/tmpl/notes/default.php +++ b/administrator/components/com_users/tmpl/notes/default.php @@ -97,7 +97,7 @@
- escape($item->user_name); ?> + user_name) ? $this->escape($item->user_name) : '[ ' . Text::_('JNONE') . ' ]'; ?> review_time !== null) : ?> diff --git a/administrator/language/en-GB/com_content.ini b/administrator/language/en-GB/com_content.ini index 0db92cc6123..67eee3b41e1 100644 --- a/administrator/language/en-GB/com_content.ini +++ b/administrator/language/en-GB/com_content.ini @@ -103,7 +103,7 @@ COM_CONTENT_FIELDS_TYPE_MODAL_ARTICLE="Article" COM_CONTENT_FIELDSET_PUBLISHING="Publishing" COM_CONTENT_FIELDSET_RULES="Permissions" COM_CONTENT_FIELDSET_URLS_AND_IMAGES="Images and Links" -COM_CONTENT_FILTER_AUTHORS_BY_ME="Created by me" +COM_CONTENT_FILTER_AUTHORS_BY_ME="- Created by me -" COM_CONTENT_FILTER_FEATURED_NO="Unfeatured Articles" COM_CONTENT_FILTER_FEATURED_YES="Featured Articles" COM_CONTENT_FILTER_SEARCH_DESC="Search in title, alias and note. Prefix with ID: or AUTHOR: or CONTENT: to search for an article ID, article author or search in article content. Prefix with CHECKEDOUT: to search for content checked out by a specified user." diff --git a/administrator/language/en-GB/com_joomlaupdate.ini b/administrator/language/en-GB/com_joomlaupdate.ini index 66daf5ab8fd..0d0e09e0377 100644 --- a/administrator/language/en-GB/com_joomlaupdate.ini +++ b/administrator/language/en-GB/com_joomlaupdate.ini @@ -3,18 +3,33 @@ ; License GNU General Public License version 2 or later; see LICENSE.txt ; Note : All ini files need to be saved as UTF-8 +COM_JOOMLAUPDATE_AUTOUPDATE_REGISTER_ERROR="Error while registering to automated update service: %s (%d)." +COM_JOOMLAUPDATE_AUTOUPDATE_REGISTER_SUCCESS="Registered to automated update service." +COM_JOOMLAUPDATE_AUTOUPDATE_UNREGISTER_ERROR="Error while unregistering from automated update service: %s (%d)." +COM_JOOMLAUPDATE_AUTOUPDATE_UNREGISTER_SUCCESS="Unregistered from automated update service." COM_JOOMLAUPDATE_CAPTIVE_HEADLINE="Confirm your credentials" COM_JOOMLAUPDATE_CHECKED_UPDATES="Checked for updates." +COM_JOOMLAUPDATE_CONFIG_AUTOMATED_UPDATES_DISABLED_LABEL="Automated updates are only supported for the \"default\" channel and \"Minimum Stability\" \"Stable\"." +COM_JOOMLAUPDATE_CONFIG_AUTOMATED_UPDATES_LABEL="Automated Updates" +COM_JOOMLAUPDATE_CONFIG_AUTOUPDATE_DESC="Automatically update Joomla to the latest version when it is available." +COM_JOOMLAUPDATE_CONFIG_AUTOUPDATE_LABEL="Automated Update" +COM_JOOMLAUPDATE_CONFIG_AUTOUPDATE_UPDATE_EMAIL_DESCRIPTION="A comma separated list of the email addresses which will receive the update notification emails. The addresses in the list MUST belong to existing users of your site who have the Super User privilege. If none of the listed emails belongs to Super Users, or if it's left blank, all Super Users of this site will receive the update notification email." +COM_JOOMLAUPDATE_CONFIG_AUTOUPDATE_UPDATE_EMAIL_LABEL="Super User Emails" COM_JOOMLAUPDATE_CONFIG_BACKUPCHECK_DESC="Shows the checkbox to confirm you have taken a backup and you're ready to update in the final step before the update is actually applied." COM_JOOMLAUPDATE_CONFIG_BACKUPCHECK_LABEL="Confirm Backup Checkbox" COM_JOOMLAUPDATE_CONFIG_CUSTOMURL_LABEL="Custom URL" COM_JOOMLAUPDATE_CONFIG_SOURCES_DESC="Configure where Joomla gets its update information from." COM_JOOMLAUPDATE_CONFIG_SOURCES_LABEL="Update Source" COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_CUSTOM="Custom URL" +COM_JOOMLAUPDATE_CONFIG_UPDATE_LAST_CHECK_DESC="The last time the automated updates server could reach this site. If this date is too old, something is wrong with the automated updates and you should re-initiate the process by re-saving the configuration." +COM_JOOMLAUPDATE_CONFIG_UPDATE_LAST_CHECK_LABEL="Last Checked" + COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_CUSTOM_ERROR="The custom URL field is empty." COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_DEFAULT="Default" COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_LABEL="Update Channel" COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_NEXT="Joomla Next" +COM_JOOMLAUPDATE_CONFIG_UPDATE_TOKEN_DESC="This is the update token that this site uses to authenticate with the Joomla Automated Updates server. It is used to ensure that only your site can update itself." +COM_JOOMLAUPDATE_CONFIG_UPDATE_TOKEN_LABEL="Update Token" ; Deprecated, will be removed with 6.0 COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_TESTING="Testing" COM_JOOMLAUPDATE_CONFIG_VERSIONCHECK_DESC="Shows the checkbox in the pre–update check if any of the extensions installed on your site is potentially incompatible with the version of Joomla you are upgrading to. Note: the checkbox is displayed when upgrading to a new Joomla version family (minor or major version)." @@ -51,6 +66,9 @@ COM_JOOMLAUPDATE_NODOWNLOAD_EMPTYSTATE_REASON_DATABASE="Your %1$s version \"%2$s COM_JOOMLAUPDATE_NODOWNLOAD_EMPTYSTATE_REASON_PHP="Your PHP version \"%1$s\" is lower than \"%2$s\"." COM_JOOMLAUPDATE_NODOWNLOAD_EMPTYSTATE_TITLE="This site can't be updated to Joomla %1$s" COM_JOOMLAUPDATE_OVERVIEW="Joomla Update" +COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_ACTION="Activate Automated Updates" +COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_DESCRIPTION="With Automated Updates you can keep your Joomla site secure and up-to-date—automatically, reliably, effortlessly. Activate Automated Updates now by clicking on the \"Activate Automated Updates\" button." +COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_TITLE="Automated Joomla Updates" COM_JOOMLAUPDATE_PREUPDATE_CHECK_CAPTION="Server settings to check before update." COM_JOOMLAUPDATE_PREUPDATE_CHECK_COMPLETED_YOU_HAVE_DANGEROUS_PLUGINS="There are plugins installed and enabled that could interfere with the Joomla update and result in a failed update that leaves the site inaccessible.

You are strongly advised to update, disable or uninstall these plugins before upgrading." COM_JOOMLAUPDATE_PREUPDATE_CHECK_EXTENSION_AUTHOR_URL="Extension Author URL" @@ -75,6 +93,8 @@ COM_JOOMLAUPDATE_UPDATE_CHECK="Update Check" COM_JOOMLAUPDATE_UPDATE_CONFIRM_BACKUP="I'm aware that a backup before any update is highly recommended." COM_JOOMLAUPDATE_UPDATE_EMPTYSTATE_TITLE="Update your site to \"Joomla! %s\"" COM_JOOMLAUPDATE_UPDATE_EMPTYSTATE_BUTTON_ADD="Start update" +COM_JOOMLAUPDATE_UPDATE_FAILED_MAIL_BODY="This email IS NOT sent by the Joomla! project. It is sent automatically by your own site,\n{SITENAME} - {URL} \n\n================================================================================\nUPDATE INFORMATION\n================================================================================\nYour site could not be updated from {OLDVERSION} to {NEWVERSION}.\nPlease check the logfile '/administrator/logs/joomla_update.php' for further debugging information.\nThis email is sent to you by your site.\nThe Joomla! project will never contact you directly.\n================================================================================\nWHY AM I RECEIVING THIS EMAIL?\n================================================================================\nThis email has been automatically sent by Joomla!, the software which powers your site.\nYou have enabled automatic updates in your site and trying to install such an update triggers this message.\nIf you do not understand what Joomla! is and what you need to do please do not contact the Joomla! project.\nThey are NOT sending you this email and they cannot help you. Instead, please contact the person who built or manages your site.\n================================================================================\nWHO SENT ME THIS EMAIL?\n================================================================================\nThis email is sent to you by your own site, {SITENAME}" +COM_JOOMLAUPDATE_UPDATE_FAILED_MAIL_SUBJECT="Joomla! update failed {SITENAME} – {URL}" COM_JOOMLAUPDATE_UPDATE_LOG_CLEANUP="Cleaning up after installation." COM_JOOMLAUPDATE_UPDATE_LOG_COMPLETE="Update to version %s is complete." COM_JOOMLAUPDATE_UPDATE_LOG_DELETE_FILES="Deleting removed files and folders." @@ -85,7 +105,13 @@ COM_JOOMLAUPDATE_UPDATE_LOG_START="Update started by user %2$s (%1$s). Old versi COM_JOOMLAUPDATE_UPDATE_LOG_UNINSTALL_EXTENSIONS="Uninstalling extensions" COM_JOOMLAUPDATE_UPDATE_LOG_UPLOAD="Uploading update file" COM_JOOMLAUPDATE_UPDATE_LOG_URL="Downloading update file from %s." +COM_JOOMLAUPDATE_MAIL_UPDATE_FAILED_DESC="Send to the site administrators if an Automated Joomla Update failed." +COM_JOOMLAUPDATE_MAIL_UPDATE_FAILED_TITLE="Automated Joomla Update Failed" +COM_JOOMLAUPDATE_MAIL_UPDATE_SUCCESS_DESC="Send to the site administrators if an Automated Joomla Update succeeded." +COM_JOOMLAUPDATE_MAIL_UPDATE_SUCCESS_TITLE="Automated Joomla Update Succeeded" COM_JOOMLAUPDATE_UPDATE_LOGGING_TEST_FAIL="Logging does not work. Make sure the log folder is writable, and try again. The logging test failed with message: %s" +COM_JOOMLAUPDATE_UPDATE_SUCCESS_MAIL_BODY="This email IS NOT sent by the Joomla! project. It is sent automatically by your own site,\n{SITENAME} - {URL} \n\n================================================================================\nUPDATE INFORMATION\n================================================================================\nYour site has been successfully automatically updated from {OLDVERSION} to {NEWVERSION}.\nPlease check your site for any unexpected behavior or malfunctions.\nThis email is sent to you by your site.\nThe Joomla! project will never contact you directly.\n================================================================================\nWHY AM I RECEIVING THIS EMAIL?\n================================================================================\nThis email has been automatically sent by Joomla!, the software which powers your site.\nYou have enabled automatic updates in your site and the installation of such an update triggers this message.\nIf you do not understand what Joomla! is and what you need to do please do not contact the Joomla! project.\nThey are NOT sending you this email and they cannot help you. Instead, please contact the person who built or manages your site.\n================================================================================\nWHO SENT ME THIS EMAIL?\n================================================================================\nThis email is sent to you by your own site, {SITENAME}" +COM_JOOMLAUPDATE_UPDATE_SUCCESS_MAIL_SUBJECT="Joomla! updated successfully at {SITENAME} – {URL}" COM_JOOMLAUPDATE_UPDATING_HEAD="Update in progress" COM_JOOMLAUPDATE_UPDATING_INPROGRESS="Please wait; Joomla is being updated. This may take a while." COM_JOOMLAUPDATE_UPDATING_FAIL="The update has failed." @@ -98,7 +124,7 @@ COM_JOOMLAUPDATE_VIEW_COMPLETE_WITH_ERROR_MESSAGE="The update completed with err COM_JOOMLAUPDATE_VIEW_DEFAULT_ACTUAL="Actual" COM_JOOMLAUPDATE_VIEW_DEFAULT_COMPATIBILITY_CHECK="Joomla! %s Compatibility Check" COM_JOOMLAUPDATE_VIEW_DEFAULT_COMPATIBLE_UPDATE_WARNING="Extensions marked with X.X.X have an extension update available for the current version of Joomla which is not marked as compatible with the updated version of Joomla. You should contact the extension developer for more information." -COM_JOOMLAUPDATE_VIEW_DEFAULT_DATABASE_STRUCTURE_NOTICE="Go to 'System - Maintenance - Database' and use the 'Update Structure' button." +COM_JOOMLAUPDATE_VIEW_DEFAULT_DATABASE_STRUCTURE_NOTICE="Go to 'System - Maintenance - Database' and use the 'Update Structure' button." COM_JOOMLAUPDATE_VIEW_DEFAULT_DATABASE_STRUCTURE_TITLE="Database Table Structure Up to Date" COM_JOOMLAUPDATE_VIEW_DEFAULT_DB_NOT_SUPPORTED="Your database type is not supported" COM_JOOMLAUPDATE_VIEW_DEFAULT_DB_NOT_SUPPORTED_DESC="An update to Joomla %1$s was found, but your current database type is not supported by the new version.
For further details take a look at the minimum requirements for Joomla %1$s." diff --git a/administrator/language/en-GB/guidedtours.joomla_whatsnew_5_4.ini b/administrator/language/en-GB/guidedtours.joomla_whatsnew_5_4.ini new file mode 100644 index 00000000000..cb87677f6fd --- /dev/null +++ b/administrator/language/en-GB/guidedtours.joomla_whatsnew_5_4.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; (C) 2025 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_DESCRIPTION="

Welcome to Joomla 5.4!

\"Display

Automated Updates are here!

Joomla! 5.4 now supports automated updates for better security and performance.

Enable your automated updates now

Stay safe and secure!

" +COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_TITLE="What’s New in Joomla 5.4!" diff --git a/administrator/language/en-GB/guidedtours.joomla_whatsnew_5_4_steps.ini b/administrator/language/en-GB/guidedtours.joomla_whatsnew_5_4_steps.ini new file mode 100644 index 00000000000..6ee83abbbb2 --- /dev/null +++ b/administrator/language/en-GB/guidedtours.joomla_whatsnew_5_4_steps.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; (C) 2025 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_STEP_0_DESCRIPTION="

Joomla 5.4 Full Release Notes

View the full release notes on joomla.org


Help and Information

Many resources to help you can be found here together with additional links to more resources, documentation, support and how to become involved in Joomla.

" +COM_GUIDEDTOURS_TOUR_WHATSNEW_5_4_STEP_0_TITLE="More Information and Help" diff --git a/administrator/language/en-GB/install.xml b/administrator/language/en-GB/install.xml index 10fd41a4451..6227c27fa0b 100644 --- a/administrator/language/en-GB/install.xml +++ b/administrator/language/en-GB/install.xml @@ -2,8 +2,8 @@ English (en-GB) en-GB - 5.3.0 - 2025-03 + 5.4.0 + 2025-02 Joomla! Project admin@joomla.org www.joomla.org diff --git a/administrator/language/en-GB/joomla.ini b/administrator/language/en-GB/joomla.ini index 3433db03d72..fbf8ee00951 100644 --- a/administrator/language/en-GB/joomla.ini +++ b/administrator/language/en-GB/joomla.ini @@ -103,6 +103,7 @@ JNEXT="Next" JNEXT_TITLE="Next article: %s" JNO="No" JNONE="None" +JNONE_FILTER="- None -" JOFF="Off" JOK="OK" JON="On" diff --git a/administrator/language/en-GB/langmetadata.xml b/administrator/language/en-GB/langmetadata.xml index 2debf87d936..3978a43b7dd 100644 --- a/administrator/language/en-GB/langmetadata.xml +++ b/administrator/language/en-GB/langmetadata.xml @@ -1,8 +1,8 @@ English (en-GB) - 5.3.0 - 2025-03 + 5.4.0 + 2025-02 Joomla! Project admin@joomla.org www.joomla.org diff --git a/administrator/language/en-GB/plg_actionlog_joomla.ini b/administrator/language/en-GB/plg_actionlog_joomla.ini index 69d026dbc27..dc2d7401786 100644 --- a/administrator/language/en-GB/plg_actionlog_joomla.ini +++ b/administrator/language/en-GB/plg_actionlog_joomla.ini @@ -40,6 +40,7 @@ PLG_ACTIONLOG_JOOMLA_TYPE_TEMPLATE="template" PLG_ACTIONLOG_JOOMLA_TYPE_USER="user" PLG_ACTIONLOG_JOOMLA_TYPE_USER_GROUP="user group" PLG_ACTIONLOG_JOOMLA_TYPE_USER_NOTE="user note" +; User actions PLG_ACTIONLOG_JOOMLA_USER_BLOCK="User {username} blocked user {title}" PLG_ACTIONLOG_JOOMLA_USER_CACHE="User {username} deleted cache group {group}" PLG_ACTIONLOG_JOOMLA_USER_CHECKIN="User {username} performed a check in to table {table}" @@ -55,7 +56,9 @@ PLG_ACTIONLOG_JOOMLA_USER_RESET_COMPLETE="User {username PLG_ACTIONLOG_JOOMLA_USER_RESET_REQUEST="User {username} requested a password reset for their account" PLG_ACTIONLOG_JOOMLA_USER_UNBLOCK="User {username} unblocked user {title}" PLG_ACTIONLOG_JOOMLA_USER_UPDATE="User {username} updated Joomla from {oldversion} to {version}" -; Component +; Automatic updates +PLG_ACTIONLOG_JOOMLA_SYSTEM_UPDATE="An automated process updated Joomla from {oldversion} to {version}" +; Config PLG_ACTIONLOG_JOOMLA_APPLICATION_CONFIG_UPDATED="User {username} changed settings of the application configuration" PLG_ACTIONLOG_JOOMLA_COMPONENT_CONFIG_UPDATED="User {username} changed settings of the component {extension_name}" ; Extensions diff --git a/administrator/language/en-GB/plg_behaviour_compat6.ini b/administrator/language/en-GB/plg_behaviour_compat6.ini new file mode 100644 index 00000000000..d550624dcc1 --- /dev/null +++ b/administrator/language/en-GB/plg_behaviour_compat6.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; (C) 2025 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +PLG_BEHAVIOUR_COMPAT6="Behaviour - Backward Compatibility 6" +PLG_COMPAT6_XML_DESCRIPTION="This plugin should be enabled before updating to Joomla 6." diff --git a/administrator/language/en-GB/plg_behaviour_compat6.sys.ini b/administrator/language/en-GB/plg_behaviour_compat6.sys.ini new file mode 100644 index 00000000000..d550624dcc1 --- /dev/null +++ b/administrator/language/en-GB/plg_behaviour_compat6.sys.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; (C) 2025 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +PLG_BEHAVIOUR_COMPAT6="Behaviour - Backward Compatibility 6" +PLG_COMPAT6_XML_DESCRIPTION="This plugin should be enabled before updating to Joomla 6." diff --git a/administrator/language/en-GB/plg_quickicon_autoupdate.ini b/administrator/language/en-GB/plg_quickicon_autoupdate.ini new file mode 100644 index 00000000000..16470178372 --- /dev/null +++ b/administrator/language/en-GB/plg_quickicon_autoupdate.ini @@ -0,0 +1,14 @@ +; Joomla! Project +; (C) 2025 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +PLG_QUICKICON_AUTOUPDATE="Quick Icon - Joomla! Automated Update Health Notification" +PLG_QUICKICON_AUTOUPDATE_CHECKING="Checking Automated Update …" +PLG_QUICKICON_AUTOUPDATE_DISABLED="Automated Updates are disabled." +PLG_QUICKICON_AUTOUPDATE_ERROR="Unknown Health status …" +PLG_QUICKICON_AUTOUPDATE_GROUP_DESC="The group of this plugin (this value is compared with the group value used in Quick Icons modules to inject icons)." +PLG_QUICKICON_AUTOUPDATE_GROUP_LABEL="Group" +PLG_QUICKICON_AUTOUPDATE_OK="Automated Updates are enabled." +PLG_QUICKICON_AUTOUPDATE_OUTDATED="Automated Updates connection broken." +PLG_QUICKICON_AUTOUPDATE_XML_DESCRIPTION="Checks the health status of automated updates and notifies you when you visit the Home Dashboard page." diff --git a/administrator/language/en-GB/plg_quickicon_autoupdate.sys.ini b/administrator/language/en-GB/plg_quickicon_autoupdate.sys.ini new file mode 100644 index 00000000000..6ae79925aac --- /dev/null +++ b/administrator/language/en-GB/plg_quickicon_autoupdate.sys.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; (C) 2025 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +PLG_QUICKICON_AUTOUPDATE="Quick Icon - Joomla! Automated Update Health Notification" +PLG_QUICKICON_AUTOUPDATE_XML_DESCRIPTION="Checks the health status of automated updates and notifies you when you visit the Home Dashboard page." diff --git a/administrator/language/en-GB/plg_system_jooa11y.ini b/administrator/language/en-GB/plg_system_jooa11y.ini index 84f6143e2c2..d4965027c6e 100644 --- a/administrator/language/en-GB/plg_system_jooa11y.ini +++ b/administrator/language/en-GB/plg_system_jooa11y.ini @@ -7,11 +7,11 @@ PLG_SYSTEM_JOOA11Y="System - Joomla Accessibility Checker" PLG_SYSTEM_JOOA11Y_FIELD_CHECK_ROOT="Target Area to Check" PLG_SYSTEM_JOOA11Y_FIELD_CHECK_ROOT_DESC="Input a single selector to target a specific region of your website. The default setting is the landmark main. Alternatives to landmarks are classes, elements or ARIA roles (e.g. #main-content, .main, [role='main']). Input body to check the entire page." PLG_SYSTEM_JOOA11Y_FIELD_CHECKS="Turn Off Checks" -PLG_SYSTEM_JOOA11Y_FIELD_CHECKS_DESC="Turn off specific checks by key name. Learn how to Turn Off Checks." +PLG_SYSTEM_JOOA11Y_FIELD_CHECKS_DESC="Turn off specific checks by key name. Learn how to Turn Off Checks." PLG_SYSTEM_JOOA11Y_FIELD_CONTAINER_IGNORE="Ignore Regions" PLG_SYSTEM_JOOA11Y_FIELD_CONTAINER_IGNORE_DESC="Ignore specific regions within the Content Container. Use commas to separate classes or elements (eg #ignore, .ignore)." PLG_SYSTEM_JOOA11Y_FIELD_EXTRA_PROPS="Extra Properties" -PLG_SYSTEM_JOOA11Y_FIELD_EXTRA_PROPS_DESC="Pass additional properties to customise. Learn how to add Extra Properties." +PLG_SYSTEM_JOOA11Y_FIELD_EXTRA_PROPS_DESC="Pass additional properties to customise. Learn how to add Extra Properties." PLG_SYSTEM_JOOA11Y_FIELD_READABILITY_ROOT="Readability Target Area" PLG_SYSTEM_JOOA11Y_FIELD_READABILITY_ROOT_DESC="Landmark on the page that will be checked for readability. The default setting is the landmark main." PLG_SYSTEM_JOOA11Y_FIELD_SHOW_ALWAYS="Show Always" diff --git a/administrator/language/en-GB/plg_webservices_joomlaupdate.ini b/administrator/language/en-GB/plg_webservices_joomlaupdate.ini new file mode 100644 index 00000000000..aace39ac4d9 --- /dev/null +++ b/administrator/language/en-GB/plg_webservices_joomlaupdate.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; (C) 2025 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +PLG_WEBSERVICES_JOOMLAUPDATE="Web Services - Joomlaupdate" +PLG_WEBSERVICES_JOOMLAUPDATE_XML_DESCRIPTION="Add Joomlaupdate routes to the API for your website." diff --git a/administrator/language/en-GB/plg_webservices_joomlaupdate.sys.ini b/administrator/language/en-GB/plg_webservices_joomlaupdate.sys.ini new file mode 100644 index 00000000000..aace39ac4d9 --- /dev/null +++ b/administrator/language/en-GB/plg_webservices_joomlaupdate.sys.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; (C) 2025 Open Source Matters, Inc. +; License GNU General Public License version 2 or later; see LICENSE.txt +; Note : All ini files need to be saved as UTF-8 + +PLG_WEBSERVICES_JOOMLAUPDATE="Web Services - Joomlaupdate" +PLG_WEBSERVICES_JOOMLAUPDATE_XML_DESCRIPTION="Add Joomlaupdate routes to the API for your website." diff --git a/administrator/manifests/files/joomla.xml b/administrator/manifests/files/joomla.xml index 1581bfb4457..a869beb5cc8 100644 --- a/administrator/manifests/files/joomla.xml +++ b/administrator/manifests/files/joomla.xml @@ -6,8 +6,8 @@ www.joomla.org (C) 2019 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt - 5.3.0-beta3-dev - 2025-03 + 5.4.0-alpha1-dev + 2025-02 FILES_JOOMLA_XML_DESCRIPTION administrator/components/com_admin/script.php diff --git a/administrator/manifests/packages/pkg_en-GB.xml b/administrator/manifests/packages/pkg_en-GB.xml index f994e1cb4e7..c952c9cddf7 100644 --- a/administrator/manifests/packages/pkg_en-GB.xml +++ b/administrator/manifests/packages/pkg_en-GB.xml @@ -2,8 +2,8 @@ English (en-GB) Language Pack en-GB - 5.3.0.1 - 2025-03 + 5.4.0.1 + 2025-02 Joomla! Project admin@joomla.org www.joomla.org diff --git a/administrator/modules/mod_feed/tmpl/default.php b/administrator/modules/mod_feed/tmpl/default.php index 4a547a3c2f2..0ed70c2c0d4 100644 --- a/administrator/modules/mod_feed/tmpl/default.php +++ b/administrator/modules/mod_feed/tmpl/default.php @@ -43,7 +43,7 @@ $direction = ' redirect-rtl'; } - if ($feed != false) : + if ($feed) : ?>
send(); // If we are supposed to copy the sender, do so. - if ($emailCopyToSender == true && !empty($data['contact_email_copy'])) { + if ($emailCopyToSender && !empty($data['contact_email_copy'])) { $mailer = new MailTemplate('com_contact.mail.copy', $app->getLanguage()->getTag()); $mailer->addRecipient($templateData['email']); $mailer->setReplyTo($templateData['email'], $templateData['name']); diff --git a/api/components/com_joomlaupdate/src/Controller/BaseController.php b/api/components/com_joomlaupdate/src/Controller/BaseController.php new file mode 100644 index 00000000000..23306f410cb --- /dev/null +++ b/api/components/com_joomlaupdate/src/Controller/BaseController.php @@ -0,0 +1,46 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Component\Joomlaupdate\Api\Controller; + +use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\MVC\Controller\ApiController; +use Tobscure\JsonApi\Exception\InvalidParameterException; + +abstract class BaseController extends ApiController +{ + /** + * Validate if the update token is correct and auto update is enabled + * + * @return void + * + * @since __DEPLOY_VERSION__ + * + * @throws \Exception + */ + protected function validateUpdateToken(): void + { + $config = ComponentHelper::getParams('com_joomlaupdate'); + + if ($config->get('updatesource') !== 'default' || (int) $config->get('minimum_stability') !== 4 || !$config->get('autoupdate')) { + throw new \RuntimeException('Auto update is disabled', 404); + } + + $token = $this->input->server->get('HTTP_X_JUPDATE_TOKEN', '', 'STRING'); + + if (empty($token)) { + throw new InvalidParameterException('Token is required', 403, null, 'token'); + } + + if ($config->get('update_token') !== $token) { + throw new InvalidParameterException('Invalid token', 403, null, 'token'); + } + } +} diff --git a/api/components/com_joomlaupdate/src/Controller/HealthcheckController.php b/api/components/com_joomlaupdate/src/Controller/HealthcheckController.php new file mode 100644 index 00000000000..dd6323aeef6 --- /dev/null +++ b/api/components/com_joomlaupdate/src/Controller/HealthcheckController.php @@ -0,0 +1,162 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Component\Joomlaupdate\Api\Controller; + +use Joomla\CMS\Language\Text; +use Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * The healthcheck controller + * + * @since __DEPLOY_VERSION__ + */ +class HealthcheckController extends BaseController +{ + /** + * The content type of the item. + * + * @var string + * @since __DEPLOY_VERSION__ + */ + protected $contentType = 'healthcheck'; + + /** + * The default view for the display method. + * + * @var string + * @since __DEPLOY_VERSION__ + */ + protected $default_view = 'healthcheck'; + + /** + * Show the healthcheck information + * + * @return HealthcheckController + * + * @since __DEPLOY_VERSION__ + */ + public function show() + { + $this->validateUpdateToken(); + + $viewType = $this->app->getDocument()->getType(); + $viewName = $this->input->get('view', $this->default_view); + $viewLayout = $this->input->get('layout', 'default', 'string'); + + try { + /** @var JsonApiView $view */ + $view = $this->getView( + $viewName, + $viewType, + '', + ['base_path' => $this->basePath, 'layout' => $viewLayout, 'contentType' => $this->contentType] + ); + } catch (\Exception $e) { + throw new \RuntimeException($e->getMessage()); + } + + /** @var UpdateModel $model */ + $model = $this->getModel('Update', 'Administrator', ['ignore_request' => true, 'state' => $this->modelState]); + + if (!$model) { + throw new \RuntimeException(Text::_('JLIB_APPLICATION_ERROR_MODEL_CREATE')); + } + + // Push the model into the view (as default) + $view->setModel($model, true); + + $view->setDocument($this->app->getDocument()); + + $view->healthCheck(); + + // If we reach this point, healthcheck was successful, so update the date in the config + $model->updateLastHealthCheck(); + + return $this; + } + + /** + * Basic display of an item view. We don't allow this + * + * @param integer $id The primary key to display. Leave empty if you want to retrieve data from the request + * + * @return static A \JControllerLegacy object to support chaining. + * + * @since __DEPLOY_VERSION__ + */ + public function displayItem($id = null) + { + throw new \RuntimeException('Not implemented', 501); + } + + /** + * List view amended to add filtering of data. We don't allow this + * + * @return static A BaseController object to support chaining. + * + * @since __DEPLOY_VERSION__ + */ + public function displayList() + { + throw new \RuntimeException('Not implemented', 501); + } + + /** + * Removes an item. + * + * @param integer $id The primary key to delete item. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function delete($id = null) + { + throw new \RuntimeException('Not implemented', 501); + } + + /** + * Method to check if you can edit an existing record. + * + * We don't allow editing from API (yet?) + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key; default is id. + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + protected function allowEdit($data = [], $key = 'id') + { + return false; + } + + /** + * Method to check if you can add a new record. + * + * We don't allow adding from API + * + * @param array $data An array of input data. + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + protected function allowAdd($data = []) + { + return false; + } +} diff --git a/api/components/com_joomlaupdate/src/Controller/NotificationController.php b/api/components/com_joomlaupdate/src/Controller/NotificationController.php new file mode 100644 index 00000000000..cf4a556cb12 --- /dev/null +++ b/api/components/com_joomlaupdate/src/Controller/NotificationController.php @@ -0,0 +1,191 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Component\Joomlaupdate\Api\Controller; + +use Joomla\CMS\Language\Text; +use Joomla\Component\Joomlaupdate\Api\View\Updates\JsonapiView; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * The updates controller + * + * @since __DEPLOY_VERSION__ + */ +class NotificationController extends BaseController +{ + /** + * The content type of the item. + * + * @var string + * @since __DEPLOY_VERSION__ + */ + protected $contentType = 'notification'; + + /** + * The default view for the display method. + * + * @var string + * @since __DEPLOY_VERSION__ + */ + protected $default_view = 'notification'; + + /** + * Send fail notification to configured super users + * + * @return NotificationController For chaining + */ + public function failed() + { + $this->validateUpdateToken(); + + $fromVersion = $this->input->json->getString('fromVersion', null); + + $view = $this->prepareView(); + + $view->notification('failed', $fromVersion); + + return $this; + } + + /** + * Send success notification to configured super users + * + * @return NotificationController + * + * @since __DEPLOY_VERSION__ + */ + public function success() + { + $this->validateUpdateToken(); + + $fromVersion = $this->input->json->getString('fromVersion', null); + + $view = $this->prepareView(); + + $view->notification('success', $fromVersion); + + return $this; + } + + /** + * Generic method to prepare the view + * + * @return JsonapiView The prepared view + */ + protected function prepareView() + { + $viewType = $this->app->getDocument()->getType(); + $viewName = $this->input->get('view', $this->default_view); + $viewLayout = $this->input->get('layout', 'default', 'string'); + + try { + /** @var JsonApiView $view */ + $view = $this->getView( + $viewName, + $viewType, + '', + ['base_path' => $this->basePath, 'layout' => $viewLayout, 'contentType' => $this->contentType] + ); + } catch (\Exception $e) { + throw new \RuntimeException($e->getMessage()); + } + + /** @var UpdateModel $model */ + $model = $this->getModel('Notification', 'Administrator', ['ignore_request' => true, 'state' => $this->modelState]); + + if (!$model) { + throw new \RuntimeException(Text::_('JLIB_APPLICATION_ERROR_MODEL_CREATE')); + } + + // Push the model into the view (as default) + $view->setModel($model, true); + + $view->setDocument($this->app->getDocument()); + + return $view; + } + + /** + * Basic display of an item view. We don't allow this + * + * @param integer $id The primary key to display. Leave empty if you want to retrieve data from the request + * + * @return static A \JControllerLegacy object to support chaining. + * + * @since __DEPLOY_VERSION__ + */ + public function displayItem($id = null) + { + throw new \RuntimeException('Not implemented', 501); + } + + /** + * List view amended to add filtering of data. We don't allow this + * + * @return static A BaseController object to support chaining. + * + * @since __DEPLOY_VERSION__ + */ + public function displayList() + { + throw new \RuntimeException('Not implemented', 501); + } + + /** + * Removes an item. + * + * @param integer $id The primary key to delete item. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function delete($id = null) + { + throw new \RuntimeException('Not implemented', 501); + } + + /** + * Method to check if you can edit an existing record. + * + * We don't allow editing from API (yet?) + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key; default is id. + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + protected function allowEdit($data = [], $key = 'id') + { + return false; + } + + /** + * Method to check if you can add a new record. + * + * We don't allow adding from API + * + * @param array $data An array of input data. + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + protected function allowAdd($data = []) + { + return false; + } +} diff --git a/api/components/com_joomlaupdate/src/Controller/UpdatesController.php b/api/components/com_joomlaupdate/src/Controller/UpdatesController.php new file mode 100644 index 00000000000..2d0a287662b --- /dev/null +++ b/api/components/com_joomlaupdate/src/Controller/UpdatesController.php @@ -0,0 +1,218 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Component\Joomlaupdate\Api\Controller; + +use Joomla\CMS\Language\Text; +use Joomla\Component\Joomlaupdate\Api\View\Updates\JsonapiView; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * The updates controller + * + * @since __DEPLOY_VERSION__ + */ +class UpdatesController extends BaseController +{ + /** + * The content type of the item. + * + * @var string + * @since __DEPLOY_VERSION__ + */ + protected $contentType = 'updates'; + + /** + * The default view for the display method. + * + * @var string + * @since __DEPLOY_VERSION__ + */ + protected $default_view = 'updates'; + + /** + * Get the latest update version for the auto updater + * + * @return UpdateController For chaining + */ + public function getUpdate() + { + $this->validateUpdateToken(); + + $view = $this->prepareView(); + + $view->getUpdate(); + + return $this; + } + + /** + * Prepare the update and output the update information + * + * @return UpdatesController + * + * @since __DEPLOY_VERSION__ + */ + public function prepareUpdate() + { + $this->validateUpdateToken(); + + /** + * @var UpdateModel $model + */ + $model = $this->getModel('Update'); + + $latestVersion = $model->getAutoUpdateVersion(); + + $targetVersion = $this->input->json->getString('targetVersion'); + + if (!$latestVersion || $latestVersion !== $targetVersion) { + throw new \Exception(Text::_('COM_JOOMLAUPDATE_VIEW_UPDATE_VERSION_WRONG'), 410); + } + + $view = $this->prepareView(); + + $view->prepareUpdate($targetVersion); + + return $this; + } + + /** + * Finalize the update + * + * @return UpdateController For chaining + */ + public function finalizeUpdate() + { + $this->validateUpdateToken(); + + $fromVersion = $this->input->json->getString('fromVersion', null); + + $view = $this->prepareView(); + + $view->finalizeUpdate($fromVersion); + + return $this; + } + + /** + * Generic method to prepare the view + * + * @return JsonapiView The prepared view + */ + protected function prepareView() + { + $viewType = $this->app->getDocument()->getType(); + $viewName = $this->input->get('view', $this->default_view); + $viewLayout = $this->input->get('layout', 'default', 'string'); + + try { + /** @var JsonApiView $view */ + $view = $this->getView( + $viewName, + $viewType, + '', + ['base_path' => $this->basePath, 'layout' => $viewLayout, 'contentType' => $this->contentType] + ); + } catch (\Exception $e) { + throw new \RuntimeException($e->getMessage()); + } + + /** @var UpdateModel $model */ + $model = $this->getModel('Update', 'Administrator', ['ignore_request' => true, 'state' => $this->modelState]); + + if (!$model) { + throw new \RuntimeException(Text::_('JLIB_APPLICATION_ERROR_MODEL_CREATE')); + } + + // Push the model into the view (as default) + $view->setModel($model, true); + + $view->setDocument($this->app->getDocument()); + + return $view; + } + + /** + * Basic display of an item view. We don't allow this + * + * @param integer $id The primary key to display. Leave empty if you want to retrieve data from the request + * + * @return static A \JControllerLegacy object to support chaining. + * + * @since __DEPLOY_VERSION__ + */ + public function displayItem($id = null) + { + throw new \RuntimeException('Not implemented', 501); + } + + /** + * List view amended to add filtering of data. We don't allow this + * + * @return static A BaseController object to support chaining. + * + * @since __DEPLOY_VERSION__ + */ + public function displayList() + { + throw new \RuntimeException('Not implemented', 501); + } + + /** + * Removes an item. + * + * @param integer $id The primary key to delete item. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function delete($id = null) + { + throw new \RuntimeException('Not implemented', 501); + } + + /** + * Method to check if you can edit an existing record. + * + * We don't allow editing from API (yet?) + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key; default is id. + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + protected function allowEdit($data = [], $key = 'id') + { + return false; + } + + /** + * Method to check if you can add a new record. + * + * We don't allow adding from API + * + * @param array $data An array of input data. + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + protected function allowAdd($data = []) + { + return false; + } +} diff --git a/api/components/com_joomlaupdate/src/View/Healthcheck/JsonapiView.php b/api/components/com_joomlaupdate/src/View/Healthcheck/JsonapiView.php new file mode 100644 index 00000000000..4c67e5da135 --- /dev/null +++ b/api/components/com_joomlaupdate/src/View/Healthcheck/JsonapiView.php @@ -0,0 +1,84 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Component\Joomlaupdate\Api\View\Healthcheck; + +use Joomla\CMS\Factory; +use Joomla\CMS\MVC\View\JsonApiView as BaseApiView; +use Joomla\CMS\Uri\Uri; +use Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel; +use Joomla\Database\DatabaseInterface; +use Tobscure\JsonApi\Resource; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * The healthcheck view + * + * @since __DEPLOY_VERSION__ + */ +class JsonapiView extends BaseApiView +{ + /** + * Generates the health check output + * + * @return string The rendered data + * + * @since __DEPLOY_VERSION__ + */ + public function healthCheck() + { + $data = $this->getStatsData(); + + $data['id'] = 'healthcheck'; + + $element = (new Resource((object) $data, $this->serializer)) + ->fields(['healthcheck' => ['php_version', 'db_type', 'db_version', 'cms_version', 'server_os', 'update_requirement_state']]); + + $this->getDocument()->setData($element); + $this->getDocument()->addLink('self', Uri::current()); + + return $this->getDocument()->render(); + } + + /** + * Get the data that will be sent to the update server. + * + * @return array + * + * @since __DEPLOY_VERSION__ + */ + protected function getStatsData() + { + $db = Factory::getContainer()->get(DatabaseInterface::class); + + /** @var UpdateModel $updateModel */ + $updateModel = Factory::getApplication()->bootComponent('com_joomlaupdate') + ->getMVCFactory()->createModel('Update', 'Administrator', ['ignore_request' => true]); + + $data = [ + 'php_version' => PHP_VERSION, + 'db_type' => $db->name, + 'db_version' => $db->getVersion(), + 'cms_version' => JVERSION, + 'server_os' => php_uname('s') . ' ' . php_uname('r'), + 'update_requirement_state' => $updateModel->getAutoUpdateRequirementsState(), + ]; + + // Check if we have a MariaDB version string and extract the proper version from it + if (preg_match('/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', $data['db_version'], $versionParts)) { + $data['db_version'] = $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch']; + } + + return $data; + } +} diff --git a/api/components/com_joomlaupdate/src/View/Notification/JsonapiView.php b/api/components/com_joomlaupdate/src/View/Notification/JsonapiView.php new file mode 100644 index 00000000000..29120d3ce45 --- /dev/null +++ b/api/components/com_joomlaupdate/src/View/Notification/JsonapiView.php @@ -0,0 +1,59 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Component\Joomlaupdate\Api\View\Notification; + +use Joomla\CMS\MVC\View\JsonApiView as BaseApiView; +use Joomla\CMS\Uri\Uri; +use Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel; +use Tobscure\JsonApi\Resource; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * The notification view + * + * @since __DEPLOY_VERSION__ + */ +class JsonapiView extends BaseApiView +{ + /** + * Sends the update result notification + * + * @return string The rendered data + * + * @since __DEPLOY_VERSION__ + */ + public function notification($type, $oldVersion) + { + /** + * @var UpdateModel $model + */ + $model = $this->getModel(); + $success = true; + + try { + // Perform the finalization action + $model->sendNotification($type, $oldVersion); + } catch (\Throwable $e) { + $success = false; + } + + $element = (new Resource((object) ['success' => $success, 'id' => $type], $this->serializer)) + ->fields(['notification' => ['success']]); + + $this->getDocument()->setData($element); + $this->getDocument()->addLink('self', Uri::current()); + + return $this->getDocument()->render(); + } +} diff --git a/api/components/com_joomlaupdate/src/View/Updates/JsonapiView.php b/api/components/com_joomlaupdate/src/View/Updates/JsonapiView.php new file mode 100644 index 00000000000..782ce99b420 --- /dev/null +++ b/api/components/com_joomlaupdate/src/View/Updates/JsonapiView.php @@ -0,0 +1,150 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Component\Joomlaupdate\Api\View\Updates; + +use Joomla\CMS\Factory; +use Joomla\CMS\MVC\View\JsonApiView as BaseApiView; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\CMS\Uri\Uri; +use Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel; +use Tobscure\JsonApi\Resource; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * The updates view + * + * @since __DEPLOY_VERSION__ + */ +class JsonapiView extends BaseApiView +{ + /** + * Generates the update output + * + * @return string The rendered data + * + * @since __DEPLOY_VERSION__ + */ + public function getUpdate() + { + /** + * @var UpdateModel $model + */ + $model = $this->getModel(); + + $latestVersion = $model->getAutoUpdateVersion(); + + if (!$latestVersion || version_compare(JVERSION, $latestVersion) >= 0) { + $latestVersion = null; + } + + // Check pre-update states, unset update if not matched + $preUpdateState = $model->getAutoUpdateRequirementsState(); + + if (!$preUpdateState) { + $latestVersion = null; + } + + // Prepare response + $element = (new Resource((object) ['availableUpdate' => $latestVersion, 'id' => 'getUpdate'], $this->serializer)) + ->fields(['updates' => ['availableUpdate']]); + + $this->getDocument()->setData($element); + $this->getDocument()->addLink('self', Uri::current()); + + return $this->getDocument()->render(); + } + + /** + * Prepares the update by setting up the update.php and returns password and file size + * + * @param string $targetVersion The target version to prepare + * + * @return string The rendered data + * + * @since __DEPLOY_VERSION__ + */ + public function prepareUpdate(string $targetVersion): string + { + /** + * @var UpdateModel $model + */ + $model = $this->getModel(); + + $fileinformation = $model->prepareAutoUpdate($targetVersion); + + $fileinformation['id'] = 'prepareUpdate'; + + $element = (new Resource((object) $fileinformation, $this->serializer)) + ->fields(['updates' => ['password', 'filesize']]); + + $this->getDocument()->setData($element); + $this->getDocument()->addLink('self', Uri::current()); + + return $this->getDocument()->render(); + } + + /** + * Run the finalize update steps + * + * @param string $fromVersion The from version + * + * @return string The rendered data + * + * @since __DEPLOY_VERSION__ + */ + public function finalizeUpdate($fromVersion) + { + /** + * @var UpdateModel $model + */ + $model = $this->getModel(); + + // Write old version to state for usage in model + Factory::getApplication()->setUserState('com_joomlaupdate.oldversion', $fromVersion); + + try { + // Perform the finalization action + $model->finaliseUpgrade(); + } catch (\Throwable $e) { + $model->collectError('finaliseUpgrade', $e); + } + + try { + // Load actionlog plugins. + PluginHelper::importPlugin('actionlog'); + + // Perform the cleanup action + $model->cleanUp(); + } catch (\Throwable $e) { + $model->collectError('cleanUp', $e); + } + + // Reset source + $model->resetUpdateSource(); + + $success = true; + + if ($model->getErrors()) { + $success = false; + } + + $element = (new Resource((object) ['success' => $success, 'id' => 'finalizeUpdate'], $this->serializer)) + ->fields(['updates' => ['success']]); + + $this->getDocument()->setData($element); + $this->getDocument()->addLink('self', Uri::current()); + + return $this->getDocument()->render(); + } +} diff --git a/api/components/com_privacy/src/View/Requests/JsonapiView.php b/api/components/com_privacy/src/View/Requests/JsonapiView.php index d4b8caf6839..4b2987b48b8 100644 --- a/api/components/com_privacy/src/View/Requests/JsonapiView.php +++ b/api/components/com_privacy/src/View/Requests/JsonapiView.php @@ -58,7 +58,7 @@ public function export() $exportData = $model->collectDataForExportRequest(); - if ($exportData == false) { + if (!$exportData) { throw new RouteNotFoundException('Item does not exist'); } diff --git a/api/language/en-GB/install.xml b/api/language/en-GB/install.xml index ff678973631..64b80fd60f2 100644 --- a/api/language/en-GB/install.xml +++ b/api/language/en-GB/install.xml @@ -2,8 +2,8 @@ English (en-GB) en-GB - 5.3.0 - 2025-03 + 5.4.0 + 2025-02 Joomla! Project admin@joomla.org www.joomla.org diff --git a/api/language/en-GB/joomla.ini b/api/language/en-GB/joomla.ini index 7e5cf62b974..56a8e97153e 100644 --- a/api/language/en-GB/joomla.ini +++ b/api/language/en-GB/joomla.ini @@ -101,6 +101,7 @@ JNEXT="Next" JNEXT_TITLE="Next article: %s" JNO="No" JNONE="None" +JNONE_FILTER="- None -" JOFF="Off" JOK="OK" JON="On" diff --git a/api/language/en-GB/langmetadata.xml b/api/language/en-GB/langmetadata.xml index 3721884bdc0..3fd210742d6 100644 --- a/api/language/en-GB/langmetadata.xml +++ b/api/language/en-GB/langmetadata.xml @@ -1,8 +1,8 @@ English (en-GB) - 5.3.0 - 2025-03 + 5.4.0 + 2025-02 Joomla! Project admin@joomla.org www.joomla.org diff --git a/build.xml b/build.xml index 84471a34c9d..494ad4fd697 100644 --- a/build.xml +++ b/build.xml @@ -85,7 +85,7 @@ - + diff --git a/build/build-modules-js/init/minify-vendor.mjs b/build/build-modules-js/init/minify-vendor.mjs index 32f16aac2ff..5d2dbd529e7 100644 --- a/build/build-modules-js/init/minify-vendor.mjs +++ b/build/build-modules-js/init/minify-vendor.mjs @@ -13,13 +13,12 @@ const folders = [ 'media/vendor/diff/js', 'media/vendor/es-module-shims/js', 'media/vendor/qrcode/js', - 'media/vendor/short-and-sweet/js', 'media/vendor/webcomponentsjs/js', ]; let allFiles = []; -const noMinified = ['accessibility.min.js', 'short-and-sweet.min.js']; +const noMinified = ['accessibility.min.js']; const alreadyMinified = [ 'media/vendor/webcomponentsjs/js/webcomponents-bundle.js', diff --git a/build/build-modules-js/init/patches.mjs b/build/build-modules-js/init/patches.mjs index 3a5bcc06f64..1a20efff1c5 100644 --- a/build/build-modules-js/init/patches.mjs +++ b/build/build-modules-js/init/patches.mjs @@ -15,7 +15,7 @@ export const patchPackages = async (options) => { // Joomla's hack to expose the chosen base classes so we can extend it ourselves // (it was better than the many hacks we had before. But I'm still ashamed of myself). - let dest = join(mediaVendorPath, 'chosen'); + const dest = join(mediaVendorPath, 'chosen'); const chosenPath = `${dest}/${options.settings.vendors['chosen-js'].js['chosen.jquery.js']}`; let ChosenJs = await readFile(chosenPath, { encoding: 'utf8' }); ChosenJs = ChosenJs.replace( @@ -26,17 +26,6 @@ export const patchPackages = async (options) => { ); await writeFile(chosenPath, ChosenJs, { encoding: 'utf8', mode: 0o644 }); - // Append initialising code to the end of the Short-and-Sweet javascript - dest = join(mediaVendorPath, 'short-and-sweet'); - const shortandsweetPath = `${dest}/${options.settings.vendors['short-and-sweet'].js['dist/short-and-sweet.min.js']}`; - let ShortandsweetJs = await readFile(shortandsweetPath, { encoding: 'utf8' }); - ShortandsweetJs = ShortandsweetJs.concat(` -shortAndSweet('textarea.charcount,input.charcount', {counterClassName: 'small text-muted'}); -/** Repeatable */ -document.addEventListener("joomla:updated", (event) => [].slice.call(event.target.querySelectorAll('textarea.charcount,input.charcount')).map((el) => shortAndSweet(el, {counterClassName: 'small text-muted'}))); -`); - await writeFile(shortandsweetPath, ShortandsweetJs, { encoding: 'utf8', mode: 0o644 }); - // Include the v5 shim for Font Awesome const faPath = join(mediaVendorPath, 'fontawesome-free/scss/fontawesome.scss'); const newScss = (await readFile(faPath, { encoding: 'utf8' })).concat(` diff --git a/build/build-modules-js/javascript/build-bootstrap-js.mjs b/build/build-modules-js/javascript/build-bootstrap-js.mjs index 02e81becf69..ffa9a0e5e63 100644 --- a/build/build-modules-js/javascript/build-bootstrap-js.mjs +++ b/build/build-modules-js/javascript/build-bootstrap-js.mjs @@ -3,7 +3,7 @@ import { } from 'node:fs/promises'; import { resolve } from 'node:path'; import { transform } from 'esbuild'; -import rimraf from 'rimraf'; +import { rimrafSync } from 'rimraf'; import { rollup } from 'rollup'; import { nodeResolve } from '@rollup/plugin-node-resolve'; import replace from '@rollup/plugin-replace'; @@ -65,6 +65,13 @@ const build = async () => { ], }), ], + }); + + await bundle.write({ + format: 'es', + sourcemap: false, + dir: outputFolder, + chunkFileNames: '[name].js', manualChunks: { alert: ['build/media_source/vendor/bootstrap/js/alert.es6.js'], button: ['build/media_source/vendor/bootstrap/js/button.es6.js'], @@ -86,19 +93,12 @@ const build = async () => { }, }); - await bundle.write({ - format: 'es', - sourcemap: false, - dir: outputFolder, - chunkFileNames: '[name].js', - }); - // closes the bundle await bundle.close(); }; export const bootstrapJs = async () => { - rimraf.sync(resolve(outputFolder)); + rimrafSync(resolve(outputFolder)); try { await build(resolve(inputFolder, 'index.es6.js')); diff --git a/build/build-modules-js/settings.json b/build/build-modules-js/settings.json index 9c0058ef2d1..2d781c7a2ef 100644 --- a/build/build-modules-js/settings.json +++ b/build/build-modules-js/settings.json @@ -704,16 +704,13 @@ "short-and-sweet": { "name": "short-and-sweet", "licenseFilename": "LICENSE", - "js": { - "dist/short-and-sweet.min.js": "js/short-and-sweet.min.js" - }, "provideAssets": [ { "name": "short-and-sweet", "type": "script", "uri": "short-and-sweet.min.js", "attributes": { - "defer": true + "type": "module" } } ] diff --git a/build/build-modules-js/stylesheets/handle-scss.mjs b/build/build-modules-js/stylesheets/handle-scss.mjs index cb44899fc12..bc6447b2854 100644 --- a/build/build-modules-js/stylesheets/handle-scss.mjs +++ b/build/build-modules-js/stylesheets/handle-scss.mjs @@ -4,28 +4,21 @@ import { dirname, sep } from 'node:path'; import rtlcss from 'rtlcss'; import { ensureDir } from 'fs-extra'; import { transform as transformCss, Features } from 'lightningcss'; -import * as Sass from 'sass-embedded'; +import { compileAsync } from 'sass-embedded'; + +const getOutputFile = (file) => file.replace(`${sep}scss${sep}`, `${sep}css${sep}`).replace('.scss', '.css').replace(`${sep}build${sep}media_source${sep}`, `${sep}media${sep}`); export const handleScssFile = async (file) => { - const cssFile = file - .replace(`${sep}scss${sep}`, `${sep}css${sep}`) - .replace(`${sep}build${sep}media_source${sep}`, `${sep}media${sep}`) - .replace('.scss', '.css'); + let contents; + const cssFile = getOutputFile(file); - let compiled; try { - compiled = Sass.compile(file); + const { css } = await compileAsync(file); + contents = css.toString(); } catch (error) { - // eslint-disable-next-line no-console - console.error(error.formatted); - process.exitCode = 1; + throw new Error(error.formatted); } - let contents = transformCss({ - code: Buffer.from(compiled.css.toString()), - minify: false, - }).code; - if (cssFile.endsWith('-rtl.css')) { contents = rtlcss.process(contents); } @@ -39,7 +32,7 @@ ${contents}`, { encoding: 'utf8', mode: 0o644 }, ); - const cssMin = transformCss({ + const { code: cssMin } = transformCss({ code: Buffer.from(contents), minify: true, exclude: Features.VendorPrefixes, @@ -47,7 +40,7 @@ ${contents}`, // Ensure the folder exists or create it await ensureDir(dirname(cssFile.replace('.css', '.min.css')), {}); - await writeFile(cssFile.replace('.css', '.min.css'), `@charset "UTF-8";${cssMin.code}`, { encoding: 'utf8', mode: 0o644 }); + await writeFile(cssFile.replace('.css', '.min.css'), `@charset "UTF-8";${cssMin}`, { encoding: 'utf8', mode: 0o644 }); // eslint-disable-next-line no-console console.log(`✅ SCSS File compiled: ${cssFile}`); diff --git a/build/build-modules-js/stylesheets/scss-transform.mjs b/build/build-modules-js/stylesheets/scss-transform.mjs deleted file mode 100644 index b0c37ae2a47..00000000000 --- a/build/build-modules-js/stylesheets/scss-transform.mjs +++ /dev/null @@ -1,54 +0,0 @@ -import Fs from 'node:fs/promises'; -import { dirname, sep } from 'node:path'; - -import FsExtra from 'fs-extra'; -import LightningCSS from 'lightningcss'; -import Sass from 'sass-embedded'; - -export const compile = async (file) => { - const cssFile = file - .replace(`${sep}scss${sep}`, `${sep}css${sep}`) - .replace('.scss', '.css') - .replace(`${sep}build${sep}media_source${sep}`, `${sep}media${sep}`); - - let compiled; - try { - compiled = Sass.compile(file); - } catch (error) { - // eslint-disable-next-line no-console - console.error(error.formatted); - process.exitCode = 1; - } - - // Auto prefixing - const { code } = LightningCSS.transform({ - code: Buffer.from(compiled.css.toString()), - minify: false, - }); - - // Ensure the folder exists or create it - await FsExtra.mkdirs(dirname(cssFile), {}); - await Fs.writeFile( - cssFile, - `@charset "UTF-8"; -${code}`, - { encoding: 'utf8', mode: 0o644 }, - ); - - const cssMin = LightningCSS.transform({ - code: Buffer.from(code), - minify: true, - exclude: LightningCSS.Features.VendorPrefixes, - }); - - // Ensure the folder exists or create it - FsExtra.mkdirs(dirname(cssFile.replace('.css', '.min.css')), {}); - await Fs.writeFile( - cssFile.replace('.css', '.min.css'), - `@charset "UTF-8";${cssMin.code}`, - { encoding: 'utf8', mode: 0o644 }, - ); - - // eslint-disable-next-line no-console - console.log(`✅ SCSS File compiled: ${cssFile}`); -}; diff --git a/build/build.php b/build/build.php index 84d485d938b..bf84fc6008d 100644 --- a/build/build.php +++ b/build/build.php @@ -122,6 +122,8 @@ function clean_checkout(string $dir) system('find libraries/vendor -name _config.yml | xargs rm -rf -'); system('find libraries/vendor -name .bowerrc | xargs rm -rf -'); system('find libraries/vendor -name bower.json | xargs rm -rf -'); + system('find libraries/vendor -name .drone.yml | xargs rm -rf -'); + system('find libraries/vendor -name .drone.jsonnet | xargs rm -rf -'); system('rm -rf libraries/vendor/bin'); // aldo26-matthias/idna-convert diff --git a/build/media_source/com_guidedtours/images/5_4/automated-updates.jpg b/build/media_source/com_guidedtours/images/5_4/automated-updates.jpg new file mode 100644 index 00000000000..8e6b91a5f01 Binary files /dev/null and b/build/media_source/com_guidedtours/images/5_4/automated-updates.jpg differ diff --git a/build/media_source/plg_installer_webinstaller/js/client.es6.js b/build/media_source/plg_installer_webinstaller/js/client.es6.js index 4f4b8e23cc9..d9ac1d58a72 100644 --- a/build/media_source/plg_installer_webinstaller/js/client.es6.js +++ b/build/media_source/plg_installer_webinstaller/js/client.es6.js @@ -413,7 +413,7 @@ customElements.whenDefined('joomla-tab').then(() => { link.click(); } - if (link.hasAttribute('aria-expanded') && link.getAttribute('aria-expanded') === 'true' && !instance) { + if (link.hasAttribute('aria-selected') && link.getAttribute('aria-selected') === 'true' && !instance) { instance = new WebInstaller(); instance.initialise(); } diff --git a/build/media_source/plg_quickicon_autoupdate/js/healthcheck.es6.js b/build/media_source/plg_quickicon_autoupdate/js/healthcheck.es6.js new file mode 100644 index 00000000000..273415fffe5 --- /dev/null +++ b/build/media_source/plg_quickicon_autoupdate/js/healthcheck.es6.js @@ -0,0 +1,55 @@ +/** + * @copyright (C) 2025 Open Source Matters, Inc. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +if (Joomla && Joomla.getOptions('js-auto-update')) { + const update = (type, text) => { + const link = document.getElementById('plg_quickicon_autoupdate'); + if (link) { + link.classList.add(type); + } + + link.querySelectorAll('span.j-links-link').forEach((span) => { + span.innerHTML = Joomla.sanitizeHtml(text); + }); + }; + + const checkHealthStatus = () => { + const options = Joomla.getOptions('js-auto-update'); + + /** + * DO NOT use fetch() for QuickIcon requests. They must be queued. + * + * @see https://github.com/joomla/joomla-cms/issues/38001 + */ + Joomla.enqueueRequest({ + url: options.ajaxUrl, + method: 'GET', + promise: true, + }).then((xhr) => { + const response = xhr.responseText; + const healthStatus = JSON.parse(response); + + if (healthStatus) { + // Not active + if (!healthStatus.active) { + update('warning', Joomla.Text._('PLG_QUICKICON_AUTOUPDATE_DISABLED')); + } else if (!healthStatus.healthy) { + update('danger', Joomla.Text._('PLG_QUICKICON_AUTOUPDATE_OUTDATED')); + } else { + update('success', Joomla.Text._('PLG_QUICKICON_AUTOUPDATE_OK')); + } + } else { + // An error occurred + update('danger', Joomla.Text._('PLG_QUICKICON_AUTOUPDATE_ERROR')); + } + }).catch(() => { + // An error occurred + update('danger', Joomla.Text._('PLG_QUICKICON_AUTOUPDATE_ERROR')); + }); + }; + + // Give some times to the layout and other scripts to settle their stuff + window.addEventListener('load', () => setTimeout(checkHealthStatus, 300)); +} diff --git a/build/media_source/system/js/fields/joomla-field-fancy-select.w-c.es6.js b/build/media_source/system/js/fields/joomla-field-fancy-select.w-c.es6.js index b8b4f08fef2..830ba1c1dfc 100644 --- a/build/media_source/system/js/fields/joomla-field-fancy-select.w-c.es6.js +++ b/build/media_source/system/js/fields/joomla-field-fancy-select.w-c.es6.js @@ -129,6 +129,7 @@ window.customElements.define('joomla-field-fancy-select', class extends HTMLElem searchFloor: this.minTermLength, searchResultLimit: parseInt(this.select.dataset.maxResults, 10) || 10, renderChoiceLimit: parseInt(this.select.dataset.maxRender, 10) || -1, + renderSelectedChoices: 'always', shouldSort: false, fuseOptions: { threshold: 0.3, // Strict search diff --git a/build/media_source/templates/administrator/atum/scss/blocks/_form.scss b/build/media_source/templates/administrator/atum/scss/blocks/_form.scss index bd9781b01ed..2c7995ccbe6 100644 --- a/build/media_source/templates/administrator/atum/scss/blocks/_form.scss +++ b/build/media_source/templates/administrator/atum/scss/blocks/_form.scss @@ -164,7 +164,8 @@ label.valid { } .filter-search-bar__description { - bottom: 100%; + top: 100%; + bottom: auto; } .container-popup .filter-search-bar__description { diff --git a/build/media_source/templates/administrator/atum/scss/pages/_com_users.scss b/build/media_source/templates/administrator/atum/scss/pages/_com_users.scss index 56d9b600906..e1f4412a73c 100644 --- a/build/media_source/templates/administrator/atum/scss/pages/_com_users.scss +++ b/build/media_source/templates/administrator/atum/scss/pages/_com_users.scss @@ -100,4 +100,10 @@ margin-inline-start: 2rem; } } + + &.view-user { + table thead th { + color: var(--bs-body-color); + } + } } diff --git a/build/media_source/templates/site/cassiopeia/scss/blocks/_form.scss b/build/media_source/templates/site/cassiopeia/scss/blocks/_form.scss index 438cb3f7a2c..e94376b815d 100644 --- a/build/media_source/templates/site/cassiopeia/scss/blocks/_form.scss +++ b/build/media_source/templates/site/cassiopeia/scss/blocks/_form.scss @@ -96,6 +96,7 @@ td .form-control { &[id^=id-skip-] { right: auto; + max-width: unset; } &[id^=cbunpublish] { diff --git a/build/media_source/vendor/short-and-sweet/js/short-and-sweet.es6.js b/build/media_source/vendor/short-and-sweet/js/short-and-sweet.es6.js new file mode 100644 index 00000000000..7c906610ab7 --- /dev/null +++ b/build/media_source/vendor/short-and-sweet/js/short-and-sweet.es6.js @@ -0,0 +1,9 @@ +import shortAndSweet from 'short-and-sweet/dist/short-and-sweet.module.js'; + +shortAndSweet('textarea.charcount,input.charcount', { counterClassName: 'small text-muted' }); + +/** Repeatable */ +document.addEventListener('joomla:updated', (event) => { + event.target.querySelectorAll('textarea.charcount,input.charcount') + .forEach((el) => shortAndSweet(el, { counterClassName: 'small text-muted' })); +}); diff --git a/build/media_source/vendor/tinymce/langs/da.es5.js b/build/media_source/vendor/tinymce/langs/da.es5.js index 2bfd5f9bc2d..e08c0403255 100644 --- a/build/media_source/vendor/tinymce/langs/da.es5.js +++ b/build/media_source/vendor/tinymce/langs/da.es5.js @@ -120,7 +120,7 @@ tinymce.addI18n('da',{ "Source": "Kilde", "Dimensions": "Dimensioner", "Constrain proportions": "Behold propertioner", -"General": "Generet", +"General": "Generelt", "Advanced": "Avanceret", "Style": "Stil", "Vertical space": "Lodret afstand", diff --git a/components/com_contact/src/Controller/ContactController.php b/components/com_contact/src/Controller/ContactController.php index d8b04fe0671..520ad430814 100644 --- a/components/com_contact/src/Controller/ContactController.php +++ b/components/com_contact/src/Controller/ContactController.php @@ -287,7 +287,7 @@ private function _sendEmail($data, $contact, $emailCopyToSender) $sent = $mailer->send(); // If we are supposed to copy the sender, do so. - if ($emailCopyToSender == true && !empty($data['contact_email_copy'])) { + if ($emailCopyToSender && !empty($data['contact_email_copy'])) { $mailer = new MailTemplate('com_contact.mail.copy', $app->getLanguage()->getTag()); $mailer->addRecipient($templateData['email']); $mailer->setReplyTo($templateData['email'], $templateData['name']); diff --git a/components/com_content/forms/filter_articles.xml b/components/com_content/forms/filter_articles.xml index 942c28dd132..7d5352422cd 100644 --- a/components/com_content/forms/filter_articles.xml +++ b/components/com_content/forms/filter_articles.xml @@ -50,7 +50,8 @@ hint="JOPTION_SELECT_AUTHOR" onchange="this.form.submit();" > - + + diff --git a/components/com_content/src/Model/FeaturedModel.php b/components/com_content/src/Model/FeaturedModel.php index 82771831000..2473a9d6879 100644 --- a/components/com_content/src/Model/FeaturedModel.php +++ b/components/com_content/src/Model/FeaturedModel.php @@ -93,7 +93,7 @@ protected function populateState($ordering = null, $direction = null) } // Check for category selection - if ($params->get('featured_categories') && implode(',', $params->get('featured_categories')) == true) { + if ($params->get('featured_categories') && implode(',', $params->get('featured_categories'))) { $featuredCategories = $params->get('featured_categories'); $this->setState('filter.frontpage.categories', $featuredCategories); } diff --git a/components/com_content/src/View/Article/HtmlView.php b/components/com_content/src/View/Article/HtmlView.php index 1287500b69a..238bd23c33f 100644 --- a/components/com_content/src/View/Article/HtmlView.php +++ b/components/com_content/src/View/Article/HtmlView.php @@ -179,7 +179,7 @@ public function display($tpl = null) $offset = (int) $this->state->get('list.offset'); // Check the view access to the article (the model has already computed the values). - if ($item->params->get('access-view') == false && ($item->params->get('show_noauth', '0') == '0')) { + if (!$item->params->get('access-view') && ($item->params->get('show_noauth', '0') == '0')) { $app->enqueueMessage(Text::_('JERROR_ALERTNOAUTHOR'), 'error'); $app->setHeader('status', 403, true); @@ -192,7 +192,7 @@ public function display($tpl = null) * - Deny access to logged users with 403 code * NOTE: we do not recheck for no access-view + show_noauth disabled ... since it was checked above */ - if ($item->params->get('access-view') == false && !\strlen($item->fulltext)) { + if (!$item->params->get('access-view') && !\strlen($item->fulltext)) { if ($this->user->guest) { $return = base64_encode(Uri::getInstance()); $login_url_with_return = Route::_('index.php?option=com_users&view=login&return=' . $return); diff --git a/components/com_content/tmpl/article/default.php b/components/com_content/tmpl/article/default.php index 98782149a9d..255dc31ec14 100644 --- a/components/com_content/tmpl/article/default.php +++ b/components/com_content/tmpl/article/default.php @@ -122,7 +122,7 @@ loadTemplate('links'); ?> - get('show_noauth') == true && $user->guest) : ?> + get('show_noauth') && $user->guest) : ?> item); ?> item->introtext); ?> diff --git a/components/com_content/tmpl/category/default_articles.php b/components/com_content/tmpl/category/default_articles.php index b5d1fa498b5..276f8bd6191 100644 --- a/components/com_content/tmpl/category/default_articles.php +++ b/components/com_content/tmpl/category/default_articles.php @@ -257,7 +257,7 @@ author) || !empty($article->created_by_alias)) : ?> author ?> created_by_alias ?: $author; ?> - contact_link) && $this->params->get('link_author') == true) : ?> + contact_link) && $this->params->get('link_author')) : ?> params->get('show_headings')) : ?> contact_link, $author); ?> diff --git a/components/com_finder/src/Helper/FinderHelper.php b/components/com_finder/src/Helper/FinderHelper.php index c7ce9e84b15..70a42092bef 100644 --- a/components/com_finder/src/Helper/FinderHelper.php +++ b/components/com_finder/src/Helper/FinderHelper.php @@ -54,7 +54,7 @@ public static function logSearch(Query $searchquery, $resultCount = 0) $temp = new \stdClass(); $temp->input = trim(strtolower((string) $searchquery->input)); $entry = new \stdClass(); - $entry->searchterm = $temp->input; + $entry->searchterm = mb_substr(trim($temp->input), 0, 255); $entry->query = serialize($temp); $entry->md5sum = md5($entry->query); $entry->hits = 1; diff --git a/components/com_tags/tmpl/tags/default_items.php b/components/com_tags/tmpl/tags/default_items.php index bfaf660b218..c5e714c33c1 100644 --- a/components/com_tags/tmpl/tags/default_items.php +++ b/components/com_tags/tmpl/tags/default_items.php @@ -80,7 +80,7 @@ class="inputbox" onchange="document.adminForm.submit();" - items == false || $n === 0) : ?> + items || $n === 0) : ?>
diff --git a/components/com_users/src/Controller/RemindController.php b/components/com_users/src/Controller/RemindController.php index d6a3491bc6d..95d1b3d825c 100644 --- a/components/com_users/src/Controller/RemindController.php +++ b/components/com_users/src/Controller/RemindController.php @@ -45,7 +45,7 @@ public function remind() $return = $model->processRemindRequest($data); // Check for a hard error. - if ($return == false && JDEBUG) { + if (!$return && JDEBUG) { // The request failed. // Go back to the request form. $message = Text::sprintf('COM_USERS_REMIND_REQUEST_FAILED', $model->getError()); diff --git a/components/com_users/src/Controller/UserController.php b/components/com_users/src/Controller/UserController.php index 4bf8e41f471..a59b802bd5b 100644 --- a/components/com_users/src/Controller/UserController.php +++ b/components/com_users/src/Controller/UserController.php @@ -99,7 +99,7 @@ public function login() } // Success - if ($options['remember'] == true) { + if ($options['remember']) { $this->app->setUserState('rememberLogin', true); } diff --git a/composer.json b/composer.json index e25d4ab3e0b..3d05ca7edc9 100644 --- a/composer.json +++ b/composer.json @@ -58,12 +58,12 @@ "joomla/console": "^3.0.1", "joomla/crypt": "^3.0.1", "joomla/data": "^3.0.1", - "joomla/database": "^3.3.1", + "joomla/database": "^3.4", "joomla/di": "^3.0.1", "joomla/event": "^3.0.1", "joomla/filter": "^3.0.2", - "joomla/filesystem": "^3.0.2", - "joomla/http": "^3.0.1", + "joomla/filesystem": "^3.1.0", + "joomla/http": "^3.1.0", "joomla/input": "~3.0", "joomla/language": "~3.0", "joomla/oauth1": "~3.0", @@ -74,7 +74,7 @@ "joomla/string": "^3.0.1", "joomla/uri": "~3.0", "joomla/utilities": "~3.0", - "algo26-matthias/idna-convert": "^3.1.1", + "algo26-matthias/idna-convert": "^3.2.0", "defuse/php-encryption": "^2.4.0", "doctrine/inflector": "^1.4.4", "fig/link-util": "^1.2.0", @@ -84,13 +84,13 @@ "phpmailer/phpmailer": "^6.9.3", "psr/link": "~1.1.1", "symfony/console": "^6.4.17", - "symfony/error-handler": "^6.4.18", + "symfony/error-handler": "^6.4.19", "symfony/ldap": "^6.4.13", "symfony/options-resolver": "^6.4.16", "symfony/polyfill-mbstring": "^1.31.0", "symfony/web-link": "^6.4.13", "symfony/yaml": "^6.4.18", - "typo3/phar-stream-wrapper": "^4.0.0", + "typo3/phar-stream-wrapper": "^3.1.8", "wamania/php-stemmer": "^4.0.0", "tobscure/json-api": "dev-joomla-backports", "willdurand/negotiation": "^3.1.0", @@ -100,7 +100,7 @@ "ext-gd": "*", "web-auth/webauthn-lib": "4.5.2", "ext-dom": "*", - "composer/ca-bundle": "^1.5.5", + "composer/ca-bundle": "^1.5.6", "dragonmantank/cron-expression": "^3.4.0", "enshrined/svg-sanitize": "^0.21.0", "lcobucci/jwt": "^4.3.0", @@ -108,16 +108,16 @@ "phpseclib/bcmath_compat": "^2.0.3", "jfcherng/php-diff": "^6.16.2", "voku/portable-utf8": "dev-joomla-5.3 as 6.0.13", - "php-tuf/php-tuf": "^1.0.2", - "php-debugbar/php-debugbar": "^2.1" + "php-tuf/php-tuf": "^1.0.3", + "php-debugbar/php-debugbar": "^2.1.6" }, "require-dev": { "phpunit/phpunit": "^9.6.22", - "friendsofphp/php-cs-fixer": "^3.68.5", - "squizlabs/php_codesniffer": "^3.11.3", + "friendsofphp/php-cs-fixer": "^3.72.0", + "squizlabs/php_codesniffer": "^3.12.0", "joomla/mediawiki": "^3.0", "joomla/test": "~3.0", - "phpstan/phpstan": "^2.1.2", + "phpstan/phpstan": "^2.1.8", "phpstan/phpstan-deprecation-rules": "^2.0.1" }, "replace": { diff --git a/composer.lock b/composer.lock index 49248c27a7b..6dd1e3a1a6e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b0456bd2df912057327fcee03b31bf70", + "content-hash": "a38a4283b189156423edcb9ee0f8c001", "packages": [ { "name": "algo26-matthias/idna-convert", - "version": "v3.1.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/algo26-matthias/idna-convert.git", - "reference": "d8dbf18599548b8460ab0c462f299a15cacf6e66" + "reference": "0cea987c75b981797cf3bc28b182c5da05c41252" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/algo26-matthias/idna-convert/zipball/d8dbf18599548b8460ab0c462f299a15cacf6e66", - "reference": "d8dbf18599548b8460ab0c462f299a15cacf6e66", + "url": "https://api.github.com/repos/algo26-matthias/idna-convert/zipball/0cea987c75b981797cf3bc28b182c5da05c41252", + "reference": "0cea987c75b981797cf3bc28b182c5da05c41252", "shasum": "" }, "require": { @@ -58,22 +58,22 @@ ], "support": { "issues": "https://github.com/algo26-matthias/idna-convert/issues", - "source": "https://github.com/algo26-matthias/idna-convert/tree/v3.1.1" + "source": "https://github.com/algo26-matthias/idna-convert/tree/v3.2.0" }, - "time": "2024-03-18T16:24:08+00:00" + "time": "2025-04-01T11:22:26+00:00" }, { "name": "brick/math", - "version": "0.12.1", + "version": "0.12.3", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", "shasum": "" }, "require": { @@ -82,7 +82,7 @@ "require-dev": { "php-coveralls/php-coveralls": "^2.2", "phpunit/phpunit": "^10.1", - "vimeo/psalm": "5.16.0" + "vimeo/psalm": "6.8.8" }, "type": "library", "autoload": { @@ -112,7 +112,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.1" + "source": "https://github.com/brick/math/tree/0.12.3" }, "funding": [ { @@ -120,20 +120,20 @@ "type": "github" } ], - "time": "2023-11-29T23:19:16+00:00" + "time": "2025-02-28T13:11:00+00:00" }, { "name": "composer/ca-bundle", - "version": "1.5.5", + "version": "1.5.6", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "08c50d5ec4c6ced7d0271d2862dec8c1033283e6" + "reference": "f65c239c970e7f072f067ab78646e9f0b2935175" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/08c50d5ec4c6ced7d0271d2862dec8c1033283e6", - "reference": "08c50d5ec4c6ced7d0271d2862dec8c1033283e6", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/f65c239c970e7f072f067ab78646e9f0b2935175", + "reference": "f65c239c970e7f072f067ab78646e9f0b2935175", "shasum": "" }, "require": { @@ -180,7 +180,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.5.5" + "source": "https://github.com/composer/ca-bundle/tree/1.5.6" }, "funding": [ { @@ -196,7 +196,7 @@ "type": "tidelift" } ], - "time": "2025-01-08T16:17:16+00:00" + "time": "2025-03-06T14:30:56+00:00" }, { "name": "defuse/php-encryption", @@ -1502,16 +1502,16 @@ }, { "name": "joomla/database", - "version": "3.3.1", + "version": "3.4.2", "source": { "type": "git", "url": "https://github.com/joomla-framework/database.git", - "reference": "d13a7504471b4730a8107e3566c6fe6d476706fc" + "reference": "ddea28591d13a2a762eb2ed080afba422868d005" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/joomla-framework/database/zipball/d13a7504471b4730a8107e3566c6fe6d476706fc", - "reference": "d13a7504471b4730a8107e3566c6fe6d476706fc", + "url": "https://api.github.com/repos/joomla-framework/database/zipball/ddea28591d13a2a762eb2ed080afba422868d005", + "reference": "ddea28591d13a2a762eb2ed080afba422868d005", "shasum": "" }, "require": { @@ -1569,7 +1569,7 @@ ], "support": { "issues": "https://github.com/joomla-framework/database/issues", - "source": "https://github.com/joomla-framework/database/tree/3.3.1" + "source": "https://github.com/joomla-framework/database/tree/3.4.2" }, "funding": [ { @@ -1581,7 +1581,7 @@ "type": "github" } ], - "time": "2025-01-26T13:22:09+00:00" + "time": "2025-05-10T11:36:16+00:00" }, { "name": "joomla/di", @@ -1725,16 +1725,16 @@ }, { "name": "joomla/filesystem", - "version": "3.0.2", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/filesystem.git", - "reference": "c69358f6500b8d931bd6e4e3042aa365dcace155" + "reference": "4bcdf0b029db585c42169ddd5c4f3635c7cac04e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/joomla-framework/filesystem/zipball/c69358f6500b8d931bd6e4e3042aa365dcace155", - "reference": "c69358f6500b8d931bd6e4e3042aa365dcace155", + "url": "https://api.github.com/repos/joomla-framework/filesystem/zipball/4bcdf0b029db585c42169ddd5c4f3635c7cac04e", + "reference": "4bcdf0b029db585c42169ddd5c4f3635c7cac04e", "shasum": "" }, "require": { @@ -1744,7 +1744,8 @@ "joomla/test": "^3.0", "mikey179/vfsstream": "^1.6.11", "phan/phan": "^5.4.2", - "phpstan/phpstan": "^1.10.7", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0", "phpunit/phpunit": "^9.5.28", "squizlabs/php_codesniffer": "^3.7.2" }, @@ -1773,7 +1774,7 @@ ], "support": { "issues": "https://github.com/joomla-framework/filesystem/issues", - "source": "https://github.com/joomla-framework/filesystem/tree/3.0.2" + "source": "https://github.com/joomla-framework/filesystem/tree/3.1.0" }, "funding": [ { @@ -1785,7 +1786,7 @@ "type": "github" } ], - "time": "2024-09-12T11:44:35+00:00" + "time": "2025-03-07T09:56:32+00:00" }, { "name": "joomla/filter", @@ -1856,16 +1857,16 @@ }, { "name": "joomla/http", - "version": "3.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/http.git", - "reference": "3379b5a0c68524685b89d6997e5fec56c6ad3d43" + "reference": "157ceb1d386cb4a80292909f128f53f1b99f2597" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/joomla-framework/http/zipball/3379b5a0c68524685b89d6997e5fec56c6ad3d43", - "reference": "3379b5a0c68524685b89d6997e5fec56c6ad3d43", + "url": "https://api.github.com/repos/joomla-framework/http/zipball/157ceb1d386cb4a80292909f128f53f1b99f2597", + "reference": "157ceb1d386cb4a80292909f128f53f1b99f2597", "shasum": "" }, "require": { @@ -1879,7 +1880,8 @@ "require-dev": { "joomla/test": "^3.0", "phan/phan": "^5.4.2", - "phpstan/phpstan": "^1.10.7", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0", "phpunit/phpunit": "^9.5.28", "squizlabs/php_codesniffer": "^3.7.2" }, @@ -1911,7 +1913,7 @@ ], "support": { "issues": "https://github.com/joomla-framework/http/issues", - "source": "https://github.com/joomla-framework/http/tree/3.0.1" + "source": "https://github.com/joomla-framework/http/tree/3.1.0" }, "funding": [ { @@ -1923,7 +1925,7 @@ "type": "github" } ], - "time": "2024-03-11T18:42:37+00:00" + "time": "2025-03-05T11:15:59+00:00" }, { "name": "joomla/input", @@ -3025,16 +3027,16 @@ }, { "name": "php-debugbar/php-debugbar", - "version": "v2.1.1", + "version": "v2.1.6", "source": { "type": "git", "url": "https://github.com/php-debugbar/php-debugbar.git", - "reference": "e044ee885e5e0ba54da1138ce49c9096f6e3facb" + "reference": "16fa68da5617220594aa5e33fa9de415f94784a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/e044ee885e5e0ba54da1138ce49c9096f6e3facb", - "reference": "e044ee885e5e0ba54da1138ce49c9096f6e3facb", + "url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/16fa68da5617220594aa5e33fa9de415f94784a0", + "reference": "16fa68da5617220594aa5e33fa9de415f94784a0", "shasum": "" }, "require": { @@ -3089,22 +3091,22 @@ ], "support": { "issues": "https://github.com/php-debugbar/php-debugbar/issues", - "source": "https://github.com/php-debugbar/php-debugbar/tree/v2.1.1" + "source": "https://github.com/php-debugbar/php-debugbar/tree/v2.1.6" }, - "time": "2025-02-13T12:08:20+00:00" + "time": "2025-02-21T17:47:03+00:00" }, { "name": "php-tuf/php-tuf", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/joomla-backports/php-tuf.git", - "reference": "051750c097eaa1c3954e5f1f480f708260de5d47" + "reference": "1fc585dc5a090a4af94823e21470779f31030733" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/joomla-backports/php-tuf/zipball/051750c097eaa1c3954e5f1f480f708260de5d47", - "reference": "051750c097eaa1c3954e5f1f480f708260de5d47", + "url": "https://api.github.com/repos/joomla-backports/php-tuf/zipball/1fc585dc5a090a4af94823e21470779f31030733", + "reference": "1fc585dc5a090a4af94823e21470779f31030733", "shasum": "" }, "require": { @@ -3165,7 +3167,7 @@ "MIT" ], "description": "PHP implementation of The Update Framework (TUF)", - "time": "2025-02-03T07:10:33+00:00" + "time": "2025-03-19T20:05:51+00:00" }, { "name": "phpmailer/phpmailer", @@ -4277,16 +4279,16 @@ }, { "name": "symfony/error-handler", - "version": "v6.4.18", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "e8d3b5b1975e67812a54388b1ba8e9ec28eb770e" + "reference": "3d4e55cd2b8f1979a65eba9ab749d6466c316f71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/e8d3b5b1975e67812a54388b1ba8e9ec28eb770e", - "reference": "e8d3b5b1975e67812a54388b1ba8e9ec28eb770e", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/3d4e55cd2b8f1979a65eba9ab749d6466c316f71", + "reference": "3d4e55cd2b8f1979a65eba9ab749d6466c316f71", "shasum": "" }, "require": { @@ -4332,7 +4334,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.4.18" + "source": "https://github.com/symfony/error-handler/tree/v6.4.19" }, "funding": [ { @@ -4348,20 +4350,20 @@ "type": "tidelift" } ], - "time": "2025-01-06T09:38:16+00:00" + "time": "2025-02-02T20:16:33+00:00" }, { "name": "symfony/http-client", - "version": "v6.4.18", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "394b440934056b8d9d6ba250001458e9d7998b7f" + "reference": "3294a433fc9d12ae58128174896b5b1822c28dad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/394b440934056b8d9d6ba250001458e9d7998b7f", - "reference": "394b440934056b8d9d6ba250001458e9d7998b7f", + "url": "https://api.github.com/repos/symfony/http-client/zipball/3294a433fc9d12ae58128174896b5b1822c28dad", + "reference": "3294a433fc9d12ae58128174896b5b1822c28dad", "shasum": "" }, "require": { @@ -4425,7 +4427,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.18" + "source": "https://github.com/symfony/http-client/tree/v6.4.19" }, "funding": [ { @@ -4441,7 +4443,7 @@ "type": "tidelift" } ], - "time": "2025-01-28T15:49:13+00:00" + "time": "2025-02-13T09:55:13+00:00" }, { "name": "symfony/http-client-contracts", @@ -5540,16 +5542,16 @@ }, { "name": "symfony/validator", - "version": "v6.4.18", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "ce20367d07b2592202e9c266b16a93fa50145207" + "reference": "f3e853dffe7c5db675686b8216d6d890dad8c885" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/ce20367d07b2592202e9c266b16a93fa50145207", - "reference": "ce20367d07b2592202e9c266b16a93fa50145207", + "url": "https://api.github.com/repos/symfony/validator/zipball/f3e853dffe7c5db675686b8216d6d890dad8c885", + "reference": "f3e853dffe7c5db675686b8216d6d890dad8c885", "shasum": "" }, "require": { @@ -5617,7 +5619,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v6.4.18" + "source": "https://github.com/symfony/validator/tree/v6.4.19" }, "funding": [ { @@ -5633,7 +5635,7 @@ "type": "tidelift" } ], - "time": "2025-01-27T16:05:44+00:00" + "time": "2025-02-19T13:12:02+00:00" }, { "name": "symfony/var-dumper", @@ -5881,12 +5883,12 @@ "source": { "type": "git", "url": "https://github.com/joomla-backports/json-api-php.git", - "reference": "c6580b75760cd00e555ef5ef1028f63231fb53f2" + "reference": "89f1ef6a6711bd2f03d67fa13e4704928da411da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/joomla-backports/json-api-php/zipball/c6580b75760cd00e555ef5ef1028f63231fb53f2", - "reference": "c6580b75760cd00e555ef5ef1028f63231fb53f2", + "url": "https://api.github.com/repos/joomla-backports/json-api-php/zipball/89f1ef6a6711bd2f03d67fa13e4704928da411da", + "reference": "89f1ef6a6711bd2f03d67fa13e4704928da411da", "shasum": "" }, "require": { @@ -5929,29 +5931,30 @@ "jsonapi", "standard" ], - "time": "2025-03-13T08:47:04+00:00" + "time": "2025-03-21T09:49:11+00:00" }, { "name": "typo3/phar-stream-wrapper", - "version": "v4.0.0", + "version": "v3.1.8", "source": { "type": "git", "url": "https://github.com/TYPO3/phar-stream-wrapper.git", - "reference": "ce4b6e9873d4dd7dce2a397511713bf14977fdf2" + "reference": "a931b28f422a60052db85c0a84a05a366453b2c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3/phar-stream-wrapper/zipball/ce4b6e9873d4dd7dce2a397511713bf14977fdf2", - "reference": "ce4b6e9873d4dd7dce2a397511713bf14977fdf2", + "url": "https://api.github.com/repos/TYPO3/phar-stream-wrapper/zipball/a931b28f422a60052db85c0a84a05a366453b2c0", + "reference": "a931b28f422a60052db85c0a84a05a366453b2c0", "shasum": "" }, "require": { "ext-json": "*", - "php": "^7.1 || ^8.0" + "php": "^7.0 || ~8.0 || ~8.1 || ~8.2 || ~8.3" }, "require-dev": { "ext-xdebug": "*", - "phpunit/phpunit": "^7 || ^8 || ^9" + "phpspec/prophecy": "^1.10", + "symfony/phpunit-bridge": "^5.1" }, "suggest": { "ext-fileinfo": "For PHP builtin file type guessing, otherwise uses internal processing" @@ -5959,7 +5962,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "v3.x-dev" } }, "autoload": { @@ -5981,9 +5984,9 @@ ], "support": { "issues": "https://github.com/TYPO3/phar-stream-wrapper/issues", - "source": "https://github.com/TYPO3/phar-stream-wrapper/tree/v4.0.0" + "source": "https://github.com/TYPO3/phar-stream-wrapper/tree/v3.1.8" }, - "time": "2024-12-10T00:00:25+00:00" + "time": "2024-12-09T23:06:33+00:00" }, { "name": "voku/portable-ascii", @@ -7113,16 +7116,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.68.5", + "version": "v3.72.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "7bedb718b633355272428c60736dc97fb96daf27" + "reference": "900389362c43d116fee1ffc51f7878145fa61b57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/7bedb718b633355272428c60736dc97fb96daf27", - "reference": "7bedb718b633355272428c60736dc97fb96daf27", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/900389362c43d116fee1ffc51f7878145fa61b57", + "reference": "900389362c43d116fee1ffc51f7878145fa61b57", "shasum": "" }, "require": { @@ -7139,7 +7142,7 @@ "react/promise": "^2.0 || ^3.0", "react/socket": "^1.0", "react/stream": "^1.0", - "sebastian/diff": "^4.0 || ^5.1 || ^6.0", + "sebastian/diff": "^4.0 || ^5.1 || ^6.0 || ^7.0", "symfony/console": "^5.4 || ^6.4 || ^7.0", "symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0", "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", @@ -7152,18 +7155,18 @@ "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.4", - "infection/infection": "^0.29.8", - "justinrainbow/json-schema": "^5.3 || ^6.0", + "facile-it/paraunit": "^1.3.1 || ^2.6", + "infection/infection": "^0.29.14", + "justinrainbow/json-schema": "^5.3 || ^6.2", "keradus/cli-executor": "^2.1", "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.7", "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.5", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.5", - "phpunit/phpunit": "^9.6.22 || ^10.5.40 || ^11.5.2", - "symfony/var-dumper": "^5.4.48 || ^6.4.15 || ^7.2.0", - "symfony/yaml": "^5.4.45 || ^6.4.13 || ^7.2.0" + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", + "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.12", + "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.3", + "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.3" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -7204,7 +7207,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.68.5" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.72.0" }, "funding": [ { @@ -7212,7 +7215,7 @@ "type": "github" } ], - "time": "2025-01-30T17:00:50+00:00" + "time": "2025-03-13T11:25:37+00:00" }, { "name": "joomla/mediawiki", @@ -7356,16 +7359,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.1", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + "reference": "024473a478be9df5fdaca2c793f2232fe788e414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414", "shasum": "" }, "require": { @@ -7404,7 +7407,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" }, "funding": [ { @@ -7412,7 +7415,7 @@ "type": "tidelift" } ], - "time": "2024-11-08T17:47:46+00:00" + "time": "2025-02-12T12:17:51+00:00" }, { "name": "nikic/php-parser", @@ -7592,16 +7595,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.2", + "version": "2.1.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "7d08f569e582ade182a375c366cbd896eccadd3a" + "reference": "f9adff3b87c03b12cc7e46a30a524648e497758f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/7d08f569e582ade182a375c366cbd896eccadd3a", - "reference": "7d08f569e582ade182a375c366cbd896eccadd3a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f", + "reference": "f9adff3b87c03b12cc7e46a30a524648e497758f", "shasum": "" }, "require": { @@ -7646,7 +7649,7 @@ "type": "github" } ], - "time": "2025-01-21T14:54:06+00:00" + "time": "2025-03-09T09:30:48+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -9608,16 +9611,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.11.3", + "version": "3.12.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10" + "reference": "2d1b63db139c3c6ea0c927698e5160f8b3b8d630" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", - "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/2d1b63db139c3c6ea0c927698e5160f8b3b8d630", + "reference": "2d1b63db139c3c6ea0c927698e5160f8b3b8d630", "shasum": "" }, "require": { @@ -9684,11 +9687,11 @@ "type": "open_collective" }, { - "url": "https://thanks.dev/phpcsstandards", + "url": "https://thanks.dev/u/gh/phpcsstandards", "type": "thanks_dev" } ], - "time": "2025-01-23T17:04:15+00:00" + "time": "2025-03-18T05:04:51+00:00" }, { "name": "symfony/event-dispatcher", @@ -9978,16 +9981,16 @@ }, { "name": "symfony/process", - "version": "v6.4.15", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "3cb242f059c14ae08591c5c4087d1fe443564392" + "reference": "7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3cb242f059c14ae08591c5c4087d1fe443564392", - "reference": "3cb242f059c14ae08591c5c4087d1fe443564392", + "url": "https://api.github.com/repos/symfony/process/zipball/7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3", + "reference": "7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3", "shasum": "" }, "require": { @@ -10019,7 +10022,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.15" + "source": "https://github.com/symfony/process/tree/v6.4.19" }, "funding": [ { @@ -10035,20 +10038,20 @@ "type": "tidelift" } ], - "time": "2024-11-06T14:19:14+00:00" + "time": "2025-02-04T13:35:48+00:00" }, { "name": "symfony/stopwatch", - "version": "v6.4.13", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "2cae0a6f8d04937d02f6d19806251e2104d54f92" + "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2cae0a6f8d04937d02f6d19806251e2104d54f92", - "reference": "2cae0a6f8d04937d02f6d19806251e2104d54f92", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", + "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", "shasum": "" }, "require": { @@ -10081,7 +10084,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.4.13" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.19" }, "funding": [ { @@ -10097,7 +10100,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2025-02-21T10:06:30+00:00" }, { "name": "theseer/tokenizer", diff --git a/cypress.config.dist.mjs b/cypress.config.dist.mjs index e69abca8a4e..612b9e0c1de 100644 --- a/cypress.config.dist.mjs +++ b/cypress.config.dist.mjs @@ -42,5 +42,6 @@ export default defineConfig({ smtp_host: 'localhost', smtp_port: '1025', cmsPath: '.', + logFile: '', }, }); diff --git a/includes/incompatible.html b/includes/incompatible.html index 4593b6d29f3..513d0941d61 100644 --- a/includes/incompatible.html +++ b/includes/incompatible.html @@ -6,7 +6,7 @@ Joomla: unsupported PHP version - +
diff --git a/index.php b/index.php index 0ce8bceb9ae..b070eda182c 100644 --- a/index.php +++ b/index.php @@ -28,5 +28,24 @@ */ define('_JEXEC', 1); +// Load global path definitions +if (file_exists(__DIR__ . '/defines.php')) { + include_once __DIR__ . '/defines.php'; +} + +require_once __DIR__ . '/includes/defines.php'; + +// Check the existence of an update-extraction config file +if ( + !empty($_GET['jautoupdate']) + && is_file(JPATH_ADMINISTRATOR . '/components/com_joomlaupdate/update.php') +) { + // Load extraction script and... + require_once JPATH_ADMINISTRATOR . '/components/com_joomlaupdate/extract.php'; + + // ... die + die(); +} + // Run the application - All executable code should be triggered through this file require_once __DIR__ . '/includes/app.php'; diff --git a/installation/language/af-ZA/langmetadata.xml b/installation/language/af-ZA/langmetadata.xml index 8e50793e377..93f6b011f39 100644 --- a/installation/language/af-ZA/langmetadata.xml +++ b/installation/language/af-ZA/langmetadata.xml @@ -1,8 +1,8 @@ Afrikaans (Suid-Afrika) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Afrikaans Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/ar-AA/joomla.ini b/installation/language/ar-AA/joomla.ini index b92783ff8b2..6452eaea8fe 100644 --- a/installation/language/ar-AA/joomla.ini +++ b/installation/language/ar-AA/joomla.ini @@ -5,31 +5,31 @@ ; Fatal error page ; These will be processed by the JavaScript Build -BUILD_FATAL_HEADER="عذرًا ، كانت هناك مشكلة لم نتمكن من تجاوزها." +BUILD_FATAL_HEADER="عذرًا، كانت هناك مشكلة لم نتمكن من تجاوزها." BUILD_FATAL_LANGUAGE="اللغة العربية بالكود الموحد" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR -BUILD_FATAL_TEXT="أعاد الخادم ملف \"{{statusCode_statusText}}\"" +BUILD_FATAL_TEXT="أرجع الخادم \"{{statusCode_statusText}}\"" BUILD_FATAL_URL_TEXT="ساعدني في حل هذا" ; These will be processed by the JavaScript Build BUILD_INCOMPLETE_HEADER="إعداد البيئة غير مكتمل" BUILD_INCOMPLETE_LANGUAGE="اللغة العربية بالكود الموحد" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR -BUILD_INCOMPLETE_TEXT="يبدو أنك تحاول تشغيل جوملا! من مستودع git الخاص بك. للقيام بذلك يتطلب منك إكمال خطوتين إضافيتين أولاً." -BUILD_INCOMPLETE_URL_TEXT="[ المزيد من التفاصيل %s]" +BUILD_INCOMPLETE_TEXT="يبدو أنك تحاول تشغيل جوملا! من مستودع git الخاص بنا. يتطلب هذا منك إكمال خطوتين إضافيتين أولاً." +BUILD_INCOMPLETE_URL_TEXT="المزيد من التفاصيل" ; These will be processed by the JavaScript Build -BUILD_NOXML_HEADER="عذرا ، PHP الخاص بك يفتقد مكتبة حيوية" +BUILD_NOXML_HEADER="عذرًا، PHP الخاص بك يفتقد مكتبة حيوية" BUILD_NOXML_LANGUAGE="اللغة العربية بالكود الموحد" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR -BUILD_NOXML_TEXT="يحتاج مضيفك إلى استخدام PHP مع دعم مكتبة XML لتشغيل هذا الإصدار من جوملا!" +BUILD_NOXML_TEXT="يحتاج الخادم الخاص بك إلى استخدام PHP مع دعم مكتبة XML لتشغيل هذا الإصدار من جوملا!" BUILD_NOXML_URL_TEXT="ساعدني في حل هذا" ; These will be processed by the JavaScript Build -BUILD_MIN_PHP_ERROR_HEADER="عذرًا ، إصدار PHP الخاص بك غير مدعوم." +BUILD_MIN_PHP_ERROR_HEADER="عذرًا، إصدار PHP الخاص بك غير مدعوم." BUILD_MIN_PHP_ERROR_LANGUAGE="اللغة العربية بالكود الموحد" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR -BUILD_MIN_PHP_ERROR_TEXT="يحتاج مضيفك إلى استخدام إصدار PHP {{phpversion}} أو أحدث لتشغيل هذا الإصدار من جوملا." +BUILD_MIN_PHP_ERROR_TEXT="يحتاج الخادم الخاص بك إلى استخدام إصدار PHP {{phpversion}} أو أحدث لتشغيل هذا الإصدار من جوملا." BUILD_MIN_PHP_ERROR_URL_TEXT="ساعدني في حل هذا" ; Main Config -INSTL_SELECT_INSTALL_LANG="حدد لغة التنصيب" -INSTL_SELECT_LANGUAGE_TITLE="اختار اللغة" +INSTL_SELECT_INSTALL_LANG="حدد لغة التثبيت" +INSTL_SELECT_LANGUAGE_TITLE="اختر اللغة" INSTL_SETUP_LOGIN_DATA="إعداد بيانات تسجيل الدخول" -INSTL_WARNJAVASCRIPT="تحذير! يجب تمكين الجافا سكريبت لكي يتم تثبيت نظتم جوملا! بشكل صحيح" -INSTL_WARNJSON="PHP خلال تثبيتكم JSON للتمكن من تثيت جوملا . يجب تفعيل" +INSTL_WARNJAVASCRIPT="تحذير! يجب تمكين الجافاسكربت لكي يتم تثبيت نظام جوملا! بشكل صحيح." +INSTL_WARNJSON="يجب تفعيل JSON في نسخة PHP الخاصة بك لتتمكن من تثبيت جوملا!" ; Precheck view INSTL_DATABASE_SUPPORT="دعم قاعدة البيانات:" INSTL_JSON_SUPPORT_AVAILABLE="دعم JSON" @@ -37,15 +37,15 @@ INSTL_MB_LANGUAGE_IS_DEFAULT="استخدام لغة افتراضية متعدد ; Deprecated, will be removed with 6.0 INSTL_MB_STRING_OVERLOAD_OFF="إيقاف التحميل الزائد للسلسة متعددة وحدات البايت" INSTL_NOTICE_DATABASE_SUPPORT="لم يتم العثور على قواعد البيانات المدعومة." -INSTL_NOTICE_JSON_SUPPORT_AVAILABLE="PHP خلال تثبيتكم JSON للتمكن من تثيت جوملا . يجب تفعيل" -INSTL_NOTICE_MBLANG_NOTDEFAULT="لم يتم تعيين لغة PHP mbstring على الحيادية. يمكن تعيين ذلك محليًا عن طريق إدخال php_value mbstring.languageutral في ملف .htaccess الخاص بك." +INSTL_NOTICE_JSON_SUPPORT_AVAILABLE="يجب تفعيل JSON في نسخة PHP الخاصة بك لتتمكن من تثبيت جوملا!" +INSTL_NOTICE_MBLANG_NOTDEFAULT="لم يتم تعيين لغة PHP mbstring على الحيادية. يمكن تعيين ذلك محليًا عن طريق إدخال php_value mbstring.languageutral في مِلَفّ .htaccess الخاص بك." ; Deprecated, will be removed with 6.0 -INSTL_NOTICE_MBSTRING_OVERLOAD_OFF="تم تعيين overload دالة PHP mbstring. يمكن إيقاف تشغيل هذا محليًا عن طريق إدخال php_value mbstring.func_overload 0 في ملف .htaccess الخاص بك." +INSTL_NOTICE_MBSTRING_OVERLOAD_OFF="تم تعيين overload دالة PHP mbstring. يمكن إيقاف تشغيل هذه محليًا عن طريق إدخال php_value mbstring.func_overload 0 في مِلَفّ .htaccess الخاص بك." INSTL_NOTICE_PARSE_INI_FILE_AVAILABLE="وظائف php المطلوبة parse_ini_file و parse_ini_string معطلة على الخادم الخاص بك." INSTL_NOTICE_XML_SUPPORT="دعم XML غير متوفر. يجب تمكين هذا افتراضيًا في php ولكن قد يحتاج مستخدمو Ubuntu إلى تثبيت هذا عن طريق إجراء sudo apt-get install php-xml متبوعًا بإعادة تشغيل خادم الويب." INSTL_NOTICE_ZLIB_COMPRESSION_SUPPORT="لم يتم تعيين ضغط Zlib. يمكن تشغيل هذا محليًا عن طريق إدخال zlib.output_compression = On في ملف php.ini ." INSTL_PARSE_INI_FILE_AVAILABLE="دعم محلل INI" -INSTL_PRECHECK_ACTUAL="حالياً" +INSTL_PRECHECK_ACTUAL="حاليًا" INSTL_PRECHECK_DESC="إذا كان أي من هذه المناصر غير مدعوم \"أي غير متوفر\" سيكون مكتوب باللون الأحمر (لا) عندها يرجى القيام بالإجراءات اللازمة لتصحيحها.
لا يمكنك تنصيب جوملا! حتى توفير الإعدادت المطلوبة أدناه." INSTL_PRECHECK_DIRECTIVE="التوجيهات" INSTL_PRECHECK_RECOMMENDED="ينصح بها" @@ -81,10 +81,13 @@ INSTL_DATABASE_NAME_LABEL="اسم قاعدة البيانات" INSTL_DATABASE_NAME_MSG_MYSQL="اسم قاعدة البيانات غير صالح. يجب ألا يحتوي على الأحرف التالية: \ /" INSTL_DATABASE_NAME_MSG_POSTGRES="اسم قاعدة البيانات غير صالح. يجب أن يبدأ بحرف ، متبوعًا بأحرف أبجدية رقمية." INSTL_DATABASE_NO_SCHEMA="لايوجد مخطط لقاعدة البيانات متوفرة لهذا النوع من قواعد البيانات." +INSTL_DATABASE_PASSWORD_DESC="أدخل كلمة مرور قاعدة البيانات التي قمت بإنشائها أو كلمة المرور المقدمة من مزود خدمة الاستضافة الخاص بك." INSTL_DATABASE_PREFIX_DESC="قم باختيار بادئة الجداول او يتم اختيارها عشوائيا. المستحسن, ان يكون عدد الحروف اربعة أو خمسة, تحتوي على حروف فقط, و يفترض ان تكون النهاية بالسطر السفلي. يجب التأكد أن البادئة ليست مستعملة لصفوف اخرى في قاعدة البيانات." +INSTL_DATABASE_PREFIX_DUPLICATE_DESC="إذا كنت تستخدم قاعدة بيانات موجودة مع جداول بنفس البادئة، سيقوم جوملا بإعادة تسمية الجداول الموجودة وذلك بإضافة البادئة \"bak_\"." INSTL_DATABASE_PREFIX_MSG="بادئة الجداول يجب أن تبدأ بحرف، يتبعه عدد اختياري من الحروف والأرقام وينتهي بالشرطة السفلية(_)." INSTL_DATABASE_RESPONSE_ERROR="فشلت عملية التنصيب." INSTL_DATABASE_TYPE_DESC="هذه على الأرجح \"MySQLi\"" +INSTL_DATABASE_USER_DESC="أدخل اسم مستخدم قاعدة البيانات التي قمت بإنشائها أو اسم المستخدم الذي حصلت عليه من مزود خدمة الاستضافة الخاص بك." INSTL_DATABASE_VALIDATION_ERROR="تحقق من بيانات اعتماد قاعدة البيانات أو نوع قاعدة البيانات أو اسم قاعدة البيانات أو اسم المضيف. إذا كان لديك MySQL 8 مثبتًا ، فالرجاء قراءة توضيحات لمزيد من المعلومات." INSTL_CONNECT_DB="إعداد اتصال قاعدة البيانات" @@ -100,6 +103,9 @@ INSTL_SETUP_SITE_NAME="إعداد اسم الموقع" INSTL_SITE="الإعدادات الرئيسية" INSTL_SITE_DEVMODE_LABEL="اكتشفنا وضع التطوير development mode" INSTL_SITE_NAME_DESC="أدخل اسم الموقع الخاص بك المبني بجوملا!." +; Expert View +INSTL_PUBLIC_FOLDER_DESC="المسار النسبي أو المطلق إلى المجلد العام" +INSTL_PUBLIC_FOLDER_DESC_SHORT="المسار النسبي أو المطلق إلى المجلد العام" ; Complete view INSTL_COMPLETE_ERROR_FOLDER_DELETE="تعذر حذف مجلد \"%s\" . يرجى القيام بحذفه بشكل يدوي." INSTL_COMPLETE_REMOVE_FOLDER="يرجى إزالة مجلد \"%s\"" @@ -117,7 +123,10 @@ INSTL_LANGUAGES_COLUMN_HEADER_LANGUAGE="اللغة" INSTL_LANGUAGES_COLUMN_HEADER_LANGUAGE_SELECT="اختار اللغة" INSTL_LANGUAGES_COLUMN_HEADER_LANGUAGE_TAG="كود اللغة" INSTL_LANGUAGES_COLUMN_HEADER_VERSION="الاصدار" +INSTL_LANGUAGES_DESC="واجهة Joomla متاحة بعدة لغات. اختر لغاتك المفضلة عن طريق اختيار خانات الاختيار ثم تثبيتها عن طريق تحديد زر \"تثبيت اللغات المحددة\".
ملاحظة: هذه العملية ستستغرق حوالي 10 ثوان لتنزيل وتثبيت كل لغة. لتجنب المهلة الزمنية يرجى تحديد ما لا يزيد عن 3 لغات للتثبيت." +INSTL_LANGUAGES_MESSAGE_PLEASE_WAIT="ستستغرق هذه العملية ما يصل إلى 10 ثوان في كل لغة للإكتمال
الرجاء الانتظار بينما نقوم بتنزيل وتثبيت اللغات …" INSTL_LANGUAGES_NO_LANGUAGE_SELECTED="لطفاً, حدد اللغة. إن لم تكن تريد تنصيب المزيد من اللغات فانقر على زر 'السابق' " +INSTL_LANGUAGES_SELECTED="قم بتثبيت اللغات المحددة" INSTL_LANGUAGES_WARNING_NO_INTERNET="لم تتمكن جوملا! من الاتصال بسيرفر اللغات. لطفاً قم بإنهاء عملية التنصيب." INSTL_LANGUAGES_WARNING_NO_INTERNET2="ملاحظة: سيكون متاحاً لك تنصيب اللغات أيضا من لوحة تحكم جوملا!" INSTL_LANGUAGES_WARNING_BACK_BUTTON="العودة للمرحلة الأخيرة من التنصيب" @@ -186,6 +195,7 @@ INSTL_NOTICE_NEEDSTOBEWRITABLE="لا يزال بإمكانك متابعة الت INSTL_OUTPUT_BUFFERING="تخزين المخرجات Output Buffering" INSTL_PHP_VERSION="نسخة PHP" INSTL_PHP_VERSION_NEWER="إصدار PHP >= %s" +INSTL_PROCESS_BUSY="العملية قيد الإجراء. الرجاء الانتظار …" INSTL_SESSION_AUTO_START="بداية تلقائية للجلسة Session auto start" INSTL_WRITABLE="%s قابل للكتابة" INSTL_ZIP_SUPPORT_AVAILABLE="توفر ZIP خاصية فك الملفات المضغوطة" @@ -201,6 +211,7 @@ JGLOBAL_SELECT_NO_RESULTS_MATCH="لايوجد نتائج مشابهة" JGLOBAL_SELECT_SOME_OPTIONS="حدد بعض الخيارات" JHIDEPASSWORD="إخفاء كلمة المرور" JINVALID_TOKEN="رمز غير صالح" +JINVALID_TOKEN_NOTICE="قيم الكود الأمني غير متوافقة. تم رفض الطلب تفاديًا لأي خرق أمني محتمل. لطفاً حاول مرة أخرى." JNEXT="التالي" JNO="ابدا" JNOTICE="ملاحظة" @@ -222,6 +233,7 @@ JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER="JFolder: :حذف: المسار لا JLIB_FORM_FIELD_INVALID="حقل غير صحيح: " JLIB_FORM_VALIDATE_FIELD_INVALID="حقل غير صالح: %s" JLIB_FORM_VALIDATE_FIELD_REQUIRED="حقل مطلوب: %s" +JLIB_INSTALLER_ABORT="إيقاف تثبيت اللغة: %s" JLIB_INSTALLER_ABORT_CREATE_DIRECTORY="التطبيق %1$s: فشل انشاء الدليل باسم: %2$s" JLIB_INSTALLER_ABORT_NOINSTALLPATH="مسار التنصيب غير موجود" JLIB_INSTALLER_ABORT_PACK_INSTALL_ERROR_EXTENSION="الحزمة %1$s: حصل خطأ عند محاولة تنصيب الاضافة: %2$s" @@ -234,6 +246,7 @@ JLIB_INSTALLER_WARNING_UNABLE_TO_INSTALL_CONTENT_LANGUAGE="تعذر إنشاء JLIB_UPDATER_ERROR_COLLECTION_FOPEN="الدالة allow_url_fopen غير مفعلة. يجب تفعيلها كي تتم عملية التحديث." JLIB_UPDATER_ERROR_COLLECTION_OPEN_URL="Update: :Collection: غير قادر على فتح %s" JLIB_UPDATER_ERROR_COLLECTION_PARSE_URL="Update: :Collection: لا يمكن تحليل %s" +JLIB_UPDATER_ERROR_OPEN_UPDATE_SITE="تحديث: تعذر فتح موقع التحديث #%d "%s", الرابط: %s." JLIB_UTIL_ERROR_CONNECT_DATABASE="JDatabase: :getInstance: تعذر الاتصال بقاعدة البيانات
joomla.library: %1$s - %2$s" ; Strings for the language debugger JDEBUG_LANGUAGE_FILES_IN_ERROR="تحليل الأخطاء في ملفات اللغة" @@ -256,6 +269,7 @@ MESSAGE="الرسالة" NOTICE="ملاحظة" WARNING="تحذير" ; Javascript ajax error messages +JLIB_JS_AJAX_ERROR_CONNECTION_ABORT="فشل الاتصال عند محاولة الحصول على بيانات JSON." JLIB_JS_AJAX_ERROR_NO_CONTENT="لايمكن استعادة المحتوى." JLIB_JS_AJAX_ERROR_OTHER="حدث خطأ أثناء جلب البيانات JSON: HTTP %s حالة الكود." JLIB_JS_AJAX_ERROR_PARSE="حدث خطأ في تحليل أثناء معالجة البيانات JSON التالية:
%s" @@ -271,4 +285,9 @@ JLIB_FORM_CONTAINS_INVALID_FIELDS="لا يمكن إرسال النموذج لأ JLIB_FORM_FIELD_INVALID_VALUE="هذه القيمة غير صالحة." JLIB_FORM_FIELD_REQUIRED_CHECK="يجب تحديد أحد الخيارات." JLIB_FORM_FIELD_REQUIRED_VALUE="يرجى ملء هذا الحقل." +; Installation progress screen +INSTL="تثبيت" +INSTL_FINISHED="تم التثبيت" +INSTL_IN_PROGRESS="يتم التثبيت" +INSTL_PROGRESS="حالة التثبيت" diff --git a/installation/language/ar-AA/langmetadata.xml b/installation/language/ar-AA/langmetadata.xml index b401c8e9f0a..808f7455b5c 100644 --- a/installation/language/ar-AA/langmetadata.xml +++ b/installation/language/ar-AA/langmetadata.xml @@ -1,8 +1,8 @@ Arabic (اللغة العربية) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Dr. Ashraf Damra (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/be-BY/langmetadata.xml b/installation/language/be-BY/langmetadata.xml index 25ac1602160..5367cca256d 100644 --- a/installation/language/be-BY/langmetadata.xml +++ b/installation/language/be-BY/langmetadata.xml @@ -1,8 +1,8 @@ Belarusian (Belarus) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla Belarus Community (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/bg-BG/langmetadata.xml b/installation/language/bg-BG/langmetadata.xml index 2146a0e491e..5315164f75e 100644 --- a/installation/language/bg-BG/langmetadata.xml +++ b/installation/language/bg-BG/langmetadata.xml @@ -1,8 +1,8 @@ Bulgarian (bg-BG) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Bulgaria (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/ca-ES/langmetadata.xml b/installation/language/ca-ES/langmetadata.xml index c11db551c59..2d1bb5f8bd7 100644 --- a/installation/language/ca-ES/langmetadata.xml +++ b/installation/language/ca-ES/langmetadata.xml @@ -1,8 +1,8 @@ Catalan (ca-ES) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Catalan [ca-ES] Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/cs-CZ/langmetadata.xml b/installation/language/cs-CZ/langmetadata.xml index 2dac1cf26b1..73b5df58f61 100644 --- a/installation/language/cs-CZ/langmetadata.xml +++ b/installation/language/cs-CZ/langmetadata.xml @@ -1,8 +1,8 @@ Czech (Čeština) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Czech Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/cy-GB/langmetadata.xml b/installation/language/cy-GB/langmetadata.xml index 6f70018d7d5..079dbc03349 100644 --- a/installation/language/cy-GB/langmetadata.xml +++ b/installation/language/cy-GB/langmetadata.xml @@ -1,8 +1,8 @@ Welsh (United Kingdom) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project - Welsh Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/da-DK/langmetadata.xml b/installation/language/da-DK/langmetadata.xml index d2684b775f8..61ded4aa4e1 100644 --- a/installation/language/da-DK/langmetadata.xml +++ b/installation/language/da-DK/langmetadata.xml @@ -1,8 +1,8 @@ Danish (Danmark) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Danish Translation Team (Transl.: Ronny Buelund) (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/de-AT/langmetadata.xml b/installation/language/de-AT/langmetadata.xml index 5256f573bfb..b59efab9263 100644 --- a/installation/language/de-AT/langmetadata.xml +++ b/installation/language/de-AT/langmetadata.xml @@ -1,8 +1,8 @@ German (Austria) - 5.2.3 - 2025-01 + 5.3.0 + 2025-04 J!German (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/de-CH/langmetadata.xml b/installation/language/de-CH/langmetadata.xml index 4b6854cae98..5e26938c292 100644 --- a/installation/language/de-CH/langmetadata.xml +++ b/installation/language/de-CH/langmetadata.xml @@ -1,8 +1,8 @@ German (Switzerland) - 5.2.3 - 2025-01 + 5.3.0 + 2025-04 J!German (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/de-DE/langmetadata.xml b/installation/language/de-DE/langmetadata.xml index 3cd4d882526..0b937660985 100644 --- a/installation/language/de-DE/langmetadata.xml +++ b/installation/language/de-DE/langmetadata.xml @@ -1,8 +1,8 @@ German (Germany) - 5.2.3 - 2025-01 + 5.3.0 + 2025-04 J!German (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/de-LI/langmetadata.xml b/installation/language/de-LI/langmetadata.xml index 6ffe9f219a3..6f0cf20019a 100644 --- a/installation/language/de-LI/langmetadata.xml +++ b/installation/language/de-LI/langmetadata.xml @@ -1,8 +1,8 @@ German (Liechtenstein) - 5.2.3 - 2025-01 + 5.3.0 + 2025-04 J!German (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/de-LU/langmetadata.xml b/installation/language/de-LU/langmetadata.xml index f52a1f0754f..c5bf9121bc4 100644 --- a/installation/language/de-LU/langmetadata.xml +++ b/installation/language/de-LU/langmetadata.xml @@ -1,8 +1,8 @@ German (Luxembourg) - 5.2.3 - 2025-01 + 5.3.0 + 2025-04 J!German (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/el-GR/langmetadata.xml b/installation/language/el-GR/langmetadata.xml index bc0754f39d7..ff308ce64c5 100644 --- a/installation/language/el-GR/langmetadata.xml +++ b/installation/language/el-GR/langmetadata.xml @@ -1,8 +1,8 @@ Greek (el-GR) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Ομάδα Μετάφρασης: joomla. gr (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/en-AU/langmetadata.xml b/installation/language/en-AU/langmetadata.xml index 1bd0824d97e..3ec7f1f6e02 100644 --- a/installation/language/en-AU/langmetadata.xml +++ b/installation/language/en-AU/langmetadata.xml @@ -1,8 +1,8 @@ English (Australia) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/en-CA/langmetadata.xml b/installation/language/en-CA/langmetadata.xml index c8c644941da..700c56b6d14 100644 --- a/installation/language/en-CA/langmetadata.xml +++ b/installation/language/en-CA/langmetadata.xml @@ -1,8 +1,8 @@ English (Canada) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/en-GB/langmetadata.xml b/installation/language/en-GB/langmetadata.xml index 17d7e55e574..cf419236355 100644 --- a/installation/language/en-GB/langmetadata.xml +++ b/installation/language/en-GB/langmetadata.xml @@ -1,8 +1,8 @@ English (United Kingdom) - 5.3.0 - 2025-03 + 5.4.0 + 2025-02 Joomla! Project (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/en-NZ/langmetadata.xml b/installation/language/en-NZ/langmetadata.xml index 1af75f1ff5c..2cfc8b64694 100644 --- a/installation/language/en-NZ/langmetadata.xml +++ b/installation/language/en-NZ/langmetadata.xml @@ -1,8 +1,8 @@ English (New Zealand) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/en-US/langmetadata.xml b/installation/language/en-US/langmetadata.xml index a43eafbb924..f96ab2859b7 100644 --- a/installation/language/en-US/langmetadata.xml +++ b/installation/language/en-US/langmetadata.xml @@ -1,8 +1,8 @@ English (United States) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/es-ES/langmetadata.xml b/installation/language/es-ES/langmetadata.xml index 9faa0213958..7b37d69ab88 100644 --- a/installation/language/es-ES/langmetadata.xml +++ b/installation/language/es-ES/langmetadata.xml @@ -1,8 +1,8 @@ Spanish (es-ES) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Spanish [es-ES] Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/et-EE/langmetadata.xml b/installation/language/et-EE/langmetadata.xml index cf3bbf41316..6350deed942 100644 --- a/installation/language/et-EE/langmetadata.xml +++ b/installation/language/et-EE/langmetadata.xml @@ -1,8 +1,8 @@ Estonian - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/eu-ES/langmetadata.xml b/installation/language/eu-ES/langmetadata.xml index 89c4f60a7ff..c14e8cdfe2f 100644 --- a/installation/language/eu-ES/langmetadata.xml +++ b/installation/language/eu-ES/langmetadata.xml @@ -1,8 +1,8 @@ Basque - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Basque Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/fa-AF/langmetadata.xml b/installation/language/fa-AF/langmetadata.xml index d8999fdec68..f6102014492 100644 --- a/installation/language/fa-AF/langmetadata.xml +++ b/installation/language/fa-AF/langmetadata.xml @@ -1,8 +1,8 @@ فارسی (دری) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 JoomlaPersian Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/fa-IR/langmetadata.xml b/installation/language/fa-IR/langmetadata.xml index 20e22f84ecf..43b3babf0c5 100644 --- a/installation/language/fa-IR/langmetadata.xml +++ b/installation/language/fa-IR/langmetadata.xml @@ -1,8 +1,8 @@ Persian (fa-IR) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Persian Translation Team: joomlafarsi.com (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/fi-FI/langmetadata.xml b/installation/language/fi-FI/langmetadata.xml index d7d8a0fc695..1c4528bb792 100644 --- a/installation/language/fi-FI/langmetadata.xml +++ b/installation/language/fi-FI/langmetadata.xml @@ -1,8 +1,8 @@ Finnish (Finland) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Finnish translation team: Joomla.fi (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/fr-CA/langmetadata.xml b/installation/language/fr-CA/langmetadata.xml index ac21d53c32f..c45826f4a73 100644 --- a/installation/language/fr-CA/langmetadata.xml +++ b/installation/language/fr-CA/langmetadata.xml @@ -1,8 +1,8 @@ French (Canada) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project - French translation team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/fr-FR/langmetadata.xml b/installation/language/fr-FR/langmetadata.xml index b50f8e2399c..08e51188e5d 100644 --- a/installation/language/fr-FR/langmetadata.xml +++ b/installation/language/fr-FR/langmetadata.xml @@ -1,8 +1,8 @@ French (fr-FR) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project - French translation team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/he-IL/langmetadata.xml b/installation/language/he-IL/langmetadata.xml index e2ef5f175bf..1c4da43e5ee 100644 --- a/installation/language/he-IL/langmetadata.xml +++ b/installation/language/he-IL/langmetadata.xml @@ -1,8 +1,8 @@ Hebrew (Israel) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 פרוייקט ג'ומלה (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/hr-HR/langmetadata.xml b/installation/language/hr-HR/langmetadata.xml index 5471965159f..207f076bd57 100644 --- a/installation/language/hr-HR/langmetadata.xml +++ b/installation/language/hr-HR/langmetadata.xml @@ -1,8 +1,8 @@ Hrvatski (Hrvatska) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Hrvatska team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/hu-HU/langmetadata.xml b/installation/language/hu-HU/langmetadata.xml index ce47af92537..995271ea896 100644 --- a/installation/language/hu-HU/langmetadata.xml +++ b/installation/language/hu-HU/langmetadata.xml @@ -1,8 +1,8 @@ Hungarian (Magyar) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Magyarország (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/id-ID/langmetadata.xml b/installation/language/id-ID/langmetadata.xml index 81699a8d50c..1db9631c084 100644 --- a/installation/language/id-ID/langmetadata.xml +++ b/installation/language/id-ID/langmetadata.xml @@ -1,8 +1,8 @@ Bahasa Indonesia (id-ID) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Indonesia (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/it-IT/langmetadata.xml b/installation/language/it-IT/langmetadata.xml index cdd5518f7b1..c947012f518 100644 --- a/installation/language/it-IT/langmetadata.xml +++ b/installation/language/it-IT/langmetadata.xml @@ -1,8 +1,8 @@ Italiano (it-IT) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project (Italian Translation Team) (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/ja-JP/langmetadata.xml b/installation/language/ja-JP/langmetadata.xml index 1775a943599..af662f5e09a 100644 --- a/installation/language/ja-JP/langmetadata.xml +++ b/installation/language/ja-JP/langmetadata.xml @@ -1,8 +1,8 @@ Japanese (Japan) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla!じゃぱん (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/ka-GE/langmetadata.xml b/installation/language/ka-GE/langmetadata.xml index bc75c09448f..63b43249db8 100644 --- a/installation/language/ka-GE/langmetadata.xml +++ b/installation/language/ka-GE/langmetadata.xml @@ -1,8 +1,8 @@ Georgian (Georgia) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Georgian Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/kk-KZ/langmetadata.xml b/installation/language/kk-KZ/langmetadata.xml index 383e5cbc895..a6657a1b567 100644 --- a/installation/language/kk-KZ/langmetadata.xml +++ b/installation/language/kk-KZ/langmetadata.xml @@ -1,8 +1,8 @@ Kazakh (Kazakhstan) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Sarvarov Akylkerey (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/ko-KR/langmetadata.xml b/installation/language/ko-KR/langmetadata.xml index bc50e2f1abb..e8cb92464cc 100644 --- a/installation/language/ko-KR/langmetadata.xml +++ b/installation/language/ko-KR/langmetadata.xml @@ -1,8 +1,8 @@ Korean (Republic of Korea) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! 프로젝트 (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/lt-LT/langmetadata.xml b/installation/language/lt-LT/langmetadata.xml index 18366fd3047..c95277f7936 100644 --- a/installation/language/lt-LT/langmetadata.xml +++ b/installation/language/lt-LT/langmetadata.xml @@ -1,8 +1,8 @@ Lietuvių (lt-LT) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Oskaras Jankauskas (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/lv-LV/langmetadata.xml b/installation/language/lv-LV/langmetadata.xml index 612ce0e8912..97a4e3a7da8 100644 --- a/installation/language/lv-LV/langmetadata.xml +++ b/installation/language/lv-LV/langmetadata.xml @@ -1,8 +1,8 @@ Latvian (Latvia) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Projekts (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/mk-MK/langmetadata.xml b/installation/language/mk-MK/langmetadata.xml index 6271f33d2ad..9abff766be8 100644 --- a/installation/language/mk-MK/langmetadata.xml +++ b/installation/language/mk-MK/langmetadata.xml @@ -1,8 +1,8 @@ Macedonian (mk-MK) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/nl-BE/joomla.ini b/installation/language/nl-BE/joomla.ini index 408b93c2cc4..21be651f038 100644 --- a/installation/language/nl-BE/joomla.ini +++ b/installation/language/nl-BE/joomla.ini @@ -6,22 +6,22 @@ ; Fatal error page ; These will be processed by the JavaScript Build BUILD_FATAL_HEADER="Sorry, er deed zich een probleem voor dat we niet konden herstellen." -BUILD_FATAL_LANGUAGE="Engels GB" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR +BUILD_FATAL_LANGUAGE="Vlaams BE" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR BUILD_FATAL_TEXT="De server heeft een \"{{statusCode_statusText}} \" teruggestuurd" BUILD_FATAL_URL_TEXT="Help dit probleem op te lossen" ; These will be processed by the JavaScript Build BUILD_INCOMPLETE_HEADER="Omgeving instellen onvolledig" -BUILD_INCOMPLETE_LANGUAGE="Engels GB" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR +BUILD_INCOMPLETE_LANGUAGE="Vlaams BE" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR BUILD_INCOMPLETE_TEXT="Het lijkt erop dat u probeert Joomla! uit te voeren vanuit onze git repository. Hiervoor is vereist dat u eerst een aantal extra stappen heeft voltooid." BUILD_INCOMPLETE_URL_TEXT="Meer details" ; These will be processed by the JavaScript Build BUILD_NOXML_HEADER="Sorry, uw PHP mist een noodzakelijke library" -BUILD_NOXML_LANGUAGE="Engels GB" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR +BUILD_NOXML_LANGUAGE="Vlaams BE" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR BUILD_NOXML_TEXT="Om deze versie van Joomla! uit te kunnen voeren moet uw host PHP gebruiken met ondersteuning voor de XML-library" BUILD_NOXML_URL_TEXT="Help dit probleem op te lossen" ; These will be processed by the JavaScript Build BUILD_MIN_PHP_ERROR_HEADER="Sorry, uw PHP-versie wordt niet ondersteund." -BUILD_MIN_PHP_ERROR_LANGUAGE="Engels GB" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR +BUILD_MIN_PHP_ERROR_LANGUAGE="Vlaams BE" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example French will be Français FR BUILD_MIN_PHP_ERROR_TEXT="Uw host moet PHP versie {{phpversion}} of nieuwer gebruiken om deze versie van Joomla uit te voeren." BUILD_MIN_PHP_ERROR_URL_TEXT="Help dit probleem op te lossen" ; Main Config @@ -81,13 +81,13 @@ INSTL_DATABASE_NAME_LABEL="Database naam" INSTL_DATABASE_NAME_MSG_MYSQL="De naam van de database is ongeldig. Het mag de volgende tekens NIET bevatten: \ /" INSTL_DATABASE_NAME_MSG_POSTGRES="De naam van de database is ongeldig. Deze moet beginnen met een letter, gevolgd door alfanumerieke tekens." INSTL_DATABASE_NO_SCHEMA="Er bestaat geen database schema voor dit database type." -INSTL_DATABASE_PASSWORD_DESC="Voer het database wachtwoord in dat is aangemaakt of een wachtwoord dat door de host is verstrekt." +INSTL_DATABASE_PASSWORD_DESC="Een wachtwoord dat uzelf heeft aangemaakt, of een wachtwoord dat door de webhost is verstrekt." INSTL_DATABASE_PREFIX_DESC="Een tabel voorvoegsel invoeren of de willekeurig gegenereerde gebruiken." INSTL_DATABASE_PREFIX_DUPLICATE_DESC="Als een bestaande database gebruikt wordt met tabellen met dezelfde voorvoegsel, zal Joomla deze bestaande tabellen hernoemen door het voorvoegsel \"bak_\" toe te voegen." INSTL_DATABASE_PREFIX_MSG="Het voorvoegsel van de tabel moet beginnen met een letter, eventueel gevolgd door alfanumerieke tekens en een onderstrepingsteken" INSTL_DATABASE_RESPONSE_ERROR="Het installatieproces is mislukt." INSTL_DATABASE_TYPE_DESC="Selecteer het databasetype." -INSTL_DATABASE_USER_DESC="Voer de gebruikersnaam in die is aangemaakt of een gebruikersnaam die door de host is verstrekt." +INSTL_DATABASE_USER_DESC="Vul de database gebruikersnaam in die u heeft aangemaakt of een gebruikersnaam die door uw host is verstrekt." INSTL_DATABASE_VALIDATION_ERROR="Controleer uw database inloggegevens, database type, database naam of hostname. Als u MySQL 8 geïnstalleerd heeft, lees dan deze wiki voor meer informatie." INSTL_CONNECT_DB="Databaseverbinding instellen" @@ -111,8 +111,8 @@ INSTL_COMPLETE_ERROR_FOLDER_DELETE="De \"%s\" map kan niet worden verwijderd. Ve INSTL_COMPLETE_REMOVE_FOLDER="Verwijder \"%s\" map" INSTL_COMPLETE_CONGRAT="Gefeliciteerd!" INSTL_COMPLETE_TITLE="Gefeliciteerd! Uw Joomla site staat klaar." -INSTL_COMPLETE_SITE_BTN="Website openen" -INSTL_COMPLETE_ADMIN_BTN="Beheer openen" +INSTL_COMPLETE_SITE_BTN="Voltooien & website openen" +INSTL_COMPLETE_ADMIN_BTN="Voltooien & beheer openen" INSTL_COMPLETE_FINAL="Installatie is voltooid" INSTL_COMPLETE_FINAL_DESC="Uw Joomla installatie is nu voltooid en klaar voor gebruik." INSTL_COMPLETE_ADD_EXTRA_LANGUAGE="Extra talen installeren" @@ -123,8 +123,8 @@ INSTL_LANGUAGES_COLUMN_HEADER_LANGUAGE="Taal" INSTL_LANGUAGES_COLUMN_HEADER_LANGUAGE_SELECT="Selecteer taal" INSTL_LANGUAGES_COLUMN_HEADER_LANGUAGE_TAG="Taal tag" INSTL_LANGUAGES_COLUMN_HEADER_VERSION="Versie" -INSTL_LANGUAGES_DESC="De Joomla-interface is beschikbaar in verschillende talen. Kies de gewenste talen door de selectievakjes aan te vinken en deze vervolgens te installeren door op de knop \"Installeer geselecteerde talen\" te klikken.
Opmerking: Deze bewerking duurt voor elke taal ongeveer 10 seconden om te downloaden en te installeren. Selecteer, om time-outs te voorkomen, niet meer dan 3 talen om te installeren." -INSTL_LANGUAGES_MESSAGE_PLEASE_WAIT="Deze bewerking duurt maximaal 10 seconden per taal om te voltooien
Een ogenblik geduld, terwijl we de talen downloaden en installeren …" +INSTL_LANGUAGES_DESC="De Joomla-interface is beschikbaar in verschillende talen. Kies de gewenste talen door de selectievakjes aan te vinken en deze vervolgens te installeren door op de knop 'Volgende' te klikken.
Opmerking: Deze bewerking duurt voor elke taal ongeveer 10 seconden om te downloaden en te installeren. Om time-outs te voorkomen, selecteert u niet meer dan 3 talen om te installeren." +INSTL_LANGUAGES_MESSAGE_PLEASE_WAIT="Deze bewerking duurt maximaal 10 seconden per taal om te voltooien
Een ogenblik geduld, terwijl we de talen downloaden en installeren ..." INSTL_LANGUAGES_NO_LANGUAGE_SELECTED="Er zijn geen talen geselecteerd om te installeren." INSTL_LANGUAGES_SELECTED="Installeer geselecteerde talen" INSTL_LANGUAGES_WARNING_NO_INTERNET="Joomla! kon niet verbinden met de taalserver. Voltooi het installatieproces." @@ -147,7 +147,7 @@ INSTL_DEFAULTLANGUAGE_FRONTEND_SET_DEFAULT="Joomla heeft %s ingesteld als uw sta INSTL_DEFAULTLANGUAGE_SET_DEFAULT_LANGUAGE="Standaardtaal instellen" INSTL_DEFAULTLANGUAGE_TRY_LATER="U kunt het later installeren met behulp van Joomla beheer." -INSTL_DEFAULTLANGUAGE_NATIVE_LANGUAGE_NAME="Vlaams (BE)" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example Spanish will be Español +INSTL_DEFAULTLANGUAGE_NATIVE_LANGUAGE_NAME="Flemish (BE)" ; IMPORTANT NOTE FOR TRANSLATORS: Do not literally translate this line, instead add the localised name of the language. For example Spanish will be Español ; Database Model INSTL_DATABASE_COULD_NOT_CONNECT="Kan geen verbinding maken met de database. Foutmelding: %s" INSTL_DATABASE_COULD_NOT_CREATE_DATABASE="De installatie kan niet met de gespecificeerde database verbinden en kan geen database aanmaken. Controleer uw instellingen en maak, indien nodig, handmatig de database aan." @@ -211,9 +211,9 @@ JGLOBAL_SELECT_NO_RESULTS_MATCH="Geen overeenkomende resultaten" JGLOBAL_SELECT_SOME_OPTIONS="Selecteer enkele opties" JHIDEPASSWORD="Verberg wachtwoord" JINVALID_TOKEN="Het laatste verzoek is geweigerd omdat het een ongeldig beveiligingstoken bevat. Vernieuw de pagina en probeer het opnieuw." -JINVALID_TOKEN_NOTICE="Het beveiligingstoken komt niet overeen. Het verzoek is geannuleerd om een inbreuk op de beveiliging te voorkomen. Probeer het opnieuw." +JINVALID_TOKEN_NOTICE="Het beveiligingstoken komt niet overeen. Het verzoek werd afgebroken om een inbreuk op de beveiliging te voorkomen. Probeer nogmaals." JNEXT="Volgende" -JNO="Nee" +JNO="Neen" JNOTICE="Opmerking" JOFF="Uit" JON="Aan" @@ -233,7 +233,7 @@ JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER="%1$s: Pad is geen map. Pad: %2$s" JLIB_FORM_FIELD_INVALID="Ongeldig veld: " JLIB_FORM_VALIDATE_FIELD_INVALID="Ongeldig veld: %s" JLIB_FORM_VALIDATE_FIELD_REQUIRED="Vereist veld: %s" -JLIB_INSTALLER_ABORT="Installeren taal afgebroken: %s" +JLIB_INSTALLER_ABORT="De taalinstallatie wordt gestopt: %s" JLIB_INSTALLER_ABORT_CREATE_DIRECTORY="Extensie %1$s: Aanmaken map mislukt: %2$s" JLIB_INSTALLER_ABORT_NOINSTALLPATH="Installatiepad bestaat niet." JLIB_INSTALLER_ABORT_PACK_INSTALL_ERROR_EXTENSION="Pakket %1$s: Er is een fout opgetreden bij het installeren van een extensie: %2$s." @@ -269,7 +269,7 @@ MESSAGE="Bericht" NOTICE="Opmerking" WARNING="Waarschuwing" ; Javascript ajax error messages -JLIB_JS_AJAX_ERROR_CONNECTION_ABORT="Er is een verbindingsfout opgetreden bij het ophalen van de JSON-gegevens." +JLIB_JS_AJAX_ERROR_CONNECTION_ABORT="De verbinding werd afgebroken tijdens het ophalen van de JSON data." JLIB_JS_AJAX_ERROR_NO_CONTENT="Er werd geen inhoud teruggegeven." JLIB_JS_AJAX_ERROR_OTHER="Er deed zich een fout voor tijdens het ophalen van de JSON data: HTTP %d status code." JLIB_JS_AJAX_ERROR_PARSE="Er deed zich een ontledingsfout voor bij het verwerken van de volgende JSON data:
%s" diff --git a/installation/language/nl-BE/langmetadata.xml b/installation/language/nl-BE/langmetadata.xml index 8efd4b97947..ff9658238f0 100644 --- a/installation/language/nl-BE/langmetadata.xml +++ b/installation/language/nl-BE/langmetadata.xml @@ -1,17 +1,17 @@ - Dutch (Belgium) - 5.2.5 - 2025-02 - Dutch (BE) translation team + Vlaams (België) + 5.3.1 + 2025-04 + Vlaams (BE) translation team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt joomla.ini - Dutch (nl-BE) - Dutch (Belgium) + Vlaams (nl-BE) + Vlaams (België) nl-BE 0 diff --git a/installation/language/nl-NL/langmetadata.xml b/installation/language/nl-NL/langmetadata.xml index 656e341d520..4dd3a706e63 100644 --- a/installation/language/nl-NL/langmetadata.xml +++ b/installation/language/nl-NL/langmetadata.xml @@ -1,8 +1,8 @@ Dutch (Netherlands) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Dutch Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/pl-PL/langmetadata.xml b/installation/language/pl-PL/langmetadata.xml index c19cc3983f7..64242edc42c 100644 --- a/installation/language/pl-PL/langmetadata.xml +++ b/installation/language/pl-PL/langmetadata.xml @@ -1,8 +1,8 @@ Polski (PL) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Projekt Joomla! (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/pt-BR/langmetadata.xml b/installation/language/pt-BR/langmetadata.xml index 98219971122..cba2be3b6c2 100644 --- a/installation/language/pt-BR/langmetadata.xml +++ b/installation/language/pt-BR/langmetadata.xml @@ -1,8 +1,8 @@ - Portuguese (Brazil) - 5.2.5 - 2025-02 + Português do Brasil (pt-BR) + 5.3.1 + 2025-04 Projeto Joomla! (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt @@ -10,7 +10,7 @@ joomla.ini - Brazilian Portuguese (pt-BR) + Português do Brasil (pt-BR) Portuguese (Brazil) pt-BR 0 diff --git a/installation/language/pt-PT/langmetadata.xml b/installation/language/pt-PT/langmetadata.xml index 4dce956078d..bffe7c15337 100644 --- a/installation/language/pt-PT/langmetadata.xml +++ b/installation/language/pt-PT/langmetadata.xml @@ -1,8 +1,8 @@ Português (Portugal) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Comunidade JoomlaPortugal (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/ro-RO/langmetadata.xml b/installation/language/ro-RO/langmetadata.xml index 7918508403d..db794d8818e 100644 --- a/installation/language/ro-RO/langmetadata.xml +++ b/installation/language/ro-RO/langmetadata.xml @@ -1,8 +1,8 @@ Română (România) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Horia Negura - Quanta (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/sk-SK/langmetadata.xml b/installation/language/sk-SK/langmetadata.xml index 9a3c8e16425..5a65b19dd28 100644 --- a/installation/language/sk-SK/langmetadata.xml +++ b/installation/language/sk-SK/langmetadata.xml @@ -1,8 +1,8 @@ Slovak (Slovakia) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Slovak translation team : Peter Michnica (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/sl-SI/langmetadata.xml b/installation/language/sl-SI/langmetadata.xml index 1d13f641fed..8a5e502741d 100644 --- a/installation/language/sl-SI/langmetadata.xml +++ b/installation/language/sl-SI/langmetadata.xml @@ -1,8 +1,8 @@ Slovenščina (Slovenija) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Slovenska prevajalska ekipa (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/sr-YU/langmetadata.xml b/installation/language/sr-YU/langmetadata.xml index 00e16afe2ac..65a87e58516 100644 --- a/installation/language/sr-YU/langmetadata.xml +++ b/installation/language/sr-YU/langmetadata.xml @@ -1,8 +1,8 @@ Srpski (Republika Srbija) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Goran Nešić - UIX Web Design & Saša Matić Bardak.RS (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/sv-SE/langmetadata.xml b/installation/language/sv-SE/langmetadata.xml index 25f6dfe3219..2effb8f3590 100644 --- a/installation/language/sv-SE/langmetadata.xml +++ b/installation/language/sv-SE/langmetadata.xml @@ -1,8 +1,8 @@ Swedish (Sweden) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Swedish Translation Team - SvenskJoomla (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/ta-IN/langmetadata.xml b/installation/language/ta-IN/langmetadata.xml index 759bfbf5bf7..9f4cef44118 100644 --- a/installation/language/ta-IN/langmetadata.xml +++ b/installation/language/ta-IN/langmetadata.xml @@ -1,8 +1,8 @@ Tamil (India) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Ilagnayeru 'MIG' Manickam, Elango Samy Manim (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/th-TH/langmetadata.xml b/installation/language/th-TH/langmetadata.xml index 78769637ec7..4932daa8df0 100644 --- a/installation/language/th-TH/langmetadata.xml +++ b/installation/language/th-TH/langmetadata.xml @@ -1,8 +1,8 @@ Thai (ภาษาไทย) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Thai Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/tr-TR/langmetadata.xml b/installation/language/tr-TR/langmetadata.xml index 78aa63b737e..7ef10fddda1 100644 --- a/installation/language/tr-TR/langmetadata.xml +++ b/installation/language/tr-TR/langmetadata.xml @@ -1,8 +1,8 @@ Turkish (Turkey) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Türkiye (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/uk-UA/langmetadata.xml b/installation/language/uk-UA/langmetadata.xml index 1ac1c9cdea7..ae3bb6c1f24 100644 --- a/installation/language/uk-UA/langmetadata.xml +++ b/installation/language/uk-UA/langmetadata.xml @@ -1,8 +1,8 @@ Ukrainian (uk-UA) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Denys Nosov (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/ur-PK/langmetadata.xml b/installation/language/ur-PK/langmetadata.xml index 2f96caf7857..69ddec77372 100644 --- a/installation/language/ur-PK/langmetadata.xml +++ b/installation/language/ur-PK/langmetadata.xml @@ -1,8 +1,8 @@ Urdu (ur-PK) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Urdu Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/vi-VN/langmetadata.xml b/installation/language/vi-VN/langmetadata.xml index 2500b96666b..817dab73f58 100644 --- a/installation/language/vi-VN/langmetadata.xml +++ b/installation/language/vi-VN/langmetadata.xml @@ -1,8 +1,8 @@ Vietnamese (Vietnam) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla! Project (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/zh-CN/langmetadata.xml b/installation/language/zh-CN/langmetadata.xml index f916390d8b5..3c757b4d845 100644 --- a/installation/language/zh-CN/langmetadata.xml +++ b/installation/language/zh-CN/langmetadata.xml @@ -1,8 +1,8 @@ Chinese Simplified (China) - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 Joomla中文网 (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/language/zh-TW/langmetadata.xml b/installation/language/zh-TW/langmetadata.xml index 59ca0e5dee8..089a70ee638 100644 --- a/installation/language/zh-TW/langmetadata.xml +++ b/installation/language/zh-TW/langmetadata.xml @@ -1,8 +1,8 @@ 正體中文 - 5.2.5 - 2025-02 + 5.3.1 + 2025-04 正體中文 Translation Team (C) 2005 Open Source Matters, Inc. GNU General Public License version 2 or later; see LICENSE.txt diff --git a/installation/sql/mysql/base.sql b/installation/sql/mysql/base.sql index db5dc5cff4a..e2cdf5a5a4f 100644 --- a/installation/sql/mysql/base.sql +++ b/installation/sql/mysql/base.sql @@ -180,7 +180,7 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'com_redirect', 'component', 'com_redirect', '', 1, 1, 0, 0, 1, '', '', ''), (0, 'com_users', 'component', 'com_users', '', 1, 1, 0, 1, 1, '', '{"allowUserRegistration":"0","new_usertype":"2","guest_usergroup":"9","sendpassword":"0","useractivation":"2","mail_to_admin":"1","captcha":"","frontend_userparams":"1","site_language":"0","change_login_name":"0","reset_count":"10","reset_time":"1","minimum_length":"12","minimum_integers":"0","minimum_symbols":"0","minimum_uppercase":"0","save_history":"1","history_limit":5,"mailSubjectPrefix":"","mailBodySuffix":""}', ''), (0, 'com_finder', 'component', 'com_finder', '', 1, 1, 0, 0, 1, '', '{"enabled":"0","show_description":"1","description_length":255,"allow_empty_query":"0","show_url":"1","show_autosuggest":"1","show_suggested_query":"1","show_explained_query":"1","show_advanced":"1","show_advanced_tips":"1","expand_advanced":"0","show_date_filters":"0","sort_order":"relevance","sort_direction":"desc","highlight_terms":"1","opensearch_name":"","opensearch_description":"","batch_size":"50","title_multiplier":"1.7","text_multiplier":"0.7","meta_multiplier":"1.2","path_multiplier":"2.0","misc_multiplier":"0.3","stem":"1","stemmer":"snowball","enable_logging":"0"}', ''), -(0, 'com_joomlaupdate', 'component', 'com_joomlaupdate', '', 1, 1, 0, 1, 1, '', '{"updatesource":"default","customurl":""}', ''), +(0, 'com_joomlaupdate', 'component', 'com_joomlaupdate', '', 1, 1, 0, 1, 1, '', '{"updatesource":"default","customurl":"","autoupdate_status":"1","autoupdate":"1","minimum_stability":"4"}', ''), (0, 'com_tags', 'component', 'com_tags', '', 1, 1, 1, 0, 1, '', '{"tag_layout":"_:default","save_history":"1","history_limit":5,"show_tag_title":"0","tag_list_show_tag_image":"0","tag_list_show_tag_description":"0","tag_list_image":"","tag_list_orderby":"title","tag_list_orderby_direction":"ASC","show_headings":"0","tag_list_show_date":"0","tag_list_show_item_image":"0","tag_list_show_item_description":"0","tag_list_item_maximum_characters":0,"return_any_or_all":"1","include_children":"0","maximum":200,"tag_list_language_filter":"all","tags_layout":"_:default","all_tags_orderby":"title","all_tags_orderby_direction":"ASC","all_tags_show_tag_image":"0","all_tags_show_tag_description":"0","all_tags_tag_maximum_characters":20,"all_tags_show_tag_hits":"0","filter_field":"1","show_pagination_limit":"1","show_pagination":"2","show_pagination_results":"1","tag_field_ajax_mode":"1","show_feed_link":"1"}', ''), (0, 'com_contenthistory', 'component', 'com_contenthistory', '', 1, 1, 1, 0, 1, '', '', ''), (0, 'com_ajax', 'component', 'com_ajax', '', 1, 1, 1, 1, 1, '', '', ''), @@ -263,8 +263,9 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'plg_authentication_joomla', 'plugin', 'joomla', 'authentication', 0, 1, 1, 1, 1, '', '', '', 2, 0), (0, 'plg_authentication_ldap', 'plugin', 'ldap', 'authentication', 0, 0, 1, 0, 1, '', '{"host":"","port":"389","use_ldapV3":"0","negotiate_tls":"0","no_referrals":"0","auth_method":"bind","base_dn":"","search_string":"","users_dn":"","username":"admin","password":"bobby7","ldap_fullname":"fullName","ldap_email":"mail","ldap_uid":"uid"}', '', 3, 0), (0, 'plg_behaviour_compat', 'plugin', 'compat', 'behaviour', 0, 1, 1, 0, 1, '', '{"classes_aliases":"1","es5_assets":"1"}', '', 1, 0), -(0, 'plg_behaviour_taggable', 'plugin', 'taggable', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', 2, 0), -(0, 'plg_behaviour_versionable', 'plugin', 'versionable', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), +(0, 'plg_behaviour_compat6', 'plugin', 'compat6', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', 2, 0), +(0, 'plg_behaviour_taggable', 'plugin', 'taggable', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), +(0, 'plg_behaviour_versionable', 'plugin', 'versionable', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), (0, 'plg_content_confirmconsent', 'plugin', 'confirmconsent', 'content', 0, 0, 1, 0, 1, '', '{}', '', 1, 0), (0, 'plg_content_contact', 'plugin', 'contact', 'content', 0, 1, 1, 0, 1, '', '', '', 2, 0), (0, 'plg_content_emailcloak', 'plugin', 'emailcloak', 'content', 0, 1, 1, 0, 1, '', '{"mode":"1"}', '', 3, 0), @@ -326,13 +327,14 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'plg_privacy_content', 'plugin', 'content', 'privacy', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), (0, 'plg_privacy_message', 'plugin', 'message', 'privacy', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), (0, 'plg_privacy_user', 'plugin', 'user', 'privacy', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), -(0, 'plg_quickicon_joomlaupdate', 'plugin', 'joomlaupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 1, 0), -(0, 'plg_quickicon_extensionupdate', 'plugin', 'extensionupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 2, 0), -(0, 'plg_quickicon_overridecheck', 'plugin', 'overridecheck', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 3, 0), -(0, 'plg_quickicon_downloadkey', 'plugin', 'downloadkey', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 4, 0), -(0, 'plg_quickicon_privacycheck', 'plugin', 'privacycheck', 'quickicon', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), -(0, 'plg_quickicon_phpversioncheck', 'plugin', 'phpversioncheck', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 6, 0), -(0, 'plg_quickicon_eos', 'plugin', 'eos', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 7, 0), +(0, 'plg_quickicon_autoupdate', 'plugin', 'autoupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 1, 0), +(0, 'plg_quickicon_joomlaupdate', 'plugin', 'joomlaupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 2, 0), +(0, 'plg_quickicon_extensionupdate', 'plugin', 'extensionupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 3, 0), +(0, 'plg_quickicon_overridecheck', 'plugin', 'overridecheck', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 4, 0), +(0, 'plg_quickicon_downloadkey', 'plugin', 'downloadkey', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 5, 0), +(0, 'plg_quickicon_privacycheck', 'plugin', 'privacycheck', 'quickicon', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), +(0, 'plg_quickicon_phpversioncheck', 'plugin', 'phpversioncheck', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 7, 0), +(0, 'plg_quickicon_eos', 'plugin', 'eos', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 8, 0), (0, 'plg_sampledata_blog', 'plugin', 'blog', 'sampledata', 0, 1, 1, 0, 1, '', '', '', 1, 0), (0, 'plg_sampledata_multilang', 'plugin', 'multilang', 'sampledata', 0, 1, 1, 0, 1, '', '', '', 2, 0), (0, 'plg_schemaorg_article', 'plugin', 'article', 'schemaorg', 0, 1, 1, 0, 0, '', '{}', '', 1, 0), @@ -391,18 +393,19 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'plg_webservices_contact', 'plugin', 'contact', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), (0, 'plg_webservices_content', 'plugin', 'content', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), (0, 'plg_webservices_installer', 'plugin', 'installer', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), -(0, 'plg_webservices_languages', 'plugin', 'languages', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), -(0, 'plg_webservices_media', 'plugin', 'media', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 7, 0), -(0, 'plg_webservices_menus', 'plugin', 'menus', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 7, 0), -(0, 'plg_webservices_messages', 'plugin', 'messages', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 8, 0), -(0, 'plg_webservices_modules', 'plugin', 'modules', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 9, 0), -(0, 'plg_webservices_newsfeeds', 'plugin', 'newsfeeds', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 10, 0), -(0, 'plg_webservices_plugins', 'plugin', 'plugins', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 11, 0), -(0, 'plg_webservices_privacy', 'plugin', 'privacy', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 12, 0), -(0, 'plg_webservices_redirect', 'plugin', 'redirect', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 13, 0), -(0, 'plg_webservices_tags', 'plugin', 'tags', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 14, 0), -(0, 'plg_webservices_templates', 'plugin', 'templates', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 15, 0), -(0, 'plg_webservices_users', 'plugin', 'users', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 16, 0), +(0, 'plg_webservices_joomlaupdate', 'plugin', 'joomlaupdate', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), +(0, 'plg_webservices_languages', 'plugin', 'languages', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 7, 0), +(0, 'plg_webservices_media', 'plugin', 'media', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 8, 0), +(0, 'plg_webservices_menus', 'plugin', 'menus', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 9, 0), +(0, 'plg_webservices_messages', 'plugin', 'messages', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 10, 0), +(0, 'plg_webservices_modules', 'plugin', 'modules', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 11, 0), +(0, 'plg_webservices_newsfeeds', 'plugin', 'newsfeeds', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 12, 0), +(0, 'plg_webservices_plugins', 'plugin', 'plugins', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 13, 0), +(0, 'plg_webservices_privacy', 'plugin', 'privacy', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 14, 0), +(0, 'plg_webservices_redirect', 'plugin', 'redirect', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 15, 0), +(0, 'plg_webservices_tags', 'plugin', 'tags', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 16, 0), +(0, 'plg_webservices_templates', 'plugin', 'templates', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 17, 0), +(0, 'plg_webservices_users', 'plugin', 'users', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 18, 0), (0, 'plg_workflow_featuring', 'plugin', 'featuring', 'workflow', 0, 1, 1, 0, 1, '', '{}', '', 1, 0), (0, 'plg_workflow_notification', 'plugin', 'notification', 'workflow', 0, 1, 1, 0, 1, '', '{}', '', 2, 0), (0, 'plg_workflow_publishing', 'plugin', 'publishing', 'workflow', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), diff --git a/installation/sql/mysql/supports.sql b/installation/sql/mysql/supports.sql index bf0a661b0ba..394aded917d 100644 --- a/installation/sql/mysql/supports.sql +++ b/installation/sql/mysql/supports.sql @@ -278,6 +278,8 @@ INSERT INTO `#__postinstall_messages` (`extension_id`, `title_key`, `description SELECT `extension_id`, 'PLG_SYSTEM_HTTPHEADERS_POSTINSTALL_INTRODUCTION_TITLE', 'PLG_SYSTEM_HTTPHEADERS_POSTINSTALL_INTRODUCTION_BODY', 'PLG_SYSTEM_HTTPHEADERS_POSTINSTALL_INTRODUCTION_ACTION', 'plg_system_httpheaders', 1, 'action', 'site://plugins/system/httpheaders/postinstall/introduction.php', 'httpheaders_postinstall_action', 'site://plugins/system/httpheaders/postinstall/introduction.php', 'httpheaders_postinstall_condition', '4.0.0', 1 FROM `#__extensions` WHERE `name` = 'files_joomla'; INSERT INTO `#__postinstall_messages` (`extension_id`, `title_key`, `description_key`, `action_key`, `language_extension`, `language_client_id`, `type`, `action_file`, `action`, `condition_file`, `condition_method`, `version_introduced`, `enabled`) SELECT `extension_id`, 'COM_USERS_POSTINSTALL_MULTIFACTORAUTH_TITLE', 'COM_USERS_POSTINSTALL_MULTIFACTORAUTH_BODY', 'COM_USERS_POSTINSTALL_MULTIFACTORAUTH_ACTION', 'com_users', 1, 'action', 'admin://components/com_users/postinstall/multifactorauth.php', 'com_users_postinstall_mfa_action', 'admin://components/com_users/postinstall/multifactorauth.php', 'com_users_postinstall_mfa_condition', '4.2.0', 1 FROM `#__extensions` WHERE `name` = 'files_joomla'; +INSERT INTO `#__postinstall_messages` (`extension_id`, `title_key`, `description_key`, `action_key`, `language_extension`, `language_client_id`, `type`, `action_file`, `action`, `condition_file`, `condition_method`, `version_introduced`, `enabled`) +SELECT `extension_id`, 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_TITLE', 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_DESCRIPTION', 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_ACTION', 'com_joomlaupdate', 1, 'action', 'admin://components/com_joomlaupdate/postinstall/autoupdate.php', 'com_joomlaupdate_postinstall_autoupdate_action', 'admin://components/com_joomlaupdate/postinstall/autoupdate.php', 'com_joomlaupdate_postinstall_autoupdate_condition', '5.4.0', 1 FROM `#__extensions` WHERE `name` = 'files_joomla'; -- -------------------------------------------------------- @@ -414,6 +416,8 @@ INSERT INTO `#__mail_templates` (`template_id`, `extension`, `language`, `subjec ('com_users.massmail.mail', 'com_users', '', 'COM_USERS_MASSMAIL_MAIL_SUBJECT', 'COM_USERS_MASSMAIL_MAIL_BODY', '', '', '{"tags":["subject","body","subjectprefix","bodysuffix"]}'), ('com_users.password_reset', 'com_users', '', 'COM_USERS_EMAIL_PASSWORD_RESET_SUBJECT', 'COM_USERS_EMAIL_PASSWORD_RESET_BODY', '', '', '{"tags":["name","email","sitename","link_text","link_html","token"]}'), ('com_users.reminder', 'com_users','', 'COM_USERS_EMAIL_USERNAME_REMINDER_SUBJECT', 'COM_USERS_EMAIL_USERNAME_REMINDER_BODY', '', '', '{"tags":["name","username","sitename","email","link_text","link_html"]}'), +('com_joomlaupdate.update.success', 'com_joomlaupdate', '', 'COM_JOOMLAUPDATE_UPDATE_SUCCESS_MAIL_SUBJECT', 'COM_JOOMLAUPDATE_UPDATE_SUCCESS_MAIL_BODY', '', '', '{"tags":["newversion","oldversion","sitename","url"]}'), +('com_joomlaupdate.update.failed', 'com_joomlaupdate', '', 'COM_JOOMLAUPDATE_UPDATE_FAILED_MAIL_SUBJECT', 'COM_JOOMLAUPDATE_UPDATE_FAILED_MAIL_BODY', '', '', '{"tags":["newversion","oldversion","sitename","url"]}'), ('plg_task_updatenotification.mail', 'plg_task_updatenotification', '', 'PLG_TASK_UPDATENOTIFICATION_EMAIL_SUBJECT', 'PLG_TASK_UPDATENOTIFICATION_EMAIL_BODY', '', '', '{"tags":["newversion","curversion","sitename","url","link","releasenews"]}'), ('plg_user_joomla.mail', 'plg_user_joomla', '', 'PLG_USER_JOOMLA_NEW_USER_EMAIL_SUBJECT', 'PLG_USER_JOOMLA_NEW_USER_EMAIL_BODY', '', '', '{"tags":["name","sitename","url","username","password","email"]}'), ('com_actionlogs.notification', 'com_actionlogs', '', 'COM_ACTIONLOGS_EMAIL_SUBJECT', 'COM_ACTIONLOGS_EMAIL_BODY', 'COM_ACTIONLOGS_EMAIL_HTMLBODY', '', '{"tags":["messages","message","date","extension","username"]}'), diff --git a/installation/sql/postgresql/base.sql b/installation/sql/postgresql/base.sql index 9a168811102..7e3223aa460 100644 --- a/installation/sql/postgresql/base.sql +++ b/installation/sql/postgresql/base.sql @@ -186,7 +186,7 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'com_redirect', 'component', 'com_redirect', '', 1, 1, 0, 0, 1, '', '', '', 0, 0), (0, 'com_users', 'component', 'com_users', '', 1, 1, 0, 1, 1, '', '{"allowUserRegistration":"0","new_usertype":"2","guest_usergroup":"9","sendpassword":"0","useractivation":"2","mail_to_admin":"1","captcha":"","frontend_userparams":"1","site_language":"0","change_login_name":"0","reset_count":"10","reset_time":"1","minimum_length":"12","minimum_integers":"0","minimum_symbols":"0","minimum_uppercase":"0","save_history":"1","history_limit":5,"mailSubjectPrefix":"","mailBodySuffix":""}', '', 0, 0), (0, 'com_finder', 'component', 'com_finder', '', 1, 1, 0, 0, 1, '', '{"enabled":"0","show_description":"1","description_length":255,"allow_empty_query":"0","show_url":"1","show_autosuggest":"1","show_suggested_query":"1","show_explained_query":"1","show_advanced":"1","show_advanced_tips":"1","expand_advanced":"0","show_date_filters":"0","sort_order":"relevance","sort_direction":"desc","highlight_terms":"1","opensearch_name":"","opensearch_description":"","batch_size":"50","title_multiplier":"1.7","text_multiplier":"0.7","meta_multiplier":"1.2","path_multiplier":"2.0","misc_multiplier":"0.3","stem":"1","stemmer":"snowball","enable_logging":"0"}', '', 0, 0), -(0, 'com_joomlaupdate', 'component', 'com_joomlaupdate', '', 1, 1, 0, 1, 1, '', '{"updatesource":"default","customurl":""}', '', 0, 0), +(0, 'com_joomlaupdate', 'component', 'com_joomlaupdate', '', 1, 1, 0, 1, 1, '', '{"updatesource":"default","customurl":"","autoupdate_status":"1","autoupdate":"1","minimum_stability":"4"}', '', 0, 0), (0, 'com_tags', 'component', 'com_tags', '', 1, 1, 1, 0, 1, '', '{"tag_layout":"_:default","save_history":"1","history_limit":5,"show_tag_title":"0","tag_list_show_tag_image":"0","tag_list_show_tag_description":"0","tag_list_image":"","tag_list_orderby":"title","tag_list_orderby_direction":"ASC","show_headings":"0","tag_list_show_date":"0","tag_list_show_item_image":"0","tag_list_show_item_description":"0","tag_list_item_maximum_characters":0,"return_any_or_all":"1","include_children":"0","maximum":200,"tag_list_language_filter":"all","tags_layout":"_:default","all_tags_orderby":"title","all_tags_orderby_direction":"ASC","all_tags_show_tag_image":"0","all_tags_show_tag_description":"0","all_tags_tag_maximum_characters":20,"all_tags_show_tag_hits":"0","filter_field":"1","show_pagination_limit":"1","show_pagination":"2","show_pagination_results":"1","tag_field_ajax_mode":"1","show_feed_link":"1"}', '', 0, 0), (0, 'com_contenthistory', 'component', 'com_contenthistory', '', 1, 1, 1, 0, 1, '', '', '', 0, 0), (0, 'com_ajax', 'component', 'com_ajax', '', 1, 1, 1, 1, 1, '', '', '', 0, 0), @@ -269,8 +269,9 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'plg_authentication_joomla', 'plugin', 'joomla', 'authentication', 0, 1, 1, 1, 1, '', '', '', 2, 0), (0, 'plg_authentication_ldap', 'plugin', 'ldap', 'authentication', 0, 0, 1, 0, 1, '', '{"host":"","port":"389","use_ldapV3":"0","negotiate_tls":"0","no_referrals":"0","auth_method":"bind","base_dn":"","search_string":"","users_dn":"","username":"admin","password":"bobby7","ldap_fullname":"fullName","ldap_email":"mail","ldap_uid":"uid"}', '', 3, 0), (0, 'plg_behaviour_compat', 'plugin', 'compat', 'behaviour', 0, 1, 1, 0, 1, '', '{"classes_aliases":"1","es5_assets":"1"}', '', 1, 0), -(0, 'plg_behaviour_taggable', 'plugin', 'taggable', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', 2, 0), -(0, 'plg_behaviour_versionable', 'plugin', 'versionable', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), +(0, 'plg_behaviour_compat6', 'plugin', 'compat6', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', 2, 0), +(0, 'plg_behaviour_taggable', 'plugin', 'taggable', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), +(0, 'plg_behaviour_versionable', 'plugin', 'versionable', 'behaviour', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), (0, 'plg_content_confirmconsent', 'plugin', 'confirmconsent', 'content', 0, 0, 1, 0, 1, '', '{}', '', 1, 0), (0, 'plg_content_contact', 'plugin', 'contact', 'content', 0, 1, 1, 0, 1, '', '', '', 2, 0), (0, 'plg_content_emailcloak', 'plugin', 'emailcloak', 'content', 0, 1, 1, 0, 1, '', '{"mode":"1"}', '', 3, 0), @@ -332,13 +333,14 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'plg_privacy_content', 'plugin', 'content', 'privacy', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), (0, 'plg_privacy_message', 'plugin', 'message', 'privacy', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), (0, 'plg_privacy_user', 'plugin', 'user', 'privacy', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), -(0, 'plg_quickicon_joomlaupdate', 'plugin', 'joomlaupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 1, 0), -(0, 'plg_quickicon_extensionupdate', 'plugin', 'extensionupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 2, 0), -(0, 'plg_quickicon_overridecheck', 'plugin', 'overridecheck', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 3, 0), -(0, 'plg_quickicon_downloadkey', 'plugin', 'downloadkey', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 4, 0), -(0, 'plg_quickicon_privacycheck', 'plugin', 'privacycheck', 'quickicon', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), -(0, 'plg_quickicon_phpversioncheck', 'plugin', 'phpversioncheck', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 6, 0), -(0, 'plg_quickicon_eos', 'plugin', 'eos', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 7, 0), +(0, 'plg_quickicon_autoupdate', 'plugin', 'autoupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 1, 0), +(0, 'plg_quickicon_joomlaupdate', 'plugin', 'joomlaupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 2, 0), +(0, 'plg_quickicon_extensionupdate', 'plugin', 'extensionupdate', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 3, 0), +(0, 'plg_quickicon_overridecheck', 'plugin', 'overridecheck', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 4, 0), +(0, 'plg_quickicon_downloadkey', 'plugin', 'downloadkey', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 5, 0), +(0, 'plg_quickicon_privacycheck', 'plugin', 'privacycheck', 'quickicon', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), +(0, 'plg_quickicon_phpversioncheck', 'plugin', 'phpversioncheck', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 7, 0), +(0, 'plg_quickicon_eos', 'plugin', 'eos', 'quickicon', 0, 1, 1, 0, 1, '', '', '', 8, 0), (0, 'plg_sampledata_blog', 'plugin', 'blog', 'sampledata', 0, 1, 1, 0, 1, '', '', '', 1, 0), (0, 'plg_sampledata_multilang', 'plugin', 'multilang', 'sampledata', 0, 1, 1, 0, 1, '', '', '', 2, 0), (0, 'plg_schemaorg_article', 'plugin', 'article', 'schemaorg', 0, 1, 1, 0, 0, '', '{}', '', 1, 0), @@ -397,18 +399,19 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'plg_webservices_contact', 'plugin', 'contact', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), (0, 'plg_webservices_content', 'plugin', 'content', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 4, 0), (0, 'plg_webservices_installer', 'plugin', 'installer', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 5, 0), -(0, 'plg_webservices_languages', 'plugin', 'languages', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), -(0, 'plg_webservices_media', 'plugin', 'media', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 7, 0), -(0, 'plg_webservices_menus', 'plugin', 'menus', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 7, 0), -(0, 'plg_webservices_messages', 'plugin', 'messages', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 8, 0), -(0, 'plg_webservices_modules', 'plugin', 'modules', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 9, 0), -(0, 'plg_webservices_newsfeeds', 'plugin', 'newsfeeds', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 10, 0), -(0, 'plg_webservices_plugins', 'plugin', 'plugins', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 11, 0), -(0, 'plg_webservices_privacy', 'plugin', 'privacy', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 12, 0), -(0, 'plg_webservices_redirect', 'plugin', 'redirect', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 13, 0), -(0, 'plg_webservices_tags', 'plugin', 'tags', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 14, 0), -(0, 'plg_webservices_templates', 'plugin', 'templates', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 15, 0), -(0, 'plg_webservices_users', 'plugin', 'users', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 16, 0), +(0, 'plg_webservices_joomlaupdate', 'plugin', 'joomlaupdate', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 6, 0), +(0, 'plg_webservices_languages', 'plugin', 'languages', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 7, 0), +(0, 'plg_webservices_media', 'plugin', 'media', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 8, 0), +(0, 'plg_webservices_menus', 'plugin', 'menus', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 9, 0), +(0, 'plg_webservices_messages', 'plugin', 'messages', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 10, 0), +(0, 'plg_webservices_modules', 'plugin', 'modules', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 11, 0), +(0, 'plg_webservices_newsfeeds', 'plugin', 'newsfeeds', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 12, 0), +(0, 'plg_webservices_plugins', 'plugin', 'plugins', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 13, 0), +(0, 'plg_webservices_privacy', 'plugin', 'privacy', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 14, 0), +(0, 'plg_webservices_redirect', 'plugin', 'redirect', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 15, 0), +(0, 'plg_webservices_tags', 'plugin', 'tags', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 16, 0), +(0, 'plg_webservices_templates', 'plugin', 'templates', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 17, 0), +(0, 'plg_webservices_users', 'plugin', 'users', 'webservices', 0, 1, 1, 0, 1, '', '{}', '', 18, 0), (0, 'plg_workflow_featuring', 'plugin', 'featuring', 'workflow', 0, 1, 1, 0, 1, '', '{}', '', 1, 0), (0, 'plg_workflow_notification', 'plugin', 'notification', 'workflow', 0, 1, 1, 0, 1, '', '{}', '', 2, 0), (0, 'plg_workflow_publishing', 'plugin', 'publishing', 'workflow', 0, 1, 1, 0, 1, '', '{}', '', 3, 0), diff --git a/installation/sql/postgresql/extensions.sql b/installation/sql/postgresql/extensions.sql index 6271c6cf4db..2aac95993f3 100644 --- a/installation/sql/postgresql/extensions.sql +++ b/installation/sql/postgresql/extensions.sql @@ -838,7 +838,7 @@ INSERT INTO "#__action_log_config" ("id", "type_title", "type_alias", "id_holder (23, 'contact', 'com_contact.form', 'id', 'name', '#__contact_details', 'PLG_ACTIONLOG_JOOMLA'); -SELECT setval('#__action_log_config_id_seq', 23, false); +SELECT setval('#__action_log_config_id_seq', 24, false); -- -- Table structure for table `#__action_logs_users` diff --git a/installation/sql/postgresql/supports.sql b/installation/sql/postgresql/supports.sql index 1478ef3ce25..83b134a42bf 100644 --- a/installation/sql/postgresql/supports.sql +++ b/installation/sql/postgresql/supports.sql @@ -289,6 +289,8 @@ INSERT INTO "#__postinstall_messages" ("extension_id", "title_key", "description SELECT "extension_id", 'PLG_SYSTEM_HTTPHEADERS_POSTINSTALL_INTRODUCTION_TITLE', 'PLG_SYSTEM_HTTPHEADERS_POSTINSTALL_INTRODUCTION_BODY', 'PLG_SYSTEM_HTTPHEADERS_POSTINSTALL_INTRODUCTION_ACTION', 'plg_system_httpheaders', 1, 'action', 'site://plugins/system/httpheaders/postinstall/introduction.php', 'httpheaders_postinstall_action', 'site://plugins/system/httpheaders/postinstall/introduction.php', 'httpheaders_postinstall_condition', '4.0.0', 1 FROM "#__extensions" WHERE "name" = 'files_joomla'; INSERT INTO "#__postinstall_messages" ("extension_id", "title_key", "description_key", "action_key", "language_extension", "language_client_id", "type", "action_file", "action", "condition_file", "condition_method", "version_introduced", "enabled") SELECT "extension_id", 'COM_USERS_POSTINSTALL_MULTIFACTORAUTH_TITLE', 'COM_USERS_POSTINSTALL_MULTIFACTORAUTH_BODY', 'COM_USERS_POSTINSTALL_MULTIFACTORAUTH_ACTION', 'com_users', 1, 'action', 'admin://components/com_users/postinstall/multifactorauth.php', 'com_users_postinstall_mfa_action', 'admin://components/com_users/postinstall/multifactorauth.php', 'com_users_postinstall_mfa_condition', '4.2.0', 1 FROM "#__extensions" WHERE "name" = 'files_joomla'; +INSERT INTO "#__postinstall_messages" ("extension_id", "title_key", "description_key", "action_key", "language_extension", "language_client_id", "type", "action_file", "action", "condition_file", "condition_method", "version_introduced", "enabled") +SELECT "extension_id", 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_TITLE', 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_DESCRIPTION', 'COM_JOOMLAUPDATE_POSTINSTALL_MSG_AUTOMATED_UPDATES_ACTION', 'com_joomlaupdate', 1, 'action', 'admin://components/com_joomlaupdate/postinstall/autoupdate.php', 'com_joomlaupdate_postinstall_autoupdate_action', 'admin://components/com_joomlaupdate/postinstall/autoupdate.php', 'com_joomlaupdate_postinstall_autoupdate_condition', '5.4.0', 1 FROM "#__extensions" WHERE "name" = 'files_joomla'; -- -- Table structure for table `#__ucm_base` @@ -425,6 +427,8 @@ INSERT INTO "#__mail_templates" ("template_id", "extension", "language", "subjec ('com_users.massmail.mail', 'com_users', '', 'COM_USERS_MASSMAIL_MAIL_SUBJECT', 'COM_USERS_MASSMAIL_MAIL_BODY', '', '', '{"tags":["subject","body","subjectprefix","bodysuffix"]}'), ('com_users.password_reset', 'com_users', '', 'COM_USERS_EMAIL_PASSWORD_RESET_SUBJECT', 'COM_USERS_EMAIL_PASSWORD_RESET_BODY', '', '', '{"tags":["name","email","sitename","link_text","link_html","token"]}'), ('com_users.reminder', 'com_users', '', 'COM_USERS_EMAIL_USERNAME_REMINDER_SUBJECT', 'COM_USERS_EMAIL_USERNAME_REMINDER_BODY', '', '', '{"tags":["name","username","sitename","email","link_text","link_html"]}'), +('com_joomlaupdate.update.success', 'com_joomlaupdate', '', 'COM_JOOMLAUPDATE_UPDATE_SUCCESS_MAIL_SUBJECT', 'COM_JOOMLAUPDATE_UPDATE_SUCCESS_MAIL_BODY', '', '', '{"tags":["newversion","oldversion","sitename","url"]}'), +('com_joomlaupdate.update.failed', 'com_joomlaupdate', '', 'COM_JOOMLAUPDATE_UPDATE_FAILED_MAIL_SUBJECT', 'COM_JOOMLAUPDATE_UPDATE_FAILED_MAIL_BODY', '', '', '{"tags":["newversion","oldversion","sitename","url"]}'), ('plg_task_updatenotification.mail', 'plg_task_updatenotification', '', 'PLG_TASK_UPDATENOTIFICATION_EMAIL_SUBJECT', 'PLG_TASK_UPDATENOTIFICATION_EMAIL_BODY', '', '', '{"tags":["newversion","curversion","sitename","url","link","releasenews"]}'), ('plg_user_joomla.mail', 'plg_user_joomla', '', 'PLG_USER_JOOMLA_NEW_USER_EMAIL_SUBJECT', 'PLG_USER_JOOMLA_NEW_USER_EMAIL_BODY', '', '', '{"tags":["name","sitename","url","username","password","email"]}'), ('com_actionlogs.notification', 'com_actionlogs', '', 'COM_ACTIONLOGS_EMAIL_SUBJECT', 'COM_ACTIONLOGS_EMAIL_BODY', 'COM_ACTIONLOGS_EMAIL_HTMLBODY', '', '{"tags":["messages","message","date","extension","username"]}'), diff --git a/installation/src/Console/InstallCommand.php b/installation/src/Console/InstallCommand.php index 9ba642657c9..fd800069520 100644 --- a/installation/src/Console/InstallCommand.php +++ b/installation/src/Console/InstallCommand.php @@ -379,7 +379,7 @@ protected function getStringFromOption($option, $question, FormField $field): st // We don't have a CLI option and now interactively get that from the user. while (\is_null($answer) || $answer === false) { - if (\in_array($option, ['admin-password', 'db-pass', 'public_folder'])) { + if (\in_array($option, ['admin-password', 'db-pass'])) { $answer = $this->ioStyle->askHidden($question); } else { $answer = $this->ioStyle->ask( diff --git a/installation/src/Model/ConfigurationModel.php b/installation/src/Model/ConfigurationModel.php index fae7afc5a36..915d7b469b4 100644 --- a/installation/src/Model/ConfigurationModel.php +++ b/installation/src/Model/ConfigurationModel.php @@ -403,6 +403,12 @@ public function createConfiguration($options) // Locale settings. $registry->set('offset', 'UTC'); + // CORS settings. + $registry->set('cors', false); + $registry->set('cors_allow_origin', '*'); + $registry->set('cors_allow_methods', ''); + $registry->set('cors_allow_headers', 'Content-Type,X-Joomla-Token'); + // Mail settings. $registry->set('mailonline', true); $registry->set('mailer', 'mail'); diff --git a/installation/template/scss/template-rtl.scss b/installation/template/scss/template-rtl.scss index 84a6c058d05..745b30e4b94 100644 --- a/installation/template/scss/template-rtl.scss +++ b/installation/template/scss/template-rtl.scss @@ -1,91 +1,2 @@ +// This file is a placeholder for the rtl template. That means it is required and should not be deleted. @import "template"; - -body { - direction: rtl; -} - -// Javascript Warning -#javascript-warning { - font-size: 16px; - text-align: center; -} - -textarea { - resize: both; -} - -textarea.vert { - resize: vertical; -} - -textarea.noResize { - resize: none; -} - -.j-install-step { - select { - width: 100%; - margin-right: 0 !important; - } -} - -.j-install-step-header { - span { - margin-right: auto; - margin-left: 5px; - } -} - -.lang-select { - left: 0; -} - -.control-group { - .control-label { - padding-top: 5px; - padding-right: 5px; - text-align: right; - } -} - -.checkboxes { - .checkbox input { - margin-right: 0; - } -} - -.form-group { - text-align: right; -} - -.container-installation { - text-align: right; -} - -.j-header-help { - margin: 20px auto 20px 20px; -} - -.j-footer { - text-align: left; -} - -.#{$jicon-css-prefix}-chevron-right::before, -.#{$fa-css-prefix}-chevron-right::before { - content: ""; -} - -.j-install-last-step [class^="icon-"], -.j-install-last-step [class*=" icon-"] { - display: inline; -} - -#jform_db_prefix { - text-align: right; - direction: ltr; -} - -// Language Table -caption { - text-align: right; -} diff --git a/installation/template/scss/template.scss b/installation/template/scss/template.scss index cb11435d1ea..a85614c2bb3 100644 --- a/installation/template/scss/template.scss +++ b/installation/template/scss/template.scss @@ -260,7 +260,7 @@ body { select { width: 100%; - margin-left: 0 !important; + margin-inline-start: 0 !important; } } @@ -275,7 +275,7 @@ body { span { position: relative; - margin-right: 5px; + margin-inline-end: 5px; font-size: 1.2rem; } } @@ -380,8 +380,8 @@ label { .control-label { padding-top: 5px; - padding-right: 5px; - text-align: left; + padding-inline-end: 5px; + text-align: start; } } @@ -407,7 +407,7 @@ legend { .checkbox input { position: static; - margin-left: 0; + margin-inline-start: 0; } } @@ -417,6 +417,13 @@ legend { margin-bottom: 0; } +/* rtl:begin:ignore */ +#jform_db_prefix { + text-align: start; + direction: ltr; +} +/* rtl:end:ignore */ + // Possibly temporary until Bootstrap support it themselves .form-select[multiple] { height: auto; diff --git a/installation/tmpl/remove/default.php b/installation/tmpl/remove/default.php index 14268f908e7..09766c71a5d 100644 --- a/installation/tmpl/remove/default.php +++ b/installation/tmpl/remove/default.php @@ -9,12 +9,15 @@ defined('_JEXEC') or die; +use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; HTMLHelper::_('behavior.formvalidator'); +$direction = Factory::getLanguage()->isRtl() ? 'left' : 'right'; + /** @var \Joomla\CMS\Installation\View\Remove\HtmlView $this */ ?>
@@ -27,7 +30,7 @@

@@ -130,7 +133,7 @@ class="form-check-input"
diff --git a/installation/tmpl/setup/default.php b/installation/tmpl/setup/default.php index 9b6978d1930..85d1e94eef9 100644 --- a/installation/tmpl/setup/default.php +++ b/installation/tmpl/setup/default.php @@ -10,11 +10,14 @@ defined('_JEXEC') or die; +use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; HTMLHelper::_('behavior.formvalidator'); +$direction = Factory::getLanguage()->isRtl() ? 'left' : 'right'; + /** @var \Joomla\CMS\Installation\View\Setup\HtmlView $this */ ?> @@ -44,7 +47,7 @@ form->renderField('site_name'); ?>
- +
@@ -66,7 +69,7 @@ form->renderField('admin_email'); ?>
- +
@@ -116,7 +119,7 @@ form->getInput('db_old'); ?>
- +
diff --git a/language/en-GB/com_content.ini b/language/en-GB/com_content.ini index 981736b228f..7ffe305b55f 100644 --- a/language/en-GB/com_content.ini +++ b/language/en-GB/com_content.ini @@ -47,6 +47,7 @@ COM_CONTENT_FIELD_URLB_LABEL="Link B" COM_CONTENT_FIELD_URLB_LINK_TEXT_LABEL="Link B Text" COM_CONTENT_FIELD_URLC_LABEL="Link C" COM_CONTENT_FIELD_URLC_LINK_TEXT_LABEL="Link C Text" +COM_CONTENT_FILTER_AUTHORS_BY_ME="- Created by me -" COM_CONTENT_FILTER_SEARCH_DESC="Content Filter Search" COM_CONTENT_FLOAT_LABEL="Image Float" COM_CONTENT_FORM_EDIT_ARTICLE="Edit an article" diff --git a/language/en-GB/install.xml b/language/en-GB/install.xml index a3d9f158ed6..ea6b88eda10 100644 --- a/language/en-GB/install.xml +++ b/language/en-GB/install.xml @@ -2,8 +2,8 @@ English (en-GB) en-GB - 5.3.0 - 2025-03 + 5.4.0 + 2025-02 Joomla! Project admin@joomla.org www.joomla.org diff --git a/language/en-GB/joomla.ini b/language/en-GB/joomla.ini index b8048f81691..ff1872f779b 100644 --- a/language/en-GB/joomla.ini +++ b/language/en-GB/joomla.ini @@ -94,6 +94,7 @@ JNEXT="Next" JNEXT_TITLE="Next article: %s" JNO="No" JNONE="None" +JNONE_FILTER="- None -" JNOTPUBLISHEDYET="Not published yet" JNOTICE="Notice" JOK="OK" diff --git a/language/en-GB/langmetadata.xml b/language/en-GB/langmetadata.xml index 43f9e10517f..f883eb34514 100644 --- a/language/en-GB/langmetadata.xml +++ b/language/en-GB/langmetadata.xml @@ -1,8 +1,8 @@ English (en-GB) - 5.3.0 - 2025-03 + 5.4.0 + 2025-02 Joomla! Project admin@joomla.org www.joomla.org diff --git a/layouts/joomla/content/info_block/author.php b/layouts/joomla/content/info_block/author.php index 53b8af327c0..1aa65f97c61 100644 --- a/layouts/joomla/content/info_block/author.php +++ b/layouts/joomla/content/info_block/author.php @@ -18,7 +18,7 @@ created_by_alias ?: $displayData['item']->author); ?> ' . $author . ''; ?> - contact_link) && $displayData['params']->get('link_author') == true) : ?> + contact_link) && $displayData['params']->get('link_author')) : ?> contact_link, $author)); ?> diff --git a/libraries/bootstrap.php b/libraries/bootstrap.php index 190240a730e..041f53782a0 100644 --- a/libraries/bootstrap.php +++ b/libraries/bootstrap.php @@ -62,17 +62,6 @@ class_exists('\\Joomla\\CMS\\Autoload\\ClassLoader'); set_error_handler(['Joomla\CMS\Exception\ExceptionHandler', 'handleUserDeprecatedErrors'], E_USER_DEPRECATED); } -// Suppress phar stream wrapper for non .phar files -$behavior = new \TYPO3\PharStreamWrapper\Behavior(); -\TYPO3\PharStreamWrapper\Manager::initialize( - $behavior->withAssertion(new \TYPO3\PharStreamWrapper\Interceptor\PharExtensionInterceptor()) -); - -if (in_array('phar', stream_get_wrappers())) { - stream_wrapper_unregister('phar'); - stream_wrapper_register('phar', 'TYPO3\\PharStreamWrapper\\PharStreamWrapper'); -} - // Define the Joomla version if not already defined. defined('JVERSION') or define('JVERSION', (new \Joomla\CMS\Version())->getShortVersion()); diff --git a/libraries/cms.php b/libraries/cms.php index 03f8203174b..e8303fd34ca 100644 --- a/libraries/cms.php +++ b/libraries/cms.php @@ -49,17 +49,6 @@ class_exists('\\Joomla\\CMS\\Autoload\\ClassLoader'); // Decorate Composer autoloader spl_autoload_register([new \Joomla\CMS\Autoload\ClassLoader($loader), 'loadClass'], true, true); -// Suppress phar stream wrapper for non .phar files -$behavior = new \TYPO3\PharStreamWrapper\Behavior(); -\TYPO3\PharStreamWrapper\Manager::initialize( - $behavior->withAssertion(new \TYPO3\PharStreamWrapper\Interceptor\PharExtensionInterceptor()) -); - -if (in_array('phar', stream_get_wrappers())) { - stream_wrapper_unregister('phar'); - stream_wrapper_register('phar', 'TYPO3\\PharStreamWrapper\\PharStreamWrapper'); -} - // Define the Joomla version if not already defined if (!defined('JVERSION')) { define('JVERSION', (new \Joomla\CMS\Version())->getShortVersion()); diff --git a/libraries/src/Application/CLI/ColorStyle.php b/libraries/src/Application/CLI/ColorStyle.php index 9bf56abb5cf..76166bfd5a0 100644 --- a/libraries/src/Application/CLI/ColorStyle.php +++ b/libraries/src/Application/CLI/ColorStyle.php @@ -106,7 +106,7 @@ final class ColorStyle public function __construct(string $fg = '', string $bg = '', array $options = []) { if ($fg) { - if (\array_key_exists($fg, static::$knownColors) == false) { + if (!\array_key_exists($fg, static::$knownColors)) { throw new \InvalidArgumentException( \sprintf( 'Invalid foreground color "%1$s" [%2$s]', @@ -120,7 +120,7 @@ public function __construct(string $fg = '', string $bg = '', array $options = [ } if ($bg) { - if (\array_key_exists($bg, static::$knownColors) == false) { + if (!\array_key_exists($bg, static::$knownColors)) { throw new \InvalidArgumentException( \sprintf( 'Invalid background color "%1$s" [%2$s]', @@ -134,7 +134,7 @@ public function __construct(string $fg = '', string $bg = '', array $options = [ } foreach ($options as $option) { - if (\array_key_exists($option, static::$knownOptions) == false) { + if (!\array_key_exists($option, static::$knownOptions)) { throw new \InvalidArgumentException( \sprintf( 'Invalid option "%1$s" [%2$s]', diff --git a/libraries/src/Application/CMSApplication.php b/libraries/src/Application/CMSApplication.php index 2d6a730c72a..3dcec60b661 100644 --- a/libraries/src/Application/CMSApplication.php +++ b/libraries/src/Application/CMSApplication.php @@ -955,7 +955,7 @@ public function login($credentials, $options = []) $user->cookieLogin = true; } - if (\in_array(false, $results, true) == false) { + if (!\in_array(false, $results, true)) { $options['user'] = $user; $options['responseType'] = $response->type; diff --git a/libraries/src/Application/ConsoleApplication.php b/libraries/src/Application/ConsoleApplication.php index bb68e31ef43..255412ca1d3 100644 --- a/libraries/src/Application/ConsoleApplication.php +++ b/libraries/src/Application/ConsoleApplication.php @@ -298,6 +298,8 @@ protected function getDefaultCommands(): array return array_merge( parent::getDefaultCommands(), [ + new Console\AutomatedUpdatesRegisterCommand(), + new Console\AutomatedUpdatesUnregisterCommand(), new Console\CleanCacheCommand(), new Console\CheckUpdatesCommand(), new Console\CheckJoomlaUpdatesCommand(), diff --git a/libraries/src/Application/MultiFactorAuthenticationHandler.php b/libraries/src/Application/MultiFactorAuthenticationHandler.php index e4148696432..daf19551046 100644 --- a/libraries/src/Application/MultiFactorAuthenticationHandler.php +++ b/libraries/src/Application/MultiFactorAuthenticationHandler.php @@ -271,11 +271,6 @@ private function needsMultiFactorAuthenticationRedirection(): bool return false; } - // Do not redirect if we are already in a MFA management or captive page - if ($this->isMultiFactorAuthenticationPage()) { - return false; - } - $option = strtolower($this->input->getCmd('option', '')); $task = strtolower($this->input->getCmd('task', '')); @@ -294,6 +289,13 @@ private function needsMultiFactorAuthenticationRedirection(): bool return false; } + // Do not redirect if we are already in a MFA management or captive page + $onlyCaptive = $this->isMultiFactorAuthenticationPending() && !$isMFASetupMandatory; + + if ($this->isMultiFactorAuthenticationPage($onlyCaptive)) { + return false; + } + return true; } diff --git a/libraries/src/Cache/Cache.php b/libraries/src/Cache/Cache.php index eb9de4a46ee..cef3fa92bfe 100644 --- a/libraries/src/Cache/Cache.php +++ b/libraries/src/Cache/Cache.php @@ -380,7 +380,7 @@ public function lock($id, $group = null, $locktime = null) */ $handler = $this->_getStorage(); - if ($this->_options['locking'] == true) { + if ($this->_options['locking']) { $locked = $handler->lock($id, $group, $locktime); if ($locked !== false) { @@ -397,7 +397,7 @@ public function lock($id, $group = null, $locktime = null) $looptime = $locktime * 10; $id2 = $id . '_lock'; - if ($this->_options['locking'] == true) { + if ($this->_options['locking']) { $data_lock = $handler->get($id2, $group, $this->_options['checkTime']); } else { $data_lock = false; @@ -421,7 +421,7 @@ public function lock($id, $group = null, $locktime = null) } } - if ($this->_options['locking'] == true) { + if ($this->_options['locking']) { $returning->locked = $handler->store($id2, $group, 1); } diff --git a/libraries/src/Cache/Storage/FileStorage.php b/libraries/src/Cache/Storage/FileStorage.php index 5b6e2d45685..38891486bd3 100644 --- a/libraries/src/Cache/Storage/FileStorage.php +++ b/libraries/src/Cache/Storage/FileStorage.php @@ -110,7 +110,7 @@ public function get($id, $group, $checkTime = true) $path = $this->_getFilePath($id, $group); $close = false; - if ($checkTime == false || ($checkTime == true && $this->_checkExpire($id, $group) === true)) { + if (!$checkTime || ($checkTime && $this->_checkExpire($id, $group))) { if (file_exists($path)) { if (isset($this->_locked_files[$path])) { $_fileopen = $this->_locked_files[$path]; diff --git a/libraries/src/Cache/Storage/RedisStorage.php b/libraries/src/Cache/Storage/RedisStorage.php index 112382e4fca..19a352c4ee6 100644 --- a/libraries/src/Cache/Storage/RedisStorage.php +++ b/libraries/src/Cache/Storage/RedisStorage.php @@ -67,7 +67,7 @@ public function __construct($options = []) */ protected function getConnection() { - if (static::isSupported() == false) { + if (!static::isSupported()) { return false; } @@ -100,7 +100,7 @@ protected function getConnection() Log::add($e->getMessage(), Log::DEBUG); } - if ($connection == false) { + if (!$connection) { static::$_redis = null; throw new CacheConnectingException('Redis connection failed', 500); @@ -121,7 +121,7 @@ protected function getConnection() $select = static::$_redis->select($server['db']); - if ($select == false) { + if (!$select) { static::$_redis = null; throw new CacheConnectingException('Redis failed to select database', 500); @@ -150,7 +150,7 @@ protected function getConnection() */ public function contains($id, $group) { - if (static::isConnected() == false) { + if (!static::isConnected()) { return false; } @@ -171,7 +171,7 @@ public function contains($id, $group) */ public function get($id, $group, $checkTime = true) { - if (static::isConnected() == false) { + if (!static::isConnected()) { return false; } @@ -187,7 +187,7 @@ public function get($id, $group, $checkTime = true) */ public function getAll() { - if (static::isConnected() == false) { + if (!static::isConnected()) { return false; } @@ -230,7 +230,7 @@ public function getAll() */ public function store($id, $group, $data) { - if (static::isConnected() == false) { + if (!static::isConnected()) { return false; } @@ -251,7 +251,7 @@ public function store($id, $group, $data) */ public function remove($id, $group) { - if (static::isConnected() == false) { + if (!static::isConnected()) { return false; } @@ -273,7 +273,7 @@ public function remove($id, $group) */ public function clean($group, $mode = null) { - if (static::isConnected() == false) { + if (!static::isConnected()) { return false; } diff --git a/libraries/src/Client/ClientHelper.php b/libraries/src/Client/ClientHelper.php index 0095285ee5e..76186c32338 100644 --- a/libraries/src/Client/ClientHelper.php +++ b/libraries/src/Client/ClientHelper.php @@ -62,7 +62,7 @@ public static function getCredentials($client, $force = false) } // If user and pass are not set in global config lets see if they are in the session - if ($options['enabled'] == true && ($options['user'] == '' || $options['pass'] == '')) { + if ($options['enabled'] && ($options['user'] == '' || $options['pass'] == '')) { $session = Factory::getSession(); $options['user'] = $session->get($client . '.user', null, 'JClientHelper'); $options['pass'] = $session->get($client . '.pass', null, 'JClientHelper'); @@ -159,7 +159,7 @@ public static function hasCredentials($client) break; } - if ($options['enabled'] == false) { + if (!$options['enabled']) { // The client is disabled in global config, so let's pretend we are OK $return = true; } elseif ($options['user'] != '' && $options['pass'] != '') { diff --git a/libraries/src/Client/FtpClient.php b/libraries/src/Client/FtpClient.php index 28be94171e1..96b46a12dd1 100644 --- a/libraries/src/Client/FtpClient.php +++ b/libraries/src/Client/FtpClient.php @@ -1423,7 +1423,7 @@ public function listDetails($path = null, $type = 'all') } // Request the file listing - if (!$this->_putCmd(($recurse == true) ? 'LIST -R' : 'LIST' . $path, [150, 125])) { + if (!$this->_putCmd($recurse ? 'LIST -R' : 'LIST' . $path, [150, 125])) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_150_125', __METHOD__, $this->_response, $path), Log::WARNING, 'jerror'); @ fclose($this->_dataconn); diff --git a/libraries/src/Console/AutomatedUpdatesRegisterCommand.php b/libraries/src/Console/AutomatedUpdatesRegisterCommand.php new file mode 100644 index 00000000000..d612950f051 --- /dev/null +++ b/libraries/src/Console/AutomatedUpdatesRegisterCommand.php @@ -0,0 +1,135 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\CMS\Console; + +use Joomla\CMS\Application\ConsoleApplication; +use Joomla\Component\Joomlaupdate\Administrator\Enum\AutoupdateRegisterState; +use Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel; +use Joomla\Console\Command\AbstractCommand; +use Joomla\Uri\UriHelper; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Console command for managing the update channel for Joomla + * + * @since __DEPLOY_VERSION__ + */ +class AutomatedUpdatesRegisterCommand extends AbstractCommand +{ + /** + * The default command name + * + * @var string + * @since __DEPLOY_VERSION__ + */ + protected static $defaultName = 'core:autoupdate:register'; + + /** + * SymfonyStyle Object + * + * @var SymfonyStyle + * @since __DEPLOY_VERSION__ + */ + private $ioStyle; + + /** + * Initialise the command. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + protected function configure(): void + { + $help = "%command.name% allows to register a site for the automated core update service. + \nUsage: php %command.full_name%"; + + $this->setDescription('Register the current site for the automated core update service.'); + $this->setHelp($help); + } + + /** + * Configures the IO + * + * @param InputInterface $input Console Input + * @param OutputInterface $output Console Output + * + * @return void + * + * @since __DEPLOY_VERSION__ + * + */ + private function configureIO(InputInterface $input, OutputInterface $output) + { + $this->ioStyle = new SymfonyStyle($input, $output); + } + + /** + * Internal function to execute the command. + * + * @param InputInterface $input The input to inject into the command. + * @param OutputInterface $output The output to inject into the command. + * + * @return integer The command exit code + * + * @since __DEPLOY_VERSION__ + */ + protected function doExecute(InputInterface $input, OutputInterface $output): int + { + $this->configureIO($input, $output); + + // Get live site parameter + $liveSite = $input->getOption('live-site'); + + /** @var ConsoleApplication $app */ + $app = $this->getApplication(); + $app->getLanguage()->load('com_joomlaupdate', JPATH_ADMINISTRATOR); + + // Check that the URL is provided + if (empty($liveSite)) { + $this->ioStyle->writeln('ERROR: Missing --live-site option'); + + return Command::FAILURE; + } + + // Parse URL and check existence of parts + $urlParts = UriHelper::parse_url($liveSite); + + if (empty($urlParts['scheme']) || empty($urlParts['host'])) { + $this->ioStyle->writeln( + 'ERROR: Incomplete --live-site provided; provide scheme and host' + ); + + return Command::FAILURE; + } + + // Run registration + /** @var UpdateModel $updateModel */ + $updateModel = $app + ->bootComponent('com_joomlaupdate') + ->getMVCFactory($app) + ->createModel('Update', 'Administrator'); + + $result = $updateModel->changeAutoUpdateRegistration(AutoupdateRegisterState::Subscribe); + + if ($result === false) { + return Command::FAILURE; + } + + return Command::SUCCESS; + } +} diff --git a/libraries/src/Console/AutomatedUpdatesUnregisterCommand.php b/libraries/src/Console/AutomatedUpdatesUnregisterCommand.php new file mode 100644 index 00000000000..ba89383e6c3 --- /dev/null +++ b/libraries/src/Console/AutomatedUpdatesUnregisterCommand.php @@ -0,0 +1,135 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\CMS\Console; + +use Joomla\CMS\Application\ConsoleApplication; +use Joomla\Component\Joomlaupdate\Administrator\Enum\AutoupdateRegisterState; +use Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel; +use Joomla\Console\Command\AbstractCommand; +use Joomla\Uri\UriHelper; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Console command for managing the update channel for Joomla + * + * @since __DEPLOY_VERSION__ + */ +class AutomatedUpdatesUnregisterCommand extends AbstractCommand +{ + /** + * The default command name + * + * @var string + * @since __DEPLOY_VERSION__ + */ + protected static $defaultName = 'core:autoupdate:unregister'; + + /** + * SymfonyStyle Object + * + * @var SymfonyStyle + * @since __DEPLOY_VERSION__ + */ + private $ioStyle; + + /** + * Initialise the command. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + protected function configure(): void + { + $help = "%command.name% allows to unregister a site from the automated core update service. + \nUsage: php %command.full_name%"; + + $this->setDescription('Unregister the current site from the unautomated core update service.'); + $this->setHelp($help); + } + + /** + * Configures the IO + * + * @param InputInterface $input Console Input + * @param OutputInterface $output Console Output + * + * @return void + * + * @since __DEPLOY_VERSION__ + * + */ + private function configureIO(InputInterface $input, OutputInterface $output) + { + $this->ioStyle = new SymfonyStyle($input, $output); + } + + /** + * Internal function to execute the command. + * + * @param InputInterface $input The input to inject into the command. + * @param OutputInterface $output The output to inject into the command. + * + * @return integer The command exit code + * + * @since __DEPLOY_VERSION__ + */ + protected function doExecute(InputInterface $input, OutputInterface $output): int + { + $this->configureIO($input, $output); + + // Get live site parameter + $liveSite = $input->getOption('live-site'); + + /** @var ConsoleApplication $app */ + $app = $this->getApplication(); + $app->getLanguage()->load('com_joomlaupdate', JPATH_ADMINISTRATOR); + + // Check that the URL is provided + if (empty($liveSite)) { + $this->ioStyle->writeln('ERROR: Missing --live-site option'); + + return Command::FAILURE; + } + + // Parse URL and check existence of parts + $urlParts = UriHelper::parse_url($liveSite); + + if (empty($urlParts['scheme']) || empty($urlParts['host'])) { + $this->ioStyle->writeln( + 'ERROR: Incomplete --live-site provided; provide scheme and host' + ); + + return Command::FAILURE; + } + + // Run registration + /** @var UpdateModel $updateModel */ + $updateModel = $app + ->bootComponent('com_joomlaupdate') + ->getMVCFactory($app) + ->createModel('Update', 'Administrator'); + + $result = $updateModel->changeAutoUpdateRegistration(AutoupdateRegisterState::Unsubscribe); + + if ($result === false) { + return Command::FAILURE; + } + + return Command::SUCCESS; + } +} diff --git a/libraries/src/Date/Date.php b/libraries/src/Date/Date.php index ac1321467ee..0315aa6cfdd 100644 --- a/libraries/src/Date/Date.php +++ b/libraries/src/Date/Date.php @@ -303,7 +303,7 @@ public function format($format, $local = false, $translate = true): string } // If the returned time should not be local use UTC. - if ($local == false) { + if (!$local) { parent::setTimezone(new \DateTimeZone('UTC')); } @@ -329,7 +329,7 @@ public function format($format, $local = false, $translate = true): string } } - if ($local == false && $this->tz !== null) { + if (!$local && $this->tz !== null) { parent::setTimezone($this->tz); } diff --git a/libraries/src/Document/Document.php b/libraries/src/Document/Document.php index 70b5c22e35e..b2090cff657 100644 --- a/libraries/src/Document/Document.php +++ b/libraries/src/Document/Document.php @@ -444,7 +444,7 @@ public function getMetaData($name, $attribute = 'name') { // B/C old http_equiv parameter. if (!\is_string($attribute)) { - $attribute = $attribute == true ? 'http-equiv' : 'name'; + $attribute = $attribute ? 'http-equiv' : 'name'; } if ($name === 'generator') { @@ -478,7 +478,7 @@ public function setMetaData($name, $content, $attribute = 'name') // B/C old http_equiv parameter. if (!\is_string($attribute)) { - $attribute = $attribute == true ? 'http-equiv' : 'name'; + $attribute = $attribute ? 'http-equiv' : 'name'; } if ($name === 'generator') { diff --git a/libraries/src/Document/HtmlDocument.php b/libraries/src/Document/HtmlDocument.php index 32bbe274dae..30818d35017 100644 --- a/libraries/src/Document/HtmlDocument.php +++ b/libraries/src/Document/HtmlDocument.php @@ -536,7 +536,7 @@ public function getBuffer($type = null, $name = null, $attribs = []) $renderer = $this->loadRenderer($type); - if ($this->_caching == true && $type === 'modules' && $name !== 'debug') { + if ($this->_caching && $type === 'modules' && $name !== 'debug') { /** @var \Joomla\CMS\Document\Renderer\Html\ModulesRenderer $renderer */ /** @var \Joomla\CMS\Cache\Controller\OutputController $cache */ $cache = $this->getCacheControllerFactory()->createCacheController('output', ['defaultgroup' => 'com_modules']); diff --git a/libraries/src/Document/Renderer/Html/MetasRenderer.php b/libraries/src/Document/Renderer/Html/MetasRenderer.php index 26b0b082e85..f6e29d269ad 100644 --- a/libraries/src/Document/Renderer/Html/MetasRenderer.php +++ b/libraries/src/Document/Renderer/Html/MetasRenderer.php @@ -76,7 +76,7 @@ public function render($head, $params = [], $content = null) throw new \RuntimeException('Detected an override for "joomla.script.options" asset'); } - $jsonFlags = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | (JDEBUG ? JSON_PRETTY_PRINT : 0); + $jsonFlags = JSON_UNESCAPED_UNICODE | (JDEBUG ? JSON_PRETTY_PRINT : 0); $jsonOptions = json_encode($scriptOptions, $jsonFlags); // Set content and update attributes of dummy asset to correct ones diff --git a/libraries/src/Extension/ExtensionHelper.php b/libraries/src/Extension/ExtensionHelper.php index db4523844ab..6182bf8008d 100644 --- a/libraries/src/Extension/ExtensionHelper.php +++ b/libraries/src/Extension/ExtensionHelper.php @@ -174,6 +174,7 @@ class ExtensionHelper // Core plugin extensions - behaviour ['plugin', 'compat', 'behaviour', 0], + ['plugin', 'compat6', 'behaviour', 0], ['plugin', 'taggable', 'behaviour', 0], ['plugin', 'versionable', 'behaviour', 0], @@ -266,6 +267,7 @@ class ExtensionHelper ['plugin', 'user', 'privacy', 0], // Core plugin extensions - quick icon + ['plugin', 'autoupdate', 'quickicon', 0], ['plugin', 'downloadkey', 'quickicon', 0], ['plugin', 'extensionupdate', 'quickicon', 0], ['plugin', 'joomlaupdate', 'quickicon', 0], @@ -339,6 +341,7 @@ class ExtensionHelper ['plugin', 'contact', 'webservices', 0], ['plugin', 'content', 'webservices', 0], ['plugin', 'installer', 'webservices', 0], + ['plugin', 'joomlaupdate', 'webservices', 0], ['plugin', 'languages', 'webservices', 0], ['plugin', 'media', 'webservices', 0], ['plugin', 'menus', 'webservices', 0], diff --git a/libraries/src/Filesystem/File.php b/libraries/src/Filesystem/File.php index 627c4c2ec97..871dee011aa 100644 --- a/libraries/src/Filesystem/File.php +++ b/libraries/src/Filesystem/File.php @@ -409,7 +409,7 @@ public static function write($file, $buffer, $useStreams = false) // If the destination directory doesn't exist we need to create it if (!file_exists(\dirname($file))) { - if (Folder::create(\dirname($file)) == false) { + if (!Folder::create(\dirname($file))) { return false; } } diff --git a/libraries/src/Filesystem/FilesystemHelper.php b/libraries/src/Filesystem/FilesystemHelper.php index 9d70f3c273e..e5486d0f41b 100644 --- a/libraries/src/Filesystem/FilesystemHelper.php +++ b/libraries/src/Filesystem/FilesystemHelper.php @@ -316,7 +316,7 @@ public static function fileUploadMaxSize($unitOutput = true) $max_size = $upload_max; } - if ($unitOutput == true) { + if ($unitOutput) { $max_size = self::parseSizeUnit($max_size); } diff --git a/libraries/src/Filesystem/Folder.php b/libraries/src/Filesystem/Folder.php index 34f3799f929..f5b2d0ebc70 100644 --- a/libraries/src/Filesystem/Folder.php +++ b/libraries/src/Filesystem/Folder.php @@ -243,7 +243,7 @@ public static function create($path = '', $mode = 0755) } } - if ($inBaseDir == false) { + if (!$inBaseDir) { // Return false for JFolder::create because the path to be created is not in open_basedir Log::add(__METHOD__ . ': ' . Text::_('JLIB_FILESYSTEM_ERROR_FOLDER_PATH'), Log::WARNING, 'jerror'); diff --git a/libraries/src/Form/Field/SpacerField.php b/libraries/src/Form/Field/SpacerField.php index d686ec7c3aa..e6f9abf7801 100644 --- a/libraries/src/Form/Field/SpacerField.php +++ b/libraries/src/Form/Field/SpacerField.php @@ -75,7 +75,7 @@ protected function getLabel() // Build the class for the label. $class = !empty($this->description) ? 'hasPopover' : ''; - $class = $this->required == true ? $class . ' required' : $class; + $class = $this->required ? $class . ' required' : $class; // Add the opening label tag and main attributes attributes. $label .= ' diff --git a/plugins/quickicon/autoupdate/services/provider.php b/plugins/quickicon/autoupdate/services/provider.php new file mode 100644 index 00000000000..22860f95a07 --- /dev/null +++ b/plugins/quickicon/autoupdate/services/provider.php @@ -0,0 +1,49 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +\defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\Quickicon\Autoupdate\Extension\Autoupdate; + +return new class () implements ServiceProviderInterface { + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function register(Container $container) + { + $container->set( + PluginInterface::class, + function (Container $container) { + // @Todo This needs to be changed to a proper factory + $plugin = \Joomla\CMS\Plugin\PluginHelper::getPlugin('quickicon', 'autoupdate'); + + $plugin = new Autoupdate( + $container->get(DispatcherInterface::class), + Factory::getApplication()->getDocument(), + (array) $plugin + ); + $plugin->setApplication(Factory::getApplication()); + + return $plugin; + } + ); + } +}; diff --git a/plugins/quickicon/autoupdate/src/Extension/Autoupdate.php b/plugins/quickicon/autoupdate/src/Extension/Autoupdate.php new file mode 100644 index 00000000000..a33bff7e394 --- /dev/null +++ b/plugins/quickicon/autoupdate/src/Extension/Autoupdate.php @@ -0,0 +1,141 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Plugin\Quickicon\Autoupdate\Extension; + +use Joomla\CMS\Document\Document; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Uri\Uri; +use Joomla\Event\DispatcherInterface; +use Joomla\Event\SubscriberInterface; +use Joomla\Module\Quickicon\Administrator\Event\QuickIconsEvent; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Joomla! auto update health check notification plugin + * + * @since __DEPLOY_VERSION__ + */ +class Autoupdate extends CMSPlugin implements SubscriberInterface +{ + /** + * Load the language file on instantiation. + * + * @var boolean + * @since __DEPLOY_VERSION__ + */ + protected $autoloadLanguage = true; + + /** + * The document. + * + * @var Document + * + * @since __DEPLOY_VERSION__ + */ + private $document; + + /** + * Returns an array of events this subscriber will listen to. + * + * @return array + * + * @since __DEPLOY_VERSION__ + */ + public static function getSubscribedEvents(): array + { + return [ + 'onGetIcons' => 'getAutoUpdateStatusNotification', + ]; + } + + /** + * Constructor + * + * @param DispatcherInterface $dispatcher The object to observe + * @param Document $document The document + * @param array $config An optional associative array of configuration settings. + * Recognized key values include 'name', 'group', 'params', 'language' + * (this list is not meant to be comprehensive). + * + * @since __DEPLOY_VERSION__ + */ + public function __construct($dispatcher, Document $document, array $config = []) + { + parent::__construct($dispatcher, $config); + + $this->document = $document; + } + + /** + * This method is called when the Quick Icons module is constructing its set + * of icons. You can return an array which defines a single icon and it will + * be rendered right after the stock Quick Icons. + * + * @param QuickIconsEvent $event The event object + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function getAutoUpdateStatusNotification(QuickIconsEvent $event) + { + $context = $event->getContext(); + + if ( + $context !== $this->params->get('context', 'update_quickicon') + || !$this->getApplication()->getIdentity()->authorise('core.admin', 'com_joomlaupdate') + ) { + return; + } + + Text::script('PLG_QUICKICON_AUTOUPDATE_ERROR'); + Text::script('PLG_QUICKICON_AUTOUPDATE_OK'); + Text::script('PLG_QUICKICON_AUTOUPDATE_OUTDATED'); + Text::script('PLG_QUICKICON_AUTOUPDATE_DISABLED'); + Text::script('MESSAGE'); + Text::script('ERROR'); + Text::script('INFO'); + Text::script('WARNING'); + + $this->document->addScriptOptions( + 'js-auto-update', + [ + 'url' => Uri::base() . 'index.php?option=com_joomlaupdate', + 'ajaxUrl' => Uri::base() . 'index.php?option=com_joomlaupdate&task=update.healthstatus&' + . Session::getFormToken() . '=1', + ] + ); + + $this->document->getWebAssetManager() + ->registerAndUseScript('plg_quickicon_autoupdate', 'plg_quickicon_autoupdate/healthcheck.min.js', [], ['defer' => true], ['core']); + + // Add the icon to the result array + $result = $event->getArgument('result', []); + + $result[] = [ + [ + 'link' => 'index.php?option=com_config&view=component&component=com_joomlaupdate#automated-updates', + 'image' => 'icon-health', + 'icon' => '', + 'text' => Text::_('PLG_QUICKICON_AUTOUPDATE_CHECKING'), + 'id' => 'plg_quickicon_autoupdate', + 'group' => 'MOD_QUICKICON_MAINTENANCE', + ], + ]; + + $event->setArgument('result', $result); + } +} diff --git a/plugins/system/httpheaders/httpheaders.xml b/plugins/system/httpheaders/httpheaders.xml index 46199572adf..97836280cb9 100644 --- a/plugins/system/httpheaders/httpheaders.xml +++ b/plugins/system/httpheaders/httpheaders.xml @@ -268,35 +268,36 @@ label="PLG_SYSTEM_HTTPHEADERS_CONTENTSECURITYPOLICY_VALUES_DIRECTIVE" class="col-md-4" validate="options" + layout="joomla.form.field.list-fancy-select" > + + + + - + + + + + + + - + - - - - - - - - - - - + - + getArgument('params')->email ?? ''; $forcedLanguage = $event->getArgument('params')->language_override ?? ''; + $updateParams = ComponentHelper::getParams('com_joomlaupdate'); + + // Don't send when automated updates are active and working + $registrationState = AutoupdateRegisterState::tryFrom($updateParams->get('autoupdate_status', '')); + $lastUpdateCheck = date_create_from_format('Y-m-d H:i:s', $updateParams->get('update_last_check', '')); + + if ($registrationState === AutoupdateRegisterState::Subscribed && $lastUpdateCheck !== false && $lastUpdateCheck->diff(new \DateTime())->days < 4) { + return Status::OK; + } + // This is the extension ID for Joomla! itself $eid = ExtensionHelper::getExtensionRecord('joomla', 'file')->extension_id; diff --git a/plugins/webservices/joomlaupdate/joomlaupdate.xml b/plugins/webservices/joomlaupdate/joomlaupdate.xml new file mode 100644 index 00000000000..1a32eb9915a --- /dev/null +++ b/plugins/webservices/joomlaupdate/joomlaupdate.xml @@ -0,0 +1,21 @@ + + + plg_webservices_joomlaupdate + Joomla! Project + 2025-03 + (C) 2025 Open Source Matters, Inc. + GNU General Public License version 2 or later; see LICENSE.txt + admin@joomla.org + www.joomla.org + 5.4.0 + PLG_WEBSERVICES_JOOMLAUPDATE_XML_DESCRIPTION + Joomla\Plugin\WebServices\Joomlaupdate + + services + src + + + language/en-GB/plg_webservices_joomlaupdate.ini + language/en-GB/plg_webservices_joomlaupdate.sys.ini + + diff --git a/plugins/webservices/joomlaupdate/services/provider.php b/plugins/webservices/joomlaupdate/services/provider.php new file mode 100644 index 00000000000..fc785c9670a --- /dev/null +++ b/plugins/webservices/joomlaupdate/services/provider.php @@ -0,0 +1,46 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +\defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\WebServices\Joomlaupdate\Extension\Joomlaupdate; + +return new class () implements ServiceProviderInterface { + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function register(Container $container): void + { + $container->set( + PluginInterface::class, + function (Container $container) { + $plugin = new Joomlaupdate( + $container->get(DispatcherInterface::class), + (array) PluginHelper::getPlugin('webservices', 'joomlaupdate') + ); + $plugin->setApplication(Factory::getApplication()); + + return $plugin; + } + ); + } +}; diff --git a/plugins/webservices/joomlaupdate/src/Extension/Joomlaupdate.php b/plugins/webservices/joomlaupdate/src/Extension/Joomlaupdate.php new file mode 100644 index 00000000000..a526f56e2fa --- /dev/null +++ b/plugins/webservices/joomlaupdate/src/Extension/Joomlaupdate.php @@ -0,0 +1,68 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Plugin\WebServices\Joomlaupdate\Extension; + +use Joomla\CMS\Event\Application\BeforeApiRouteEvent; +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Event\SubscriberInterface; +use Joomla\Router\Route; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Web Services adapter for com_joomlaupdate. + * + * @since __DEPLOY_VERSION__ + */ +final class Joomlaupdate extends CMSPlugin implements SubscriberInterface +{ + /** + * Returns an array of events this subscriber will listen to. + * + * @return array + * + * @since __DEPLOY_VERSION__ + */ + public static function getSubscribedEvents(): array + { + return [ + 'onBeforeApiRoute' => 'onBeforeApiRoute', + ]; + } + + /** + * Registers com_joomlaupdate's API's routes in the application + * + * @param BeforeApiRouteEvent $event The event object + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function onBeforeApiRoute(BeforeApiRouteEvent $event): void + { + $router = $event->getRouter(); + $defaults = ['component' => 'com_joomlaupdate', 'public' => true]; + + $routes = [ + new Route(['GET'], 'v1/joomlaupdate/healthcheck', 'healthcheck.show', [], $defaults), + new Route(['GET'], 'v1/joomlaupdate/getUpdate', 'updates.getUpdate', [], $defaults), + new Route(['POST'], 'v1/joomlaupdate/prepareUpdate', 'updates.prepareUpdate', [], $defaults), + new Route(['POST'], 'v1/joomlaupdate/finalizeUpdate', 'updates.finalizeUpdate', [], $defaults), + new Route(['POST'], 'v1/joomlaupdate/notificationSuccess', 'notification.success', [], $defaults), + new Route(['POST'], 'v1/joomlaupdate/notificationFailed', 'notification.failed', [], $defaults), + ]; + + $router->addRoutes($routes); + } +} diff --git a/templates/system/build_incomplete.html b/templates/system/build_incomplete.html index 50014b0c650..9eb53c2561d 100644 --- a/templates/system/build_incomplete.html +++ b/templates/system/build_incomplete.html @@ -6,7 +6,7 @@ Joomla: Environment Setup Incomplete - +
diff --git a/templates/system/fatal-error.html b/templates/system/fatal-error.html index 75c9faff1a5..68247714020 100644 --- a/templates/system/fatal-error.html +++ b/templates/system/fatal-error.html @@ -6,7 +6,7 @@ An Error Occurred: {{statusText}} - +
diff --git a/templates/system/incompatible.html b/templates/system/incompatible.html index 4593b6d29f3..513d0941d61 100644 --- a/templates/system/incompatible.html +++ b/templates/system/incompatible.html @@ -6,7 +6,7 @@ Joomla: unsupported PHP version - +
diff --git a/tests/System/README.md b/tests/System/README.md index 550b185e948..ff9002aebbc 100644 --- a/tests/System/README.md +++ b/tests/System/README.md @@ -178,6 +178,8 @@ The Joomla System Tests come with some convenient [Cypress Tasks](https://docs.c - **writeRelativeFile** – Writes a file relative to the CMS root folder - **deleteRelativePath** – Deletes a file or folder relative to the CMS root folder - **copyRelativeFile** – Copies a file relative to the CMS root folder +- **checkForLogs** – Checks the log file (path defined in configuration) for errors +- **clearLogs** – Clears the logs in the log file from the configuration - **startMailServer** – Starts the smtp-tester SMTP server - **getMails** – Get received mails from smtp-tester - **clearEmails** – Clear all smtp-tester received mails diff --git a/tests/System/entrypoint.sh b/tests/System/entrypoint.sh index 40c5f5efd02..631c4f07074 100644 --- a/tests/System/entrypoint.sh +++ b/tests/System/entrypoint.sh @@ -39,4 +39,4 @@ if [ -z "$( ls -A '/root/.cache/Cypress' )" ]; then npx cypress verify fi -npx cypress run --browser=firefox --e2e --env cmsPath=/tests/www/$TEST_GROUP,db_type=$DB_ENGINE,db_host=$DB_HOST,db_password=joomla_ut,db_prefix="${TEST_GROUP}_" --config baseUrl=https://localhost/$TEST_GROUP,screenshotsFolder=$JOOMLA_BASE/tests/System/output/screenshots +npx cypress run --browser=firefox --e2e --env cmsPath=/tests/www/$TEST_GROUP,db_type=$DB_ENGINE,db_host=$DB_HOST,db_password=joomla_ut,db_prefix="${TEST_GROUP}_",logFile=/var/log/apache2/error.log --config baseUrl=https://localhost/$TEST_GROUP,screenshotsFolder=$JOOMLA_BASE/tests/System/output/screenshots diff --git a/tests/System/integration/administrator/components/com_contenthistory/Content.cy.js b/tests/System/integration/administrator/components/com_contenthistory/Content.cy.js index afa40f90dab..d2a7729a2ae 100644 --- a/tests/System/integration/administrator/components/com_contenthistory/Content.cy.js +++ b/tests/System/integration/administrator/components/com_contenthistory/Content.cy.js @@ -94,7 +94,7 @@ describe('Test in backend that the content history list', () => { .should('contain.text', 'Please select two versions'); }); - it('can delete an history content item', () => { + it('can delete a history content item', () => { cy.visit('/administrator/index.php?option=com_content&task=article.add'); cy.get('#jform_title').clear().type('Test article versions'); cy.clickToolbarButton('Save'); diff --git a/tests/System/integration/administrator/components/com_templates/Styles.cy.js b/tests/System/integration/administrator/components/com_templates/Styles.cy.js new file mode 100644 index 00000000000..5e23a787053 --- /dev/null +++ b/tests/System/integration/administrator/components/com_templates/Styles.cy.js @@ -0,0 +1,51 @@ +describe('Test in backend that the styles list', () => { + beforeEach(() => cy.doAdministratorLogin()); + + it('can list site styles', () => { + cy.visit('/administrator/index.php?option=com_templates&view=styles&client_id=0'); + cy.get('h1.page-title').should('contain.text', 'Templates: Styles (Site)'); + cy.get('#client_id option:selected').should('have.text', 'Site'); + cy.get('#styleList tbody a').contains('Cassiopeia - Default').click(); + cy.get('h1.page-title').should('contain.text', 'Templates: Edit Style'); + cy.clickToolbarButton('Cancel'); + }); + + it('can list administrator styles', () => { + cy.visit('/administrator/index.php?option=com_templates&view=styles&client_id=1'); + cy.get('h1.page-title').should('contain.text', 'Templates: Styles (Administrator)'); + cy.get('#client_id option:selected').should('have.text', 'Administrator'); + cy.get('#styleList tbody a').contains('Atum - Default').click(); + cy.get('h1.page-title').should('contain.text', 'Templates: Edit Style'); + cy.clickToolbarButton('Cancel'); + }); + + it('can select client', () => { + cy.visit('/administrator/index.php?option=com_templates&view=styles&client_id=0'); + cy.get('h1.page-title').should('contain.text', 'Templates: Styles (Site)'); + cy.get('#client_id').select('Administrator'); + cy.get('h1.page-title').should('contain.text', 'Templates: Styles (Administrator)'); + cy.get('#client_id').select('Site'); + cy.get('h1.page-title').should('contain.text', 'Templates: Styles (Site)'); + }); + + it('can open templates', () => { + cy.visit('/administrator/index.php?option=com_templates&view=styles&client_id=1'); + cy.get('h1.page-title').should('contain.text', 'Templates: Styles (Administrator)'); + cy.get('#toolbar-templates').click(); + cy.get('h1.page-title').should('contain.text', 'Templates: Templates (Administrator)'); + }); + + it('can open options', () => { + cy.visit('/administrator/index.php?option=com_templates&view=styles&client_id=0'); + cy.intercept('**/administrator/index.php?option=com_config&view=component&component=com_templates*').as('options'); + cy.intercept('**/administrator/index.php?option=com_templates&view=styles&client_id=0*').as('listview'); + + cy.clickToolbarButton('Options'); + cy.wait('@options'); + cy.title().should('contain', 'Template: Options'); + cy.get('h1.page-title').should('contain', 'Template: Options'); + + cy.clickToolbarButton('Cancel'); + cy.wait('@listview'); + }); +}); diff --git a/tests/System/integration/administrator/components/com_templates/Templates.cy.js b/tests/System/integration/administrator/components/com_templates/Templates.cy.js new file mode 100644 index 00000000000..bac91ca4655 --- /dev/null +++ b/tests/System/integration/administrator/components/com_templates/Templates.cy.js @@ -0,0 +1,51 @@ +describe('Test in backend that the templates list', () => { + beforeEach(() => cy.doAdministratorLogin()); + + it('can list site templates', () => { + cy.visit('/administrator/index.php?option=com_templates&view=templates&client_id=0'); + cy.get('h1.page-title').should('contain.text', 'Templates: Templates (Site)'); + cy.get('#client_id option:selected').should('have.text', 'Site'); + cy.get('#templateList tbody .template-name a').contains('Cassiopeia').click(); + cy.get('h1.page-title').should('contain.text', 'Templates: Customise (Cassiopeia)'); + cy.clickToolbarButton('Cancel'); + }); + + it('can list administrator templates', () => { + cy.visit('/administrator/index.php?option=com_templates&view=templates&client_id=1'); + cy.get('h1.page-title').should('contain.text', 'Templates: Templates (Administrator)'); + cy.get('#client_id option:selected').should('have.text', 'Administrator'); + cy.get('#templateList tbody .template-name a').contains('Atum').click(); + cy.get('h1.page-title').should('contain.text', 'Templates: Customise (Atum)'); + cy.clickToolbarButton('Cancel'); + }); + + it('can select client', () => { + cy.visit('/administrator/index.php?option=com_templates&view=templates&client_id=0'); + cy.get('h1.page-title').should('contain.text', 'Templates: Templates (Site)'); + cy.get('#client_id').select('Administrator'); + cy.get('h1.page-title').should('contain.text', 'Templates: Templates (Administrator)'); + cy.get('#client_id').select('Site'); + cy.get('h1.page-title').should('contain.text', 'Templates: Templates (Site)'); + }); + + it('can open styles', () => { + cy.visit('/administrator/index.php?option=com_templates&view=templates&client_id=0'); + cy.get('h1.page-title').should('contain.text', 'Templates: Templates (Site)'); + cy.get('#toolbar-styles').click(); + cy.get('h1.page-title').should('contain.text', 'Templates: Styles (Site)'); + }); + + it('can open options', () => { + cy.visit('/administrator/index.php?option=com_templates&view=templates&client_id=1'); + cy.intercept('**/administrator/index.php?option=com_config&view=component&component=com_templates*').as('options'); + cy.intercept('**/administrator/index.php?option=com_templates&view=templates&client_id=1*').as('listview'); + + cy.clickToolbarButton('Options'); + cy.wait('@options'); + cy.title().should('contain', 'Template: Options'); + cy.get('h1.page-title').should('contain', 'Template: Options'); + + cy.clickToolbarButton('Cancel'); + cy.wait('@listview'); + }); +}); diff --git a/tests/System/integration/administrator/components/com_users/Mfa.cy.js b/tests/System/integration/administrator/components/com_users/Mfa.cy.js new file mode 100644 index 00000000000..040d9275678 --- /dev/null +++ b/tests/System/integration/administrator/components/com_users/Mfa.cy.js @@ -0,0 +1,125 @@ +import { TOTP } from 'totp-generator'; + +afterEach(() => cy.db_getUserId().then((uid) => cy.task('queryDB', `DELETE FROM #__user_mfa WHERE user_id = ${uid}`))); + +describe('Test in backend that the user', () => { + it('can login with Multi-factor Authentication (email)', () => { + cy.doAdministratorLogin(); + cy.visit('/administrator/index.php?option=com_users&view=users'); + cy.get('.header-profile:visible').click(); + cy.get('.header-profile a.dropdown-item').contains('Edit Account').click(); + cy.get('#myTab div[role="tablist"] button[aria-controls="multifactorauth"]').click(); + cy.task('clearEmails'); + cy.get('.com-users-methods-list-method-name-email a.com-users-methods-list-method-addnew').click(); + cy.get('#com-users-method-edit-title').clear().type('Test Code'); + cy.task('getMails').then((mails) => { + cy.wrap(mails).should('have.lengthOf', 1); + cy.wrap(mails[0].headers.subject).should('match', /code is -\d{6}-$/); + cy.wrap(/code is -(\d{6})-$/.exec(mails[0].headers.subject)[1]).as('code') + .then((code) => cy.wrap(mails[0].body).should('have.string', `Your authentication code is ${code}.`)); + cy.wrap(mails[0].html).should('be.false'); + }); + cy.get('@code').then((code) => cy.get('#com-users-method-code').clear().type(code)); + cy.get('#com-users-method-edit').submit(); + cy.get('.com-users-methods-list-method-name-email .com-users-methods-list-method-record').contains('Test Code'); + cy.clickToolbarButton('Cancel'); + cy.doAdministratorLogout(); + cy.get('#mod-login-username').type(Cypress.env('username')); + cy.get('#mod-login-password').type(Cypress.env('password')); + cy.get('#form-login').submit(); + cy.get('#users-mfa-title').contains('Test Code'); + cy.task('getMails').then((mails) => { + cy.wrap(mails).should('have.lengthOf', 2); + cy.wrap(mails[1].headers.subject).should('match', /code is -\d{6}-$/); + cy.wrap(/code is -(\d{6})-$/.exec(mails[1].headers.subject)[1]).as('code'); + }); + cy.get('@code').then((code) => cy.get('#users-mfa-code').clear().type(code)); + cy.get('#users-mfa-captive-form').submit(); + cy.visit('/administrator/index.php?option=com_users&view=users'); + cy.get('.header-profile:visible').click(); + cy.get('.header-profile a.dropdown-item').contains('Edit Account').click(); + cy.get('#myTab div[role="tablist"] button[aria-controls="multifactorauth"]').click(); + cy.get('#com-users-methods-reset-message').contains('is enabled'); + cy.get('.com-users-methods-list-method-name-email a.com-users-methods-list-method-record-delete').click(); + cy.on('window:confirm', (text) => expect(text).to.contains('Are you sure you want to delete?')); + cy.get('#com-users-methods-reset-message').contains('not enabled'); + }); + + it('can login with Multi-factor Authentication (totp)', () => { + cy.doAdministratorLogin(); + cy.visit('/administrator/index.php?option=com_users&view=users'); + cy.get('.header-profile:visible').click(); + cy.get('.header-profile a.dropdown-item').contains('Edit Account').click(); + cy.get('#myTab div[role="tablist"] button[aria-controls="multifactorauth"]').click(); + cy.get('.com-users-methods-list-method-name-totp a.com-users-methods-list-method-addnew').click(); + cy.get('#com-users-method-edit-title').clear().type('Test Code'); + cy.get('.com-users-method-edit-tabular-container table tr td') + .contains('Enter this key') + .next() + .invoke('text') + .then((key) => key.trim()) + .as('secret'); + cy.get('@secret').then((secret) => cy.get('#com-users-method-code').clear().type(TOTP.generate(secret).otp)); + cy.get('#com-users-method-edit').submit(); + cy.get('.com-users-methods-list-method-name-totp .com-users-methods-list-method-record').contains('Test Code'); + cy.clickToolbarButton('Cancel'); + cy.doAdministratorLogout(); + cy.get('#mod-login-username').type(Cypress.env('username')); + cy.get('#mod-login-password').type(Cypress.env('password')); + cy.get('#form-login').submit(); + cy.get('#users-mfa-title').contains('Verification code'); + cy.get('@secret').then((secret) => cy.get('#users-mfa-code').clear().type(TOTP.generate(secret).otp)); + cy.get('#users-mfa-captive-form').submit(); + cy.visit('/administrator/index.php?option=com_users&view=users'); + cy.get('.header-profile:visible').click(); + cy.get('.header-profile a.dropdown-item').contains('Edit Account').click(); + cy.get('#myTab div[role="tablist"] button[aria-controls="multifactorauth"]').click(); + cy.get('#com-users-methods-reset-message').contains('is enabled'); + cy.get('.com-users-methods-list-method-name-totp a.com-users-methods-list-method-record-delete').click(); + cy.on('window:confirm', (text) => expect(text).to.contains('Are you sure you want to delete?')); + cy.get('#com-users-methods-reset-message').contains('not enabled'); + }); + + it('can login with Multi-factor Authentication (backup codes)', () => { + cy.doAdministratorLogin(); + cy.visit('/administrator/index.php?option=com_users&view=users'); + cy.get('.header-profile:visible').click(); + cy.get('.header-profile a.dropdown-item').contains('Edit Account').click(); + cy.get('#myTab div[role="tablist"] button[aria-controls="multifactorauth"]').click(); + cy.get('.com-users-methods-list-method-name-totp a.com-users-methods-list-method-addnew').click(); + cy.get('#com-users-method-edit-title').clear().type('Test Code'); + cy.get('.com-users-method-edit-tabular-container table tr td') + .contains('Enter this key') + .next() + .invoke('text') + .then((key) => key.trim()) + .as('secret'); + cy.get('@secret').then((secret) => cy.get('#com-users-method-code').clear().type(TOTP.generate(secret).otp)); + cy.get('#com-users-method-edit').submit(); + cy.get('.com-users-methods-list-method-name-totp .com-users-methods-list-method-record').contains('Test Code'); + cy.get('.com-users-methods-list-method-name-backupcodes .com-users-methods-list-method-record-info a') + .should('have.text', 'Print these codes') + .click(); + cy.get('table > tbody > tr > td').first().invoke('text').then((code) => cy.wrap(/\d{8}/.exec(code)[0]).as('code')); + cy.get('#toolbar-user-mfa-edit-cancel').contains('Back').click(); + cy.clickToolbarButton('Cancel'); + cy.doAdministratorLogout(); + cy.get('#mod-login-username').type(Cypress.env('username')); + cy.get('#mod-login-password').type(Cypress.env('password')); + cy.get('#form-login').submit(); + cy.get('#users-mfa-title').contains('Verification code'); + cy.get('#toolbar-user-mfa-choose-another').click(); + cy.get('a.com-users-method').contains('Backup Codes').click(); + cy.get('#users-mfa-title').contains('Backup Codes'); + cy.get('@code').then((code) => cy.get('#users-mfa-code').clear().type(code)); + cy.get('#users-mfa-captive-form').submit(); + cy.visit('/administrator/index.php?option=com_users&view=users'); + cy.get('.header-profile:visible').click(); + cy.get('.header-profile a.dropdown-item').contains('Edit Account').click(); + cy.get('#myTab div[role="tablist"] button[aria-controls="multifactorauth"]').click(); + cy.get('#com-users-methods-reset-message').contains('is enabled'); + cy.get('.com-users-methods-list-method-name-totp a.com-users-methods-list-method-record-delete').click(); + cy.on('window:confirm', (text) => expect(text).to.contains('Are you sure you want to delete?')); + cy.get('#com-users-methods-reset-message').contains('not enabled'); + }); +}); diff --git a/tests/System/integration/api/Basic.cy.js b/tests/System/integration/api/Basic.cy.js index 337eadedc2a..ab01bc20f6f 100644 --- a/tests/System/integration/api/Basic.cy.js +++ b/tests/System/integration/api/Basic.cy.js @@ -13,7 +13,7 @@ describe('Test that the API', () => { it('returns not found return code when wrong route is set', () => { cy.api_getBearerToken().then((token) => cy.request({ - method: 'GET', url: '/api/index.php/v1/content/articles/1', headers: { Authorization: `Bearer ${token}` }, failOnStatusCode: false, + method: 'GET', url: '/api/index.php/v1/invalid', headers: { Authorization: `Bearer ${token}` }, failOnStatusCode: false, }) .its('status').should('equal', 404)); }); diff --git a/tests/System/integration/site/components/com_config/Config.cy.js b/tests/System/integration/site/components/com_config/Config.cy.js new file mode 100644 index 00000000000..b69209aa1b4 --- /dev/null +++ b/tests/System/integration/site/components/com_config/Config.cy.js @@ -0,0 +1,48 @@ +describe('Test in frontend that the config config view', () => { + beforeEach(() => cy.doFrontendLogin()); + + it('can edit site configuration without menu item', () => { + cy.visit('/index.php?option=com_config&view=config'); + cy.title().should('equal', 'Home'); + cy.get('#jform_sitename_pagetitles').select('After'); + cy.get('#application-form button[data-submit-task="config.apply"]').click(); + + cy.checkForSystemMessage('Configuration saved.'); + cy.title().should('equal', `Home - ${Cypress.env('sitename')}`); + cy.get('#jform_sitename_pagetitles').select('No'); + cy.get('#application-form button[data-submit-task="config.apply"]').click(); + + cy.checkForSystemMessage('Configuration saved.'); + cy.title().should('equal', 'Home'); + }); + + it('can edit site configuration with menu item', () => { + cy.db_createMenuItem({ title: 'automated test site configuration', link: 'index.php?option=com_config&view=config' }) + .then(() => { + cy.visit('/'); + cy.get('a:contains(automated test site configuration)').click(); + + cy.title().should('equal', 'automated test site configuration'); + cy.get('head meta[name=description]').should('not.exist'); + cy.get('#jform_MetaDesc').clear().type('test meta description'); + cy.get('#application-form button[data-submit-task="config.apply"]').click(); + + cy.checkForSystemMessage('Configuration saved.'); + cy.get('head meta[name=description]').should('have.attr', 'content').should('contain', 'test meta description'); + cy.get('#jform_MetaDesc').clear(); + cy.get('#application-form button[data-submit-task="config.apply"]').click(); + + cy.checkForSystemMessage('Configuration saved.'); + cy.get('head meta[name=description]').should('not.exist'); + }); + }); + + it('can toggle inline help options', () => { + cy.visit('/index.php?option=com_config&view=config'); + cy.get('#jform_access-desc').should('be.not.visible'); + cy.get('button.button-inlinehelp').click(); + cy.get('#jform_access-desc').should('be.visible'); + cy.get('button.button-inlinehelp').click(); + cy.get('#jform_access-desc').should('be.not.visible'); + }); +}); diff --git a/tests/System/integration/site/components/com_config/Modules.cy.js b/tests/System/integration/site/components/com_config/Modules.cy.js new file mode 100644 index 00000000000..65dd6da93c1 --- /dev/null +++ b/tests/System/integration/site/components/com_config/Modules.cy.js @@ -0,0 +1,24 @@ +describe('Test in frontend that the config modules view', () => { + beforeEach(() => cy.doFrontendLogin()); + + it('can edit a module', () => { + cy.visit('/'); + cy.get('nav.mod-breadcrumbs__wrapper li.mod-breadcrumbs__here').should('exist'); + cy.get('nav.mod-breadcrumbs__wrapper li.mod-breadcrumbs__divider').should('not.exist'); + cy.get('nav.mod-breadcrumbs__wrapper a.jmodedit').click(); + + cy.title().should('equal', 'Module Settings'); + cy.get('#options button.accordion-button').contains('Options').click(); + cy.get('#jform_params_showHere0').click(); + cy.get('#modules-form button[data-submit-task="modules.apply"]').click(); + + cy.checkForSystemMessage('Module saved.'); + cy.get('nav.mod-breadcrumbs__wrapper li.mod-breadcrumbs__here').should('not.exist'); + cy.get('nav.mod-breadcrumbs__wrapper li.mod-breadcrumbs__divider').should('exist'); + cy.get('#options button.accordion-button').contains('Options').click(); + cy.get('#jform_params_showHere1').click(); + cy.get('#modules-form button[data-submit-task="modules.save"]').click(); + + cy.checkForSystemMessage('Module saved.'); + }); +}); diff --git a/tests/System/integration/site/components/com_config/Templates.cy.js b/tests/System/integration/site/components/com_config/Templates.cy.js new file mode 100644 index 00000000000..85e659c511f --- /dev/null +++ b/tests/System/integration/site/components/com_config/Templates.cy.js @@ -0,0 +1,38 @@ +describe('Test in frontend that the config config view', () => { + beforeEach(() => cy.doFrontendLogin()); + + it('can edit template settings without menu item', () => { + cy.visit('/index.php?option=com_config&view=templates'); + cy.title().should('equal', 'Home'); + cy.get('#params_siteDescription').clear().type('test site description'); + cy.get('#templates-form button[data-submit-task="templates.apply"]').click(); + + cy.checkForSystemMessage('Configuration saved.'); + cy.get('header div.site-description').should('contain', 'test site description'); + cy.get('#params_siteDescription').clear(); + cy.get('#templates-form button[data-submit-task="templates.apply"]').click(); + + cy.checkForSystemMessage('Configuration saved.'); + cy.get('header div.site-description').should('not.exist'); + }); + + it('can edit template settings with menu item', () => { + cy.db_createMenuItem({ title: 'automated test template settings', link: 'index.php?option=com_config&view=templates' }) + .then(() => { + cy.visit('/'); + cy.get('a:contains(automated test template settings)').click(); + + cy.title().should('equal', 'automated test template settings'); + cy.get('#params_brand0').click(); + cy.get('#templates-form button[data-submit-task="templates.apply"]').click(); + + cy.checkForSystemMessage('Configuration saved.'); + cy.get('header div.navbar-brand').should('not.exist'); + cy.get('#params_brand1').click(); + cy.get('#templates-form button[data-submit-task="templates.apply"]').click(); + + cy.checkForSystemMessage('Configuration saved.'); + cy.get('header div.navbar-brand').should('exist'); + }); + }); +}); diff --git a/tests/System/integration/site/components/com_users/Mfa.cy.js b/tests/System/integration/site/components/com_users/Mfa.cy.js new file mode 100644 index 00000000000..91790661a79 --- /dev/null +++ b/tests/System/integration/site/components/com_users/Mfa.cy.js @@ -0,0 +1,103 @@ +import { TOTP } from 'totp-generator'; + +afterEach(() => cy.db_getUserId().then((uid) => cy.task('queryDB', `DELETE FROM #__user_mfa WHERE user_id = ${uid}`))); + +describe('Test in frontend that the user', () => { + it('can login with Multi-factor Authentication (email)', () => { + cy.doFrontendLogin(); + cy.visit('/index.php?option=com_users&view=profile&layout=edit'); + cy.task('clearEmails'); + cy.get('.com-users-methods-list-method-name-email a.com-users-methods-list-method-addnew').click(); + cy.get('#com-users-method-edit-title').clear().type('Test Code'); + cy.task('getMails').then((mails) => { + cy.wrap(mails).should('have.lengthOf', 1); + cy.wrap(mails[0].headers.subject).should('match', /code is -\d{6}-$/); + cy.wrap(/code is -(\d{6})-$/.exec(mails[0].headers.subject)[1]).as('code') + .then((code) => cy.wrap(mails[0].body).should('have.string', `Your authentication code is ${code}.`)); + cy.wrap(mails[0].html).should('be.false'); + }); + cy.get('@code').then((code) => cy.get('#com-users-method-code').clear().type(code)); + cy.get('#com-users-method-edit').submit(); + cy.get('.com-users-methods-list-method-name-email .com-users-methods-list-method-record').contains('Test Code'); + cy.doFrontendLogout(); + cy.get('form.mod-login input[name="username"]').type(Cypress.env('username')); + cy.get('form.mod-login input[name="password"]').type(Cypress.env('password')); + cy.get('form.mod-login').submit(); + cy.get('#users-mfa-title').contains('Test Code'); + cy.task('getMails').then((mails) => { + cy.wrap(mails).should('have.lengthOf', 2); + cy.wrap(mails[1].headers.subject).should('match', /code is -\d{6}-$/); + cy.wrap(/code is -(\d{6})-$/.exec(mails[1].headers.subject)[1]).as('code'); + }); + cy.get('@code').then((code) => cy.get('#users-mfa-code').clear().type(code)); + cy.get('#users-mfa-captive-form').submit(); + cy.visit('/index.php?option=com_users&view=profile&layout=edit'); + cy.get('#com-users-methods-reset-message').contains('is enabled'); + cy.get('.com-users-methods-list-method-name-email a.com-users-methods-list-method-record-delete').click(); + cy.on('window:confirm', (text) => expect(text).to.contains('Are you sure you want to delete?')); + cy.get('#com-users-methods-reset-message').contains('not enabled'); + }); + + it('can login with Multi-factor Authentication (totp)', () => { + cy.doFrontendLogin(); + cy.visit('/index.php?option=com_users&view=profile&layout=edit'); + cy.get('.com-users-methods-list-method-name-totp a.com-users-methods-list-method-addnew').click(); + cy.get('#com-users-method-edit-title').clear().type('Test Code'); + cy.get('.com-users-method-edit-tabular-container table tr td') + .contains('Enter this key') + .next() + .invoke('text') + .then((key) => key.trim()) + .as('secret'); + cy.get('@secret').then((secret) => cy.get('#com-users-method-code').clear().type(TOTP.generate(secret).otp)); + cy.get('#com-users-method-edit').submit(); + cy.get('.com-users-methods-list-method-name-totp .com-users-methods-list-method-record').contains('Test Code'); + cy.doFrontendLogout(); + cy.get('form.mod-login input[name="username"]').type(Cypress.env('username')); + cy.get('form.mod-login input[name="password"]').type(Cypress.env('password')); + cy.get('form.mod-login').submit(); + cy.get('#users-mfa-title').contains('Verification code'); + cy.get('@secret').then((secret) => cy.get('#users-mfa-code').clear().type(TOTP.generate(secret).otp)); + cy.get('#users-mfa-captive-form').submit(); + cy.visit('/index.php?option=com_users&view=profile&layout=edit'); + cy.get('#com-users-methods-reset-message').contains('is enabled'); + cy.get('.com-users-methods-list-method-name-totp a.com-users-methods-list-method-record-delete').click(); + cy.on('window:confirm', (text) => expect(text).to.contains('Are you sure you want to delete?')); + cy.get('#com-users-methods-reset-message').contains('not enabled'); + }); + + it('can login with Multi-factor Authentication (backup codes)', () => { + cy.doFrontendLogin(); + cy.visit('/index.php?option=com_users&view=profile&layout=edit'); + cy.get('.com-users-methods-list-method-name-totp a.com-users-methods-list-method-addnew').click(); + cy.get('#com-users-method-edit-title').clear().type('Test Code'); + cy.get('.com-users-method-edit-tabular-container table tr td') + .contains('Enter this key') + .next() + .invoke('text') + .then((key) => key.trim()) + .as('secret'); + cy.get('@secret').then((secret) => cy.get('#com-users-method-code').clear().type(TOTP.generate(secret).otp)); + cy.get('#com-users-method-edit').submit(); + cy.get('.com-users-methods-list-method-name-totp .com-users-methods-list-method-record').contains('Test Code'); + cy.get('.com-users-methods-list-method-name-backupcodes .com-users-methods-list-method-record-info a') + .should('have.text', 'Print these codes') + .click(); + cy.get('table > tbody > tr > td').first().invoke('text').then((code) => cy.wrap(/\d{8}/.exec(code)[0]).as('code')); + cy.doFrontendLogout(); + cy.get('form.mod-login input[name="username"]').type(Cypress.env('username')); + cy.get('form.mod-login input[name="password"]').type(Cypress.env('password')); + cy.get('form.mod-login').submit(); + cy.get('#users-mfa-title').contains('Verification code'); + cy.get('#users-mfa-captive-form-choose-another a').click(); + cy.get('a.com-users-method').contains('Backup Codes').click(); + cy.get('#users-mfa-title').contains('Backup Codes'); + cy.get('@code').then((code) => cy.get('#users-mfa-code').clear().type(code)); + cy.get('#users-mfa-captive-form').submit(); + cy.visit('/index.php?option=com_users&view=profile&layout=edit'); + cy.get('#com-users-methods-reset-message').contains('is enabled'); + cy.get('.com-users-methods-list-method-name-totp a.com-users-methods-list-method-record-delete').click(); + cy.on('window:confirm', (text) => expect(text).to.contains('Are you sure you want to delete?')); + cy.get('#com-users-methods-reset-message').contains('not enabled'); + }); +}); diff --git a/tests/System/plugins/index.mjs b/tests/System/plugins/index.mjs index 0c36b2f6940..2b558ec0858 100644 --- a/tests/System/plugins/index.mjs +++ b/tests/System/plugins/index.mjs @@ -1,6 +1,7 @@ import { getMails, clearEmails, startMailServer } from './mail.mjs'; import { writeRelativeFile, deleteRelativePath, copyRelativeFile } from './fs.mjs'; import { queryTestDB, deleteInsertedItems } from './db.mjs'; +import { checkForLogs, clearLogs } from './logs.mjs'; /** * Does the setup of the plugins. @@ -17,6 +18,8 @@ export default function setupPlugins(on, config) { writeRelativeFile: ({ path, content, mode }) => writeRelativeFile(path, content, config, mode), deleteRelativePath: (path) => deleteRelativePath(path, config), copyRelativeFile: ({ source, destination }) => copyRelativeFile(source, destination, config), + checkForLogs: () => checkForLogs(config), + clearLogs: () => clearLogs(config), getMails: () => getMails(), clearEmails: () => clearEmails(), startMailServer: () => startMailServer(config), diff --git a/tests/System/plugins/logs.mjs b/tests/System/plugins/logs.mjs new file mode 100644 index 00000000000..dd69be37b20 --- /dev/null +++ b/tests/System/plugins/logs.mjs @@ -0,0 +1,42 @@ +import { existsSync, readFileSync, writeFileSync } from 'node:fs'; + +/** + * Checks if there are entries written to the logs file. + * + * @param {object} config - The Cypress configuration object + * + * @returns null + */ +function checkForLogs(config) { + const { logFile } = config.env; + if (!existsSync(logFile)) { + return null; + } + + const log = readFileSync(logFile, 'utf8'); + if (!log) { + return null; + } + + throw new Error(log); +} + +/** + * Clears the log file. + * + * @param {object} config - The Cypress configuration object + * + * @returns null + */ +function clearLogs(config) { + const { logFile } = config.env; + if (!existsSync(logFile)) { + return null; + } + + writeFileSync(logFile, ''); + + return null; +} + +export { checkForLogs, clearLogs }; diff --git a/tests/System/support/index.js b/tests/System/support/index.js index 56187a334d6..43be275d13c 100644 --- a/tests/System/support/index.js +++ b/tests/System/support/index.js @@ -3,9 +3,11 @@ import('joomla-cypress'); before(() => { cy.task('startMailServer'); + cy.task('clearLogs'); }); afterEach(() => { cy.checkForPhpNoticesOrWarnings(); + cy.task('checkForLogs'); cy.task('cleanupDB'); });