diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 035103d764dc..68eac8b51988 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -2658,6 +2658,13 @@ public function deleteUnexistingFiles($dryRun = false, $suppressOutput = false) $this->fixFilenameCasing(); + /** + * Fix filesystem permissions when updating a new 5.2.0 installation. + * + * @todo: Remove in 6.0 + */ + $this->fixFilesystemPermissions(); + if ($suppressOutput === false && \count($status['folders_errors'])) { echo implode('
', $status['folders_errors']); } @@ -3145,4 +3152,424 @@ protected function fixFilenameCasing() } } } + + /** + * Fix filesystem permissions when updating a new 5.2.0 installation. + * + * @return void + * + * @since __DEPLOY_VERSION__ + * + * @todo 6.0 Remove this method + * + * @deprecated __DEPLOY_VERSION__ will be removed in 6.0 without replacement + */ + protected function fixFilesystemPermissions() + { + // Don't do anything if not updating from a 5.2.0 or 5.2.1 + if ( + empty($this->fromVersion) + || version_compare($this->fromVersion, '5.2.0', 'lt') + || version_compare($this->fromVersion, '5.2.1', 'gt') + ) { + return; + } + + // First check tmp folder if it has mode 777 + if (decoct(fileperms(JPATH_ROOT . '/tmp') & 0777) === '777') { + // We are either on Windows where folders always have 777, or we have to fix permissions + @chmod(JPATH_ROOT . '/tmp', 0755); + clearstatcache(true, JPATH_ROOT . '/tmp'); + } + + // Check tmp folder again if it still has mode 777 + if (decoct(fileperms(JPATH_ROOT . '/tmp') & 0777) === '777') { + // We are on Windows or chmod has no effect + return; + } + + try { + // Using hard-coded string because a new language string would not be available in all cases + Log::add('Fixing permissions for files and folders.', Log::INFO, 'Update'); + } catch (\RuntimeException $exception) { + // Informational log only + } + + $files = [ + '/htaccess.txt', + '/index.php', + '/libraries/.htaccess', + '/libraries/vendor/jfcherng/php-diff/.phpstorm.meta.php', + '/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', + '/LICENSE.txt', + '/README.txt', + '/robots.txt', + '/robots.txt.dist', + '/tmp/index.html', + '/web.config.txt', + ]; + + $folders = [ + '/administrator', + '/administrator/cache', + '/administrator/components', + '/administrator/help', + '/administrator/help/en-GB', + '/administrator/includes', + '/administrator/language', + '/administrator/language/en-GB', + '/administrator/language/overrides', + '/administrator/logs', + '/administrator/manifests', + '/administrator/manifests/files', + '/administrator/manifests/libraries', + '/administrator/manifests/packages', + '/administrator/modules', + '/administrator/templates', + '/api', + '/api/components', + '/api/includes', + '/api/language', + '/api/language/en-GB', + '/cache', + '/cli', + '/components', + '/images', + '/images/banners', + '/images/headers', + '/images/sampledata', + '/images/sampledata/cassiopeia', + '/includes', + '/language', + '/language/en-GB', + '/language/overrides', + '/layouts', + '/layouts/chromes', + '/layouts/libraries', + '/layouts/libraries/html', + '/layouts/libraries/html/bootstrap', + '/layouts/libraries/html/bootstrap/modal', + '/layouts/libraries/html/bootstrap/tab', + '/libraries', + '/libraries/php-encryption', + '/libraries/phpass', + '/media', + '/media/cache', + '/media/templates', + '/media/templates/administrator', + '/media/templates/site', + '/media/vendor', + '/modules', + '/plugins', + '/templates', + ]; + + $foldersRecursive = [ + '/administrator/components/com_actionlogs', + '/administrator/components/com_admin', + '/administrator/components/com_ajax', + '/administrator/components/com_associations', + '/administrator/components/com_banners', + '/administrator/components/com_cache', + '/administrator/components/com_categories', + '/administrator/components/com_checkin', + '/administrator/components/com_config', + '/administrator/components/com_contact', + '/administrator/components/com_content', + '/administrator/components/com_contenthistory', + '/administrator/components/com_cpanel', + '/administrator/components/com_fields', + '/administrator/components/com_finder', + '/administrator/components/com_guidedtours', + '/administrator/components/com_installer', + '/administrator/components/com_joomlaupdate', + '/administrator/components/com_languages', + '/administrator/components/com_login', + '/administrator/components/com_mails', + '/administrator/components/com_media', + '/administrator/components/com_menus', + '/administrator/components/com_messages', + '/administrator/components/com_modules', + '/administrator/components/com_newsfeeds', + '/administrator/components/com_plugins', + '/administrator/components/com_postinstall', + '/administrator/components/com_privacy', + '/administrator/components/com_redirect', + '/administrator/components/com_scheduler', + '/administrator/components/com_tags', + '/administrator/components/com_templates', + '/administrator/components/com_users', + '/administrator/components/com_workflow', + '/administrator/components/com_wrapper', + '/administrator/modules/mod_custom', + '/administrator/modules/mod_feed', + '/administrator/modules/mod_frontend', + '/administrator/modules/mod_guidedtours', + '/administrator/modules/mod_latest', + '/administrator/modules/mod_latestactions', + '/administrator/modules/mod_logged', + '/administrator/modules/mod_login', + '/administrator/modules/mod_loginsupport', + '/administrator/modules/mod_menu', + '/administrator/modules/mod_messages', + '/administrator/modules/mod_multilangstatus', + '/administrator/modules/mod_popular', + '/administrator/modules/mod_post_installation_messages', + '/administrator/modules/mod_privacy_dashboard', + '/administrator/modules/mod_privacy_status', + '/administrator/modules/mod_quickicon', + '/administrator/modules/mod_sampledata', + '/administrator/modules/mod_stats_admin', + '/administrator/modules/mod_submenu', + '/administrator/modules/mod_title', + '/administrator/modules/mod_toolbar', + '/administrator/modules/mod_user', + '/administrator/modules/mod_version', + '/administrator/templates/atum', + '/administrator/templates/system', + '/api/components/com_banners', + '/api/components/com_categories', + '/api/components/com_config', + '/api/components/com_contact', + '/api/components/com_content', + '/api/components/com_contenthistory', + '/api/components/com_fields', + '/api/components/com_installer', + '/api/components/com_languages', + '/api/components/com_media', + '/api/components/com_menus', + '/api/components/com_messages', + '/api/components/com_modules', + '/api/components/com_newsfeeds', + '/api/components/com_plugins', + '/api/components/com_privacy', + '/api/components/com_redirect', + '/api/components/com_tags', + '/api/components/com_templates', + '/api/components/com_users', + '/components/com_ajax', + '/components/com_banners', + '/components/com_config', + '/components/com_contact', + '/components/com_content', + '/components/com_contenthistory', + '/components/com_fields', + '/components/com_finder', + '/components/com_media', + '/components/com_menus', + '/components/com_modules', + '/components/com_newsfeeds', + '/components/com_privacy', + '/components/com_tags', + '/components/com_users', + '/components/com_wrapper', + '/layouts/joomla', + '/layouts/plugins', + '/libraries/src', + '/libraries/vendor', + '/media/com_actionlogs', + '/media/com_admin', + '/media/com_associations', + '/media/com_banners', + '/media/com_cache', + '/media/com_categories', + '/media/com_config', + '/media/com_contact', + '/media/com_content', + '/media/com_contenthistory', + '/media/com_cpanel', + '/media/com_fields', + '/media/com_finder', + '/media/com_guidedtours', + '/media/com_installer', + '/media/com_joomlaupdate', + '/media/com_languages', + '/media/com_mails', + '/media/com_media', + '/media/com_menus', + '/media/com_modules', + '/media/com_scheduler', + '/media/com_tags', + '/media/com_templates', + '/media/com_users', + '/media/com_workflow', + '/media/com_wrapper', + '/media/layouts', + '/media/legacy', + '/media/mailto', + '/media/mod_articles', + '/media/mod_articles_news', + '/media/mod_languages', + '/media/mod_login', + '/media/mod_menu', + '/media/mod_quickicon', + '/media/mod_sampledata', + '/media/plg_behaviour_compat', + '/media/plg_captcha_recaptcha', + '/media/plg_captcha_recaptcha_invisible', + '/media/plg_content_vote', + '/media/plg_editors-xtd_image', + '/media/plg_editors_codemirror', + '/media/plg_editors_none', + '/media/plg_editors_tinymce', + '/media/plg_installer_folderinstaller', + '/media/plg_installer_packageinstaller', + '/media/plg_installer_urlinstaller', + '/media/plg_installer_webinstaller', + '/media/plg_media-action_crop', + '/media/plg_media-action_resize', + '/media/plg_media-action_rotate', + '/media/plg_multifactorauth_email', + '/media/plg_multifactorauth_fixed', + '/media/plg_multifactorauth_totp', + '/media/plg_multifactorauth_webauthn', + '/media/plg_multifactorauth_yubikey', + '/media/plg_quickicon_eos', + '/media/plg_quickicon_extensionupdate', + '/media/plg_quickicon_joomlaupdate', + '/media/plg_quickicon_overridecheck', + '/media/plg_quickicon_privacycheck', + '/media/plg_system_debug', + '/media/plg_system_guidedtours', + '/media/plg_system_jooa11y', + '/media/plg_system_schedulerunner', + '/media/plg_system_shortcut', + '/media/plg_system_stats', + '/media/plg_system_webauthn', + '/media/plg_user_token', + '/media/system', + '/media/templates/administrator/atum', + '/media/templates/site/cassiopeia', + '/media/vendor/accessibility', + '/media/vendor/awesomplete', + '/media/vendor/bootstrap', + '/media/vendor/choicesjs', + '/media/vendor/chosen', + '/media/vendor/codemirror', + '/media/vendor/cropperjs', + '/media/vendor/debugbar', + '/media/vendor/diff', + '/media/vendor/dragula', + '/media/vendor/es-module-shims', + '/media/vendor/focus-visible', + '/media/vendor/fontawesome-free', + '/media/vendor/hotkeysjs', + '/media/vendor/joomla-custom-elements', + '/media/vendor/jquery', + '/media/vendor/jquery-migrate', + '/media/vendor/mediaelement', + '/media/vendor/metismenujs', + '/media/vendor/minicolors', + '/media/vendor/qrcode', + '/media/vendor/roboto-fontface', + '/media/vendor/sa11y', + '/media/vendor/shepherdjs', + '/media/vendor/short-and-sweet', + '/media/vendor/skipto', + '/media/vendor/tinymce', + '/media/vendor/webcomponentsjs', + '/modules/mod_articles', + '/modules/mod_articles_archive', + '/modules/mod_articles_categories', + '/modules/mod_articles_category', + '/modules/mod_articles_latest', + '/modules/mod_articles_news', + '/modules/mod_articles_popular', + '/modules/mod_banners', + '/modules/mod_breadcrumbs', + '/modules/mod_custom', + '/modules/mod_feed', + '/modules/mod_finder', + '/modules/mod_footer', + '/modules/mod_languages', + '/modules/mod_login', + '/modules/mod_menu', + '/modules/mod_random_image', + '/modules/mod_related_items', + '/modules/mod_stats', + '/modules/mod_syndicate', + '/modules/mod_tags_popular', + '/modules/mod_tags_similar', + '/modules/mod_users_latest', + '/modules/mod_whosonline', + '/modules/mod_wrapper', + '/plugins/actionlog', + '/plugins/api-authentication', + '/plugins/authentication', + '/plugins/behaviour', + '/plugins/captcha', + '/plugins/content', + '/plugins/editors', + '/plugins/editors-xtd', + '/plugins/extension', + '/plugins/fields', + '/plugins/filesystem', + '/plugins/finder', + '/plugins/installer', + '/plugins/media-action', + '/plugins/multifactorauth', + '/plugins/privacy', + '/plugins/quickicon', + '/plugins/sampledata', + '/plugins/schemaorg', + '/plugins/system', + '/plugins/task', + '/plugins/user', + '/plugins/webservices', + '/plugins/workflow', + '/templates/cassiopeia', + '/templates/system', + ]; + + foreach ($files as $file) { + if (is_file(JPATH_ROOT . $file) && decoct(fileperms(JPATH_ROOT . $file) & 0777) === '777') { + @chmod(JPATH_ROOT . $file, 0644); + } + } + + foreach ($folders as $folder) { + if (is_dir(JPATH_ROOT . $folder) && decoct(fileperms(JPATH_ROOT . $folder) & 0777) === '777') { + @chmod(JPATH_ROOT . $folder, 0755); + } + + foreach (Folder::files(JPATH_ROOT . $folder, '.', false, true) as $file) { + if (decoct(fileperms($file) & 0777) === '777') { + @chmod($file, 0644); + } + } + } + + foreach ($foldersRecursive as $parentFolder) { + if (is_dir(JPATH_ROOT . $parentFolder)) { + if (decoct(fileperms(JPATH_ROOT . $parentFolder) & 0777) === '777') { + @chmod(JPATH_ROOT . $parentFolder, 0755); + } + + foreach (Folder::folders(JPATH_ROOT . $parentFolder, '.', true, true) as $folder) { + if (decoct(fileperms($folder) & 0777) === '777') { + @chmod($folder, 0755); + } + } + + foreach (Folder::files(JPATH_ROOT . $parentFolder, '.', true, true) as $file) { + if (decoct(fileperms($file) & 0777) === '777') { + @chmod($file, 0644); + } + } + } + } + } } diff --git a/administrator/components/com_admin/sql/updates/mysql/5.2.0-2024-09-24.sql b/administrator/components/com_admin/sql/updates/mysql/5.2.1-2024-09-24.sql similarity index 100% rename from administrator/components/com_admin/sql/updates/mysql/5.2.0-2024-09-24.sql rename to administrator/components/com_admin/sql/updates/mysql/5.2.1-2024-09-24.sql diff --git a/administrator/components/com_admin/sql/updates/postgresql/5.2.0-2024-09-24.sql b/administrator/components/com_admin/sql/updates/postgresql/5.2.1-2024-09-24.sql similarity index 100% rename from administrator/components/com_admin/sql/updates/postgresql/5.2.0-2024-09-24.sql rename to administrator/components/com_admin/sql/updates/postgresql/5.2.1-2024-09-24.sql diff --git a/administrator/components/com_content/src/Model/ArticlesModel.php b/administrator/components/com_content/src/Model/ArticlesModel.php index 51c43e1e2988..ab9b346a918b 100644 --- a/administrator/components/com_content/src/Model/ArticlesModel.php +++ b/administrator/components/com_content/src/Model/ArticlesModel.php @@ -143,6 +143,14 @@ protected function populateState($ordering = 'a.id', $direction = 'desc') $this->context .= '.' . $forcedLanguage; } + // Required content filters for the administrator menu + $this->getUserStateFromRequest($this->context . '.filter.category_id', 'filter_category_id'); + $this->getUserStateFromRequest($this->context . '.filter.level', 'filter_level'); + $this->getUserStateFromRequest($this->context . '.filter.author_id', 'filter_author_id'); + $this->getUserStateFromRequest($this->context . '.filter.tag', 'filter_tag', ''); + $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access'); + $this->getUserStateFromRequest($this->context . '.filter.language', 'filter_language', ''); + // List state information. parent::populateState($ordering, $direction); diff --git a/administrator/components/com_guidedtours/src/Controller/StepController.php b/administrator/components/com_guidedtours/src/Controller/StepController.php index 172062bcd7ae..045e1409c799 100644 --- a/administrator/components/com_guidedtours/src/Controller/StepController.php +++ b/administrator/components/com_guidedtours/src/Controller/StepController.php @@ -23,4 +23,21 @@ */ class StepController extends FormController { + /** + * Gets the URL arguments to append to a list redirect. + * + * @return string The arguments to append to the redirect URL. + * + * @since __DEPLOY_VERSION__ + */ + protected function getRedirectToListAppend() + { + $append = parent::getRedirectToListAppend(); + $tourId = $this->app->getUserState('com_guidedtours.tour_id'); + if (!empty($tourId)) { + $append .= '&tour_id=' . $tourId; + } + + return $append; + } } diff --git a/administrator/components/com_installer/src/Model/UpdateModel.php b/administrator/components/com_installer/src/Model/UpdateModel.php index 92c472a1e2ac..2cd000b8ca4a 100644 --- a/administrator/components/com_installer/src/Model/UpdateModel.php +++ b/administrator/components/com_installer/src/Model/UpdateModel.php @@ -24,6 +24,7 @@ use Joomla\Database\Exception\ExecutionFailureException; use Joomla\Database\ParameterType; use Joomla\Database\QueryInterface; +use Joomla\Filesystem\Path; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects @@ -358,10 +359,24 @@ public function update($uids, $minimumStability = Updater::STABILITY_STABLE) $update->set('extra_query', $updateSiteInstance->extra_query); } - $this->preparePreUpdate($update, $instance); - - // Install sets state and enqueues messages - $res = $this->install($update); + try { + $this->preparePreUpdate($update, $instance); + + // Install sets state and enqueues messages + $res = $this->install($update); + } catch (\Throwable $t) { + $res = false; + + Factory::getApplication()->enqueueMessage( + Text::sprintf( + 'COM_INSTALLER_UPDATE_ERROR', + $instance->name, + $t->getMessage(), + (JDEBUG ? str_replace(JPATH_ROOT, 'JROOT', Path::clean($t->getFile())) . ':' . $t->getLine() : '') + ), + 'error' + ); + } if ($res) { $instance->delete($uid); diff --git a/administrator/components/com_menus/src/Model/MenutypesModel.php b/administrator/components/com_menus/src/Model/MenutypesModel.php index 3a73b5fa0dc1..f38f88e66743 100644 --- a/administrator/components/com_menus/src/Model/MenutypesModel.php +++ b/administrator/components/com_menus/src/Model/MenutypesModel.php @@ -411,7 +411,14 @@ protected function getTypeOptionsFromManifest($component) $request['sub'] = (string) $attributes->sub; } - $o->request = array_filter($request, 'strlen'); + $o->request = array_filter($request, function ($value) { + if (\is_array($value)) { + return !empty($value); + } + + return \strlen($value); + }); + $options[] = new CMSObject($o); // Do not repeat the default view link (index.php?option=com_abc). diff --git a/administrator/components/com_users/tmpl/captive/select.php b/administrator/components/com_users/tmpl/captive/select.php index 8f63b1127f39..e3afe6ef28f0 100644 --- a/administrator/components/com_users/tmpl/captive/select.php +++ b/administrator/components/com_users/tmpl/captive/select.php @@ -31,7 +31,7 @@

-
+
records as $record) : if (!array_key_exists($record->method, $this->mfaMethods) && ($record->method != 'backupcodes')) { continue; @@ -48,11 +48,13 @@ $methodName = $this->getModel()->translateMethodName($record->method); ?> - +
<?php echo $this->escape(strip_tags($record->title)) ?> + class="img-fluid" /> +
allowEntryBatching || !$allowEntryBatching) : ?> method === 'backupcodes') : ?> diff --git a/administrator/language/en-GB/com_installer.ini b/administrator/language/en-GB/com_installer.ini index 48f7bf5477eb..f93f2d1d4e51 100644 --- a/administrator/language/en-GB/com_installer.ini +++ b/administrator/language/en-GB/com_installer.ini @@ -258,6 +258,7 @@ COM_INSTALLER_UNINSTALL_ERROR="Error uninstalling %s." COM_INSTALLER_UNINSTALL_ERROR_LOCKED_EXTENSION="The extension \"%1$s\" (ID %2$s) is locked and cannot be uninstalled." COM_INSTALLER_UNINSTALL_SUCCESS="Uninstalling the %s was successful." COM_INSTALLER_UNPACK_ERROR="Failed to extract file: %s" +COM_INSTALLER_UPDATE_ERROR="Error updating: %1$s
%2$s
%3$s
" COM_INSTALLER_UPDATE_FILTER_SEARCH_DESC="Search in extension name. Prefix with ID:, UID: or EID: to search for an update ID, update site ID or extension ID." COM_INSTALLER_UPDATE_FILTER_SEARCH_LABEL="Search Extensions with Updates" COM_INSTALLER_UPDATE_FORM_EDIT="Edit Update Site" diff --git a/administrator/language/en-GB/plg_schemaorg_blogposting.ini b/administrator/language/en-GB/plg_schemaorg_blogposting.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_blogposting.sys.ini b/administrator/language/en-GB/plg_schemaorg_blogposting.sys.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_book.ini b/administrator/language/en-GB/plg_schemaorg_book.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_book.sys.ini b/administrator/language/en-GB/plg_schemaorg_book.sys.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_custom.ini b/administrator/language/en-GB/plg_schemaorg_custom.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_custom.sys.ini b/administrator/language/en-GB/plg_schemaorg_custom.sys.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_event.ini b/administrator/language/en-GB/plg_schemaorg_event.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_event.sys.ini b/administrator/language/en-GB/plg_schemaorg_event.sys.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_jobposting.ini b/administrator/language/en-GB/plg_schemaorg_jobposting.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_jobposting.sys.ini b/administrator/language/en-GB/plg_schemaorg_jobposting.sys.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_organization.ini b/administrator/language/en-GB/plg_schemaorg_organization.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_organization.sys.ini b/administrator/language/en-GB/plg_schemaorg_organization.sys.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_person.ini b/administrator/language/en-GB/plg_schemaorg_person.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_person.sys.ini b/administrator/language/en-GB/plg_schemaorg_person.sys.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_recipe.ini b/administrator/language/en-GB/plg_schemaorg_recipe.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_schemaorg_recipe.sys.ini b/administrator/language/en-GB/plg_schemaorg_recipe.sys.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_system_schemaorg.ini b/administrator/language/en-GB/plg_system_schemaorg.ini old mode 100755 new mode 100644 diff --git a/administrator/language/en-GB/plg_system_schemaorg.sys.ini b/administrator/language/en-GB/plg_system_schemaorg.sys.ini old mode 100755 new mode 100644 diff --git a/administrator/templates/atum/component.php b/administrator/templates/atum/component.php index dd9aa21b45c1..f230be351754 100644 --- a/administrator/templates/atum/component.php +++ b/administrator/templates/atum/component.php @@ -27,23 +27,36 @@ $linkColorDark = $this->params->get('link-color-dark', '#6fbfdb'); list($rd, $gd, $bd) = sscanf($linkColorDark, "#%02x%02x%02x"); +$adjustColorLightness = function ($r, $g, $b, $percent) { + $adjust = function ($color) use ($percent) { + $newColor = $color + ($color * $percent / 100); + return min(max(0, $newColor), 255); + }; + return [$adjust($r), $adjust($g), $adjust($b)]; +}; + +list($lighterRd, $lighterGd, $lighterBd) = $adjustColorLightness($rd, $gd, $bd, 10); +$linkColorDarkHvr = sprintf("%d, %d, %d", $lighterRd, $lighterGd, $lighterBd); + // Enable assets $wa->usePreset('template.atum.' . ($this->direction === 'rtl' ? 'rtl' : 'ltr')) ->useStyle('template.active.language') ->useStyle('template.user') ->addInlineStyle(':root { - --hue: ' . $matches[1] . '; - --template-bg-light: ' . $this->params->get('bg-light', 'var(--template-bg-light)') . '; - --template-text-dark: ' . $this->params->get('text-dark', 'var(--template-text-dark)') . '; - --template-text-light: ' . $this->params->get('text-light', 'var(--template-text-light)') . '; - --link-color: ' . $linkColor . '; + --hue: ' . $matches[1] . '; + --template-bg-light: ' . $this->params->get('bg-light', 'var(--template-bg-light)') . '; + --template-text-dark: ' . $this->params->get('text-dark', 'var(--template-text-dark)') . '; + --template-text-light: ' . $this->params->get('text-light', 'var(--template-text-light)') . '; + --link-color: ' . $linkColor . '; --link-color-rgb: ' . $r . ',' . $g . ',' . $b . '; - --template-special-color: ' . $this->params->get('special-color', 'var(--template-special-color)') . '; - }') - ->addInlineStyle('@media (prefers-color-scheme: dark) { :root { - --link-color: ' . $linkColorDark . '; - --link-color-rgb: ' . $rd . ',' . $gd . ',' . $bd . '; - }}'); + --template-special-color: ' . $this->params->get('special-color', 'var(--template-special-color)') . '; + }') + ->addInlineStyle(':root[data-color-scheme="dark"] { + --link-color: ' . $linkColorDark . '; + --link-color-rgb: ' . $rd . ',' . $gd . ',' . $bd . '; + --link-color-rgb-hvr: ' . $linkColorDarkHvr . '; + --template-special-color: #6fbfdb; + }'); // No template.js for modals $wa->disableScript('template.atum'); @@ -75,14 +88,17 @@ ?> -> +> + + + diff --git a/build/media_source/com_finder/js/debug.es6.js b/build/media_source/com_finder/js/debug.es6.js index 274ba59c49d3..aab5ee479fa8 100644 --- a/build/media_source/com_finder/js/debug.es6.js +++ b/build/media_source/com_finder/js/debug.es6.js @@ -24,11 +24,14 @@ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, onSuccess: (response) => { const output = document.getElementById('indexer-output'); + const allowedHtml = { + fieldset: [], legend: [], dl: ['class'], dt: ['class'], dd: ['class'], + }; try { const parsed = JSON.parse(response); - output.innerHTML = parsed.rendered; + output.innerHTML = Joomla.sanitizeHtml(parsed.rendered, allowedHtml); } catch (e) { - output.innerHTML = response; + output.innerHTML = Joomla.sanitizeHtml(response, allowedHtml); } }, onError: (xhr) => { diff --git a/build/media_source/com_menus/js/admin-item-edit_container.es6.js b/build/media_source/com_menus/js/admin-item-edit_container.es6.js index 75cdd8b5e197..68b444bbbcaf 100644 --- a/build/media_source/com_menus/js/admin-item-edit_container.es6.js +++ b/build/media_source/com_menus/js/admin-item-edit_container.es6.js @@ -35,7 +35,7 @@ const selfChecked = isChecked(target); if (root) { - getTreeElements(root).map((element) => toggleState(element, selfChecked)); + getTreeElements(root).forEach((element) => toggleState(element, selfChecked)); } }; diff --git a/build/media_source/mod_articles/css/mod-articles.css b/build/media_source/mod_articles/css/mod-articles.css index 3cad0a1286bb..82a6501d1d0a 100644 --- a/build/media_source/mod_articles/css/mod-articles.css +++ b/build/media_source/mod_articles/css/mod-articles.css @@ -1,23 +1,3 @@ -.mod-articles-item-card .mod-articles-link::before { - position: absolute; - z-index: 1; - content: ""; - inset: 0; -} -.mod-articles-item-card { - position: relative; -} -.mod-articles-item-card:focus-within { - box-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring-color); -} -.mod-articles-item-card:focus-within :focus { - border: none; - outline: none; - box-shadow: none; -} -.mod-articles-item-card:hover { - box-shadow: 1px 1px 4px #0000001a; -} .mod-articles-item { display: flex; flex-direction: column; @@ -29,8 +9,8 @@ margin-bottom: .5rem; } @supports (container-type: inline-size) { - div:has(.mod-articles-grid), - section:has(.mod-articles-grid) { + div:has(> .mod-articles-grid), + section:has(> .mod-articles-grid) { container-type: inline-size; } } diff --git a/build/media_source/plg_captcha_recaptcha_invisible/js/recaptcha.es6.js b/build/media_source/plg_captcha_recaptcha_invisible/js/recaptcha.es6.js index a66792e32377..a4b05dbacd04 100644 --- a/build/media_source/plg_captcha_recaptcha_invisible/js/recaptcha.es6.js +++ b/build/media_source/plg_captcha_recaptcha_invisible/js/recaptcha.es6.js @@ -10,7 +10,7 @@ window.JoomlainitReCaptchaInvisible = () => { const optionKeys = ['sitekey', 'badge', 'size', 'tabindex', 'callback', 'expired-callback', 'error-callback']; - document.getElementsByClassName('g-recaptcha').forEach((element) => { + document.querySelectorAll('.g-recaptcha').forEach((element) => { let options = {}; if (element.dataset) { diff --git a/build/media_source/system/js/treeselectmenu.es6.js b/build/media_source/system/js/treeselectmenu.es6.js index 0bd38969bb3a..e12072137416 100644 --- a/build/media_source/system/js/treeselectmenu.es6.js +++ b/build/media_source/system/js/treeselectmenu.es6.js @@ -3,7 +3,7 @@ * @license GNU General Public License version 2 or later; see LICENSE.txt */ -const treeselectmenu = document.getElementById('treeselectmenu').innerHTML; +const treeselectmenu = document.getElementById('treeselectmenu'); const direction = (document.dir !== undefined) ? document.dir : document.documentElement.dir; document.querySelectorAll('.treeselect li').forEach((li) => { @@ -20,11 +20,14 @@ document.querySelectorAll('.treeselect li').forEach((li) => { li.querySelector('span.icon-').classList.add('treeselect-toggle', 'icon-chevron-down'); // Append drop down menu in nodes - li.querySelector('div.treeselect-item label').insertAdjacentHTML('afterend', treeselectmenu); + if (treeselectmenu) { + li.querySelector('div.treeselect-item label').insertAdjacentHTML('afterend', treeselectmenu.innerHTML); + } const sub = li.querySelector('ul.treeselect-sub'); - if (!sub.querySelector('ul.treeselect-sub')) { - li.querySelector('div.treeselect-menu-expand').remove(); + const expand = li.querySelector('div.treeselect-menu-expand'); + if (!sub.querySelector('ul.treeselect-sub') && expand) { + expand.remove(); } } }); 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 7364d4590dc0..56d9b600906d 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 @@ -21,7 +21,7 @@ } } - &.view-user, &.view-methods { + &.view-user, &.view-methods, &.view-captive { #com-users-methods-reset-container { background-color: $users-methods-reset-cont-bg; } diff --git a/components/com_tags/src/Service/Router.php b/components/com_tags/src/Service/Router.php index e96b23363f43..9c2118117bf7 100644 --- a/components/com_tags/src/Service/Router.php +++ b/components/com_tags/src/Service/Router.php @@ -76,7 +76,12 @@ public function __construct(SiteApplication $app, AbstractMenu $menu, ?CategoryF parent::__construct($app, $menu); $sefPlugin = PluginHelper::getPlugin('system', 'sef'); - $this->sefparams = new Registry($sefPlugin->params); + + if ($sefPlugin) { + $this->sefparams = new Registry($sefPlugin->params); + } else { + $this->sefparams = new Registry(); + } $this->buildLookup(); } diff --git a/components/com_wrapper/tmpl/wrapper/default.xml b/components/com_wrapper/tmpl/wrapper/default.xml index 7f8f5a304bcf..697171d22ab9 100644 --- a/components/com_wrapper/tmpl/wrapper/default.xml +++ b/components/com_wrapper/tmpl/wrapper/default.xml @@ -18,6 +18,7 @@ type="url" validate="url" filter="url" + relative="true" label="COM_WRAPPER_FIELD_URL_LABEL" required="true" /> diff --git a/composer.json b/composer.json index b9c2006bb3b5..b0689ad9e729 100644 --- a/composer.json +++ b/composer.json @@ -116,8 +116,8 @@ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", "joomla/mediawiki": "^3.0", "joomla/test": "~3.0", - "phpstan/phpstan": "^1.12.4", - "phpstan/phpstan-deprecation-rules": "^1.2.1" + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0" }, "replace": { "paragonie/random_compat": "9.99.99", diff --git a/composer.lock b/composer.lock index 5f5acb250f66..8fdae5799e8f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b4730542e342b0e2e39d4cfb81f5be63", + "content-hash": "0f1f9d337d9ee7e6f717bf13b606a359", "packages": [ { "name": "algo26-matthias/idna-convert", @@ -7682,20 +7682,20 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.4", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "ffa517cb918591b93acc9b95c0bebdcd0e4538bd" + "reference": "72115ab2bf1e40af1f9b238938d493ba7f3221e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ffa517cb918591b93acc9b95c0bebdcd0e4538bd", - "reference": "ffa517cb918591b93acc9b95c0bebdcd0e4538bd", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/72115ab2bf1e40af1f9b238938d493ba7f3221e7", + "reference": "72115ab2bf1e40af1f9b238938d493ba7f3221e7", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -7736,30 +7736,30 @@ "type": "github" } ], - "time": "2024-09-19T07:58:01+00:00" + "time": "2024-11-11T07:06:55+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", - "version": "1.2.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-deprecation-rules.git", - "reference": "f94d246cc143ec5a23da868f8f7e1393b50eaa82" + "reference": "81833b5787e2e8f451b31218875e29e4ed600ab2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/f94d246cc143ec5a23da868f8f7e1393b50eaa82", - "reference": "f94d246cc143ec5a23da868f8f7e1393b50eaa82", + "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/81833b5787e2e8f451b31218875e29e4ed600ab2", + "reference": "81833b5787e2e8f451b31218875e29e4ed600ab2", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.12" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5" + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" }, "type": "phpstan-extension", "extra": { @@ -7781,9 +7781,9 @@ "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", "support": { "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", - "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.2.1" + "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.0" }, - "time": "2024-09-11T15:52:35+00:00" + "time": "2024-10-26T16:04:11+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/libraries/src/Component/Router/Rules/MenuRules.php b/libraries/src/Component/Router/Rules/MenuRules.php index 9fc82938f5e6..4deac6ec64a1 100644 --- a/libraries/src/Component/Router/Rules/MenuRules.php +++ b/libraries/src/Component/Router/Rules/MenuRules.php @@ -63,7 +63,12 @@ public function __construct(RouterView $router) { $this->router = $router; $sefPlugin = PluginHelper::getPlugin('system', 'sef'); - $this->sefparams = new Registry($sefPlugin->params); + + if ($sefPlugin) { + $this->sefparams = new Registry($sefPlugin->params); + } else { + $this->sefparams = new Registry(); + } $this->buildLookup(); } diff --git a/libraries/src/Form/Field/SchemaorgComponentSectionsField.php b/libraries/src/Form/Field/SchemaorgComponentSectionsField.php old mode 100755 new mode 100644 diff --git a/libraries/src/MVC/Model/AdminModel.php b/libraries/src/MVC/Model/AdminModel.php index 78204bf2a8fa..9d9f568c5f1a 100644 --- a/libraries/src/MVC/Model/AdminModel.php +++ b/libraries/src/MVC/Model/AdminModel.php @@ -1108,8 +1108,6 @@ public function publish(&$pks, $value = 1) // Prune items that you can't change. unset($pks[$i]); - - return false; } /** diff --git a/libraries/src/Mail/MailTemplate.php b/libraries/src/Mail/MailTemplate.php index 17bd3671c3bb..c718d1daac19 100644 --- a/libraries/src/Mail/MailTemplate.php +++ b/libraries/src/Mail/MailTemplate.php @@ -328,8 +328,6 @@ public function send() $htmlBody = nl2br($this->replaceTags(Text::_($mail->body), $plainData, true), false); } - $htmlBody = MailHelper::convertRelativeToAbsoluteUrls($htmlBody); - if ($useLayout) { // Add additional data to the layout template $this->addLayoutTemplateData([ @@ -381,6 +379,8 @@ public function send() $htmlBody = $this->replaceTags(Text::_($htmlBody), $this->data); } + $htmlBody = MailHelper::convertRelativeToAbsoluteUrls($htmlBody); + $this->mailer->setBody($htmlBody); } diff --git a/libraries/src/Schemaorg/SchemaorgPluginTrait.php b/libraries/src/Schemaorg/SchemaorgPluginTrait.php old mode 100755 new mode 100644 diff --git a/libraries/src/Schemaorg/SchemaorgServiceInterface.php b/libraries/src/Schemaorg/SchemaorgServiceInterface.php old mode 100755 new mode 100644 diff --git a/libraries/src/Schemaorg/SchemaorgServiceTrait.php b/libraries/src/Schemaorg/SchemaorgServiceTrait.php old mode 100755 new mode 100644 diff --git a/modules/mod_wrapper/mod_wrapper.xml b/modules/mod_wrapper/mod_wrapper.xml index b44cd4c9ea3e..53215aab1974 100644 --- a/modules/mod_wrapper/mod_wrapper.xml +++ b/modules/mod_wrapper/mod_wrapper.xml @@ -28,6 +28,7 @@ type="url" validate="url" filter="url" + relative="true" label="MOD_WRAPPER_FIELD_URL_LABEL" required="true" /> diff --git a/plugins/actionlog/joomla/src/Extension/Joomla.php b/plugins/actionlog/joomla/src/Extension/Joomla.php index be2683ae77b9..64d56acf7422 100644 --- a/plugins/actionlog/joomla/src/Extension/Joomla.php +++ b/plugins/actionlog/joomla/src/Extension/Joomla.php @@ -531,11 +531,7 @@ public function onExtensionAfterSave(Model\AfterSaveEvent $event): void $table = $event->getItem(); $isNew = $event->getIsNew(); - $option = $this->getApplication()->getInput()->getCmd('option'); - - if ($table->module != null) { - $option = 'com_modules'; - } + [$option] = explode('.', $context); if (!$this->checkLoggable($option)) { return; diff --git a/plugins/schemaorg/blogposting/blogposting.xml b/plugins/schemaorg/blogposting/blogposting.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/blogposting/forms/schemaorg.xml b/plugins/schemaorg/blogposting/forms/schemaorg.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/blogposting/src/Extension/BlogPosting.php b/plugins/schemaorg/blogposting/src/Extension/BlogPosting.php old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/book/book.xml b/plugins/schemaorg/book/book.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/book/forms/schemaorg.xml b/plugins/schemaorg/book/forms/schemaorg.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/book/src/Extension/Book.php b/plugins/schemaorg/book/src/Extension/Book.php old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/custom/custom.xml b/plugins/schemaorg/custom/custom.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/custom/forms/schemaorg.xml b/plugins/schemaorg/custom/forms/schemaorg.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/custom/src/Extension/Custom.php b/plugins/schemaorg/custom/src/Extension/Custom.php old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/event/event.xml b/plugins/schemaorg/event/event.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/event/forms/schemaorg.xml b/plugins/schemaorg/event/forms/schemaorg.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/event/src/Extension/Event.php b/plugins/schemaorg/event/src/Extension/Event.php old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/jobposting/forms/schemaorg.xml b/plugins/schemaorg/jobposting/forms/schemaorg.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/jobposting/jobposting.xml b/plugins/schemaorg/jobposting/jobposting.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/jobposting/src/Extension/JobPosting.php b/plugins/schemaorg/jobposting/src/Extension/JobPosting.php old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/organization/forms/schemaorg.xml b/plugins/schemaorg/organization/forms/schemaorg.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/organization/organization.xml b/plugins/schemaorg/organization/organization.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/organization/src/Extension/Organization.php b/plugins/schemaorg/organization/src/Extension/Organization.php old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/person/forms/schemaorg.xml b/plugins/schemaorg/person/forms/schemaorg.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/person/person.xml b/plugins/schemaorg/person/person.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/person/src/Extension/Person.php b/plugins/schemaorg/person/src/Extension/Person.php old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/recipe/forms/duration.xml b/plugins/schemaorg/recipe/forms/duration.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/recipe/forms/schemaorg.xml b/plugins/schemaorg/recipe/forms/schemaorg.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/recipe/recipe.xml b/plugins/schemaorg/recipe/recipe.xml old mode 100755 new mode 100644 diff --git a/plugins/schemaorg/recipe/src/Extension/Recipe.php b/plugins/schemaorg/recipe/src/Extension/Recipe.php old mode 100755 new mode 100644 diff --git a/plugins/system/highlight/src/Extension/Highlight.php b/plugins/system/highlight/src/Extension/Highlight.php index 0f8a013d02e7..52fcb230d659 100644 --- a/plugins/system/highlight/src/Extension/Highlight.php +++ b/plugins/system/highlight/src/Extension/Highlight.php @@ -13,6 +13,7 @@ use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\CMS\Uri\Uri; use Joomla\Component\Finder\Administrator\Indexer\Result; // phpcs:disable PSR1.Files.SideEffects @@ -121,7 +122,9 @@ public function onFinderResult($item, $query) && empty($item->mime) && $params->get('highlight_terms', 1) ) { - $item->route .= '&highlight=' . base64_encode(json_encode(\array_slice($query->highlight, 0, 10))); + $uri = new Uri($item->route); + $uri->setVar('highlight', base64_encode(json_encode(\array_slice($query->highlight, 0, 10)))); + $item->route = $uri->toString(); } } } diff --git a/plugins/system/schemaorg/forms/schemaorg.xml b/plugins/system/schemaorg/forms/schemaorg.xml old mode 100755 new mode 100644 diff --git a/plugins/system/schemaorg/schemaorg.xml b/plugins/system/schemaorg/schemaorg.xml old mode 100755 new mode 100644 diff --git a/tests/System/README.md b/tests/System/README.md index a009c0d48853..7d880847001e 100644 --- a/tests/System/README.md +++ b/tests/System/README.md @@ -163,6 +163,7 @@ The Joomla System Tests come with some convenient [Cypress Tasks](https://docs.c - **cleanupDB** – Deletes the inserted items from the database - **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 - **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/drone-system-run.sh b/tests/System/drone-system-run.sh index 7b106355bc87..823b8f08c598 100644 --- a/tests/System/drone-system-run.sh +++ b/tests/System/drone-system-run.sh @@ -18,6 +18,7 @@ chown -R www-data /tests/www/$TEST_GROUP/ chmod -R 777 /tests/www/$TEST_GROUP/images echo "[RUNNER] Start Apache" +a2enmod rewrite apache2ctl -D FOREGROUND & echo "[RUNNER] Run cypress tests" diff --git a/tests/System/integration/administrator/components/com_scheduler/Tasks.cy.js b/tests/System/integration/administrator/components/com_scheduler/Tasks.cy.js new file mode 100644 index 000000000000..4588e6e33947 --- /dev/null +++ b/tests/System/integration/administrator/components/com_scheduler/Tasks.cy.js @@ -0,0 +1,97 @@ +describe('Test in backend that the tasks list', () => { + beforeEach(() => { + cy.doAdministratorLogin(); + cy.visit('/administrator/index.php?option=com_scheduler&view=tasks&filter='); + }); + afterEach(() => cy.task('queryDB', "DELETE FROM #__scheduler_tasks WHERE title = 'Test task'")); + + it('has a title', () => { + cy.get('h1.page-title').should('contain.text', ' Scheduled Tasks'); + }); + + it('can display a list of tasks', () => { + cy.contains('Update Notification'); + }); + + it('can open the task list', () => { + cy.clickToolbarButton('New'); + cy.contains('Select a Task type'); + cy.contains('GET Request'); + }); + + it('can publish the test task', () => { + cy.clickToolbarButton('New'); + cy.get('#comSchedulerSelectSearch').clear().type('GET'); + cy.get('a[href*="plg_task_requests_task_get"]').click(); + cy.get('#jform_title').clear().type('Test task'); + cy.get('#jform_params_url').clear().type('www.test.task'); + cy.get('#jform_execution_rules_interval_minutes').clear().type('1'); + cy.clickToolbarButton('Save & Close'); + cy.get('#system-message-container').contains('Item saved').should('exist'); + }); + + it('can unpublish the test task', () => { + cy.clickToolbarButton('New'); + cy.get('#comSchedulerSelectSearch').clear().type('GET'); + cy.get('a[href*="plg_task_requests_task_get"]').click(); + cy.get('#jform_title').clear().type('Test task'); + cy.get('#jform_params_url').clear().type('www.test.task'); + cy.get('#jform_execution_rules_interval_minutes').clear().type('1'); + cy.clickToolbarButton('Save & Close'); + cy.get('#system-message-container').contains('Item saved').should('exist'); + + cy.reload(); + cy.searchForItem('Test task'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.contains('Disable').click(); + cy.on('window:confirm', () => true); + + cy.get('#system-message-container').contains('Task disabled').should('exist'); + }); + + it('can trash the test task', () => { + cy.clickToolbarButton('New'); + cy.get('#comSchedulerSelectSearch').clear().type('GET'); + cy.get('a[href*="plg_task_requests_task_get"]').click(); + cy.get('#jform_title').clear().type('Test task'); + cy.get('#jform_params_url').clear().type('www.test.task'); + cy.get('#jform_execution_rules_interval_minutes').clear().type('1'); + cy.get('#jform_state').select('Disabled'); + cy.clickToolbarButton('Save & Close'); + + cy.get('#system-message-container').contains('Item saved').should('exist'); + + cy.reload(); + cy.searchForItem('Test task'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.contains('Trash').click(); + cy.on('window:confirm', () => true); + + cy.get('#system-message-container').contains('Task trashed').should('exist'); + }); + + it('can delete the test task', () => { + cy.clickToolbarButton('New'); + cy.get('#comSchedulerSelectSearch').clear().type('GET'); + cy.get('a[href*="plg_task_requests_task_get"]').click(); + cy.get('#jform_title').clear().type('Test task'); + cy.get('#jform_params_url').clear().type('www.test.task'); + cy.get('#jform_execution_rules_interval_minutes').clear().type('1'); + cy.get('#jform_state').select('Trashed'); + cy.clickToolbarButton('Save & Close'); + + cy.get('#system-message-container').contains('Item saved').should('exist'); + + cy.reload(); + + cy.setFilter('state', 'Trashed'); + cy.searchForItem('Test task'); + cy.checkAllResults(); + cy.clickToolbarButton('empty trash'); + cy.clickDialogConfirm(true); + + cy.get('#system-message-container').contains('Task deleted').should('exist'); + }); +}); diff --git a/tests/System/integration/plugins/system/sef/SefPlugin.cy.js b/tests/System/integration/plugins/system/sef/SefPlugin.cy.js new file mode 100644 index 000000000000..35b27448284b --- /dev/null +++ b/tests/System/integration/plugins/system/sef/SefPlugin.cy.js @@ -0,0 +1,115 @@ +describe('Test that the sef system plugin', () => { + afterEach(() => { + cy.task('deleteRelativePath', '.htaccess'); + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef=true sef_suffix=false sef_rewrite=false`); + cy.db_updateExtensionParameter('enforcesuffix', '1', 'plg_system_sef'); + cy.db_updateExtensionParameter('indexphp', '1', 'plg_system_sef'); + cy.db_updateExtensionParameter('trailingslash', '0', 'plg_system_sef'); + cy.db_updateExtensionParameter('strictrouting', '1', 'plg_system_sef'); + }); + + it('can process if option \'sef\' disabled', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef=false`); + cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.request({ url: '/index.php/component/users/login', failOnStatusCode: false, followRedirect: false }).then((response) => { + expect(response.status).to.eq(404); + }); + }); + + it('can process if option \'enforcesuffix\' enabled', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_suffix=true`); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login\.html$/); + }); + cy.request({ url: '/index.php/component/users/login.html', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + }); + + it('can process if option \'enforcesuffix\' disabled', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_suffix=true`); + cy.db_updateExtensionParameter('enforcesuffix', '0', 'plg_system_sef'); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.request({ url: '/index.php/component/users/login.html', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + }); + + it('can process if option \'indexphp\' enabled', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_rewrite=true`); + cy.task('copyRelativeFile', { source: 'htaccess.txt', destination: '.htaccess' }); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/(? { + expect(response.status).to.eq(200); + }); + }); + + it('can process if option \'indexphp\' disabled', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_rewrite=true`); + cy.task('copyRelativeFile', { source: 'htaccess.txt', destination: '.htaccess' }); + cy.db_updateExtensionParameter('indexphp', '0', 'plg_system_sef'); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.request({ url: '/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + }); + + it('can process if option \'trailingslash\' disabled', () => { + cy.request({ url: '/index.php/component/users/login/', followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login$/); + }); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.visit('/'); + cy.get('li.nav-item').contains('Home') + .should('have.attr', 'href') + .and('match', /\/index\.php$/); + }); + + it('can process if option \'trailingslash\' enabled', () => { + cy.db_updateExtensionParameter('trailingslash', '1', 'plg_system_sef'); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login\/$/); + }); + cy.request({ url: '/index.php/component/users/login/', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.visit('/'); + cy.get('li.nav-item').contains('Home') + .should('have.attr', 'href') + .and('match', /\/index\.php\/$/); + }); + + it('can process if option \'strictrouting\' enabled', () => { + cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login$/); + }); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + }); + + it('can process if option \'strictrouting\' disabled', () => { + cy.db_updateExtensionParameter('strictrouting', '0', 'plg_system_sef'); + cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + }); +}); diff --git a/tests/System/integration/site/components/com_contact/Router.cy.js b/tests/System/integration/site/components/com_contact/Router.cy.js new file mode 100644 index 000000000000..822e322c0692 --- /dev/null +++ b/tests/System/integration/site/components/com_contact/Router.cy.js @@ -0,0 +1,131 @@ +describe('Test in frontend that the contact site router', () => { + it('can process contact without a menu item', () => { + cy.db_createContact({ name: 'Test Contact', alias: 'test-contact-router' }).then((contact) => { + cy.request({ url: `/index.php?option=com_contact&view=contact&id=${contact.id}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + + cy.visit('/index.php/component/contact/contact/test-contact-router'); + cy.url().should('match', /\/index\.php\/component\/contact\/contact\/test-contact-router$/); + cy.title().should('equal', 'Test Contact'); + cy.get('main h1').contains('Home'); + cy.get('main h2').contains('Test Contact'); + cy.get('main h3').contains('Contact'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Uncategorised'); + cy.get('@breadcrumb').eq(3).should('contain', 'Test Contact'); + }); + }); + + it('can process contact with a single contact menu item', () => { + cy.db_createContact({ name: 'Test Contact', alias: 'test-contact-router' }).then((contact) => { + cy.db_createMenuItem({ + title: 'Test Menu Single Contact', + alias: 'test-menu-contact-router', + path: 'test-menu-contact-router', + link: `index.php?option=com_contact&view=contact&id=${contact.id}`, + }); + cy.request({ url: `/index.php?option=com_contact&view=contact&id=${contact.id}&catid=${contact.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-contact-router$/); + }); + + cy.visit('/index.php/test-menu-contact-router'); + cy.url().should('match', /\/index\.php\/test-menu-contact-router$/); + cy.title().should('equal', 'Test Menu Single Contact'); + cy.get('main h1').contains('Test Contact'); + cy.get('main h2').contains('Contact'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Single Contact'); + }); + }); + + it('can process contact with a category list menu item', () => { + cy.db_createContact({ name: 'Test Contact', alias: 'test-contact-router' }).then((contact) => { + cy.db_createMenuItem({ + title: 'Test Menu Contact Category', + alias: 'test-menu-category-router', + path: 'test-menu-category-router', + link: `index.php?option=com_contact&view=category&id=${contact.catid}`, + }); + cy.request({ url: `/index.php?option=com_contact&view=contact&id=${contact.id}&catid=${contact.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-category-router\/test-contact-router$/); + }); + + cy.visit('/index.php/test-menu-category-router'); + cy.url().should('match', /\/index\.php\/test-menu-category-router$/); + cy.title().should('equal', 'Test Menu Contact Category'); + cy.get('main h1').contains('Uncategorised'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Contact Category'); + cy.get('main div.com-contact-category a') + .contains('Test Contact') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-category-router\/test-contact-router$/); + + cy.visit('/index.php/test-menu-category-router/test-contact-router'); + cy.url().should('match', /\/index\.php\/test-menu-category-router\/test-contact-router$/); + cy.title().should('equal', 'Test Contact'); + cy.get('main h1').contains('Test Contact'); + cy.get('main h2').contains('Contact'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Contact Category'); + cy.get('@breadcrumb').eq(3).should('contain', 'Test Contact'); + }); + }); + + it('can process contact with a categories list menu item', () => { + cy.db_createContact({ name: 'Test Contact', alias: 'test-contact-router' }).then((contact) => { + cy.db_createMenuItem({ + title: 'Test Menu Contact Categories', + alias: 'test-menu-categories-router', + path: 'test-menu-categories-router', + link: 'index.php?option=com_contact&view=categories&id=0', + }); + cy.request({ url: `/index.php?option=com_contact&view=contact&id=${contact.id}&catid=${contact.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-categories-router\/uncategorised\/test-contact-router$/); + }); + + cy.visit('/index.php/test-menu-categories-router'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router$/); + cy.title().should('equal', 'Test Menu Contact Categories'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Contact Categories'); + cy.get('main div.com-contact-categories h3 a') + .contains('Uncategorised') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-categories-router\/uncategorised$/); + + cy.visit('/index.php/test-menu-categories-router/uncategorised'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router\/uncategorised$/); + cy.title().should('equal', 'Test Menu Contact Categories'); + cy.get('main h1').contains('Uncategorised'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Contact Categories'); + cy.get('@breadcrumb').eq(3).should('contain', 'Uncategorised'); + cy.get('main div.com-contact-category a') + .contains('Test Contact') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-categories-router\/uncategorised\/test-contact-router$/); + + cy.visit('/index.php/test-menu-categories-router/uncategorised/test-contact-router'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router\/uncategorised\/test-contact-router$/); + cy.title().should('equal', 'Test Contact'); + cy.get('main h1').contains('Test Contact'); + cy.get('main h2').contains('Contact'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 5); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Contact Categories'); + cy.get('@breadcrumb').eq(3).should('contain', 'Uncategorised'); + cy.get('@breadcrumb').eq(4).should('contain', 'Test Contact'); + }); + }); +}); diff --git a/tests/System/integration/site/components/com_content/Router.cy.js b/tests/System/integration/site/components/com_content/Router.cy.js new file mode 100644 index 000000000000..0036e3d196fb --- /dev/null +++ b/tests/System/integration/site/components/com_content/Router.cy.js @@ -0,0 +1,130 @@ +describe('Test in frontend that the content site router', () => { + it('can process article without a menu item', () => { + cy.db_createArticle({ title: 'Test Article', alias: 'test-content-router' }).then((article) => { + cy.request({ url: `/index.php?option=com_content&view=article&id=${article.id}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + // @TODO: Not working if 'Featured Articles' is home menu item + // expect(response.status).to.eq(301); + // expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/content\/article\/test-content-router$/); + }); + + cy.visit('/index.php/component/content/article/test-content-router'); + cy.url().should('match', /\/index\.php\/component\/content\/article\/test-content-router$/); + cy.title().should('equal', 'Test Article'); + cy.get('main h1').contains('Home'); + cy.get('main h2').contains('Test Article'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Uncategorised'); + cy.get('@breadcrumb').eq(3).should('contain', 'Test Article'); + }); + }); + + it('can process article with a single article menu item', () => { + cy.db_createArticle({ title: 'Test Article', alias: 'test-content-router' }).then((article) => { + cy.db_createMenuItem({ + title: 'Test Menu Single Article', + alias: 'test-menu-article-router', + path: 'test-menu-article-router', + link: `index.php?option=com_content&view=article&id=${article.id}`, + }); + cy.request({ url: `/index.php?option=com_content&view=article&id=${article.id}&catid=${article.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-article-router$/); + }); + + cy.visit('/index.php/test-menu-article-router'); + cy.url().should('match', /\/index\.php\/test-menu-article-router$/); + cy.title().should('equal', 'Test Article'); + cy.get('main h1').contains('Test Article'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Single Article'); + }); + }); + + it('can process article with a category list menu item', () => { + cy.db_createArticle({ title: 'Test Article', alias: 'test-content-router' }).then((article) => { + cy.db_createMenuItem({ + title: 'Test Menu Article Category', + alias: 'test-menu-category-router', + path: 'test-menu-category-router', + link: `index.php?option=com_content&view=category&id=${article.catid}`, + }); + cy.request({ url: `/index.php?option=com_content&view=article&id=${article.id}&catid=${article.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-category-router\/test-content-router$/); + }); + + cy.visit('/index.php/test-menu-category-router'); + cy.url().should('match', /\/index\.php\/test-menu-category-router$/); + cy.title().should('equal', 'Test Menu Article Category'); + cy.get('main h1').should('not.exist'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Article Category'); + cy.get('main div.com-content-category a') + .contains('Test Article') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-category-router\/test-content-router$/); + + cy.visit('/index.php/test-menu-category-router/test-content-router'); + cy.url().should('match', /\/index\.php\/test-menu-category-router\/test-content-router$/); + cy.title().should('equal', 'Test Article'); + cy.get('main h1').contains('Test Article'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Article Category'); + cy.get('@breadcrumb').eq(3).should('contain', 'Test Article'); + }); + }); + + it('can process article with a categories list menu item', () => { + cy.db_createArticle({ title: 'Test Article', alias: 'test-content-router' }).then((article) => { + cy.db_createMenuItem({ + title: 'Test Menu Article Categories', + alias: 'test-menu-categories-router', + path: 'test-menu-categories-router', + link: 'index.php?option=com_content&view=categories&id=0', + }); + cy.request({ url: `/index.php?option=com_content&view=article&id=${article.id}&catid=${article.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-categories-router\/uncategorised\/test-content-router$/); + }); + + cy.visit('/index.php/test-menu-categories-router'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router$/); + cy.title().should('equal', 'Test Menu Article Categories'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Article Categories'); + cy.get('main div.com-content-categories div a') + .contains('Uncategorised') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-categories-router\/uncategorised$/); + + cy.visit('/index.php/test-menu-categories-router/uncategorised'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router\/uncategorised$/); + cy.title().should('equal', 'Uncategorised'); + cy.get('main h1').should('not.exist'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Article Categories'); + cy.get('@breadcrumb').eq(3).should('contain', 'Uncategorised'); + cy.get('main div.com-content-category-blog h2 a') + .contains('Test Article') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-categories-router\/uncategorised\/test-content-router$/); + + cy.visit('/index.php/test-menu-categories-router/uncategorised/test-content-router'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router\/uncategorised\/test-content-router$/); + cy.title().should('equal', 'Test Article'); + cy.get('main h1').contains('Test Article'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 5); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Article Categories'); + cy.get('@breadcrumb').eq(3).should('contain', 'Uncategorised'); + cy.get('@breadcrumb').eq(4).should('contain', 'Test Article'); + }); + }); +}); diff --git a/tests/System/integration/site/modules/mod_articles/Default.cy.js b/tests/System/integration/site/modules/mod_articles/Default.cy.js new file mode 100644 index 000000000000..0d1d11718b9a --- /dev/null +++ b/tests/System/integration/site/modules/mod_articles/Default.cy.js @@ -0,0 +1,21 @@ +describe('Test in frontend that the articles module', () => { + it('can display the title of the article', () => { + cy.db_createCategory({ extension: 'com_content' }) + .then(async (categoryId) => { + await cy.db_createArticle({ title: 'automated test article', catid: categoryId }); + await cy.db_createModule({ + module: 'mod_articles', + params: JSON.stringify({ + catid: categoryId, + item_title: 1, + show_introtext: 0, + }), + }); + }) + .then(() => { + cy.visit('/'); + + cy.contains('li', 'automated test article'); + }); + }); +}); diff --git a/tests/System/plugins/fs.mjs b/tests/System/plugins/fs.mjs index 618f28b870e5..06dfbdd24980 100644 --- a/tests/System/plugins/fs.mjs +++ b/tests/System/plugins/fs.mjs @@ -1,5 +1,5 @@ import { - chmodSync, existsSync, writeFileSync, mkdirSync, rmSync, + chmodSync, existsSync, writeFileSync, mkdirSync, rmSync, copyFileSync, } from 'node:fs'; import { dirname, join } from 'node:path'; import { umask } from 'node:process'; @@ -56,4 +56,24 @@ function writeRelativeFile(relativePath, content, config, mode = 0o444) { return null; } -export { writeRelativeFile, deleteRelativePath }; +/** + * Copies a file to a specified path relative to the CMS root folder. + * + * If the file already exists, it will be overwritten. + * + * @param {string} source - The relative file path of the existing file + * @param {string} destination - The relative file path of the new file + * @param {object} config - The Cypress configuration object + * + * @returns null + */ +function copyRelativeFile(source, destination, config) { + const fullSource = join(config.env.cmsPath, source); + const fullDestination = join(config.env.cmsPath, destination); + + copyFileSync(fullSource, fullDestination); + + return null; +} + +export { writeRelativeFile, deleteRelativePath, copyRelativeFile }; diff --git a/tests/System/plugins/index.mjs b/tests/System/plugins/index.mjs index ab7c8a2b72a5..0c36b2f6940c 100644 --- a/tests/System/plugins/index.mjs +++ b/tests/System/plugins/index.mjs @@ -1,5 +1,5 @@ import { getMails, clearEmails, startMailServer } from './mail.mjs'; -import { writeRelativeFile, deleteRelativePath } from './fs.mjs'; +import { writeRelativeFile, deleteRelativePath, copyRelativeFile } from './fs.mjs'; import { queryTestDB, deleteInsertedItems } from './db.mjs'; /** @@ -16,6 +16,7 @@ export default function setupPlugins(on, config) { cleanupDB: () => deleteInsertedItems(config), writeRelativeFile: ({ path, content, mode }) => writeRelativeFile(path, content, config, mode), deleteRelativePath: (path) => deleteRelativePath(path, config), + copyRelativeFile: ({ source, destination }) => copyRelativeFile(source, destination, config), getMails: () => getMails(), clearEmails: () => clearEmails(), startMailServer: () => startMailServer(config),