From 811ab3f5f2830638d8e11b4f85385c5fcb895bc9 Mon Sep 17 00:00:00 2001 From: Anurag <30978328+Anu1601CS@users.noreply.github.com> Date: Thu, 6 Sep 2018 16:30:11 +0530 Subject: [PATCH] [4.0][GSoC 2018] Improve Override Management (#21851) * Load correct core files of override files (#2) Start implements loadcorefile() in administrator/components/com_templates/Model/TemplateModel.php * CS (#3) Coding Standards * codingstandards * codingstandards (#4) * Test (#6) Phase 2 (2 part) Mechanism to find correct core file and implementation. * Remove Notice: Only available for html-folder * Remove Warning if core file not found (#11) Thanks. So one part of the issue https://github.com/joomla-projects/gsoc18_override_management/issues/12 is done. * Implement the diff view in template manager Implement the diff view in template manager * coding standard (#17) * fix diff (#18) Fix bug in path in case of administrator template override. Fix bug in path in case of administrator template override. * Notification after update and TEST (#16) Find changed files of overridden files and show message. * coding standard (#21) * correction * correction (#26) * Correcthtmlpath (#27) * correction * change oldhtml to newhtml * List of updated override files. (#30) * addcss (#34) * Final Product (#39) Core and Diff view Updated override history list. Quick icon notification plugin. Override control plugin. * save 3 lines :) * New feature show status. (#47) show status in com_template view templates * link * corrected namespace * Button to Switch (#35) * wip add Switcher * wip style switcher * wip style switch make inline and change on off text * wip start with js * wip js * wip delete buttons and make js more robust * wip save to storage * wip delete old code * wip * wip lint * wip css * set default value for switcher * wip make switcher blue * wip * wip * build * correct names * create new functions * fist test code * use onchange * undo installer.min.js * add forgotten new line at the end of css file * correct align * correct compare.es6 - only deleted the toggle part * correct compare.js - only deleted the toggle part * wip * reduce timeout * wrap in funcitons * wip * add use strict to both js-files(compare and toggle) * add the timeout value of 500 again, because 200 are not enought in my case * use css class 'active' for toggle views * add strict * time out for editor * wip * improvments use newActive and switch * correction * width of switcher-spans * correct align * do not use global * wip * removed timeouts * JTEXT to TEXT * forgotton last line * deleted duplicated comments * css fix align * use unnamed functions in es6 * Sql files for fix database (#50) * sql files for database fix * delete space * Suggestion for displaying Dates in view updates files (#52) Correct Dates and do not use date of file any more * Store Date as UTC and show it in server time zone (#57) * modified and created date are created and stored in UTC * convert dates for displaying in model * spar a loop * normalize timezone in view * use language constants for dateformat * JToolbarHelper to ToolbarHelper * CS * namespace * plural * name * clean * text * fx * sin * files * s * Suggestion for language strings (#60) * language strings * correct typo * delete media folder plg_quickicon * add folder plg_quickicon to build/media_src * delete files in media folder * Move media folder - System (#66) * multi * cs * delete files in media folder for joomla toolbar (#67) * Fix button switchers style. (#70) * button * CS * changed uitab.addTab for updated files * Bring back core.js changes. (#69) * core.js * const * fix * form * core * hound * CS * scopr * grid * alpha * cs * lang * only override file * lang * override lang installer * Cs * sub * Update list of core extensions (#71) * Language changes (#76) * update * Update en-GB.com_templates.ini * override JLIB_HTML_PUBLISH_ITEM this is the hover text on the publish icon in the list of files * Change icon (#74) change the icon to use an outline for more consistency * lang * not core (#75) * not core * Update en-GB.plg_installer_override.ini * namespace * cs * Updated files (#82) * Update default_updated_files.php * Update en-GB.com_templates.ini * Update en-GB.com_templates.ini (#81) * Update en-GB.plg_quickicon_overridecheck.ini (#80) * Update en-GB.plg_quickicon_overridecheck.ini (#79) * remove space (#78) * Update en-GB.plg_quickicon_overridecheck.ini * Update en-GB.plg_quickicon_overridecheck.sys.ini * remove hardcoded id * null get function * state * clean * More changes "core" to "original" (#85) * cs * update * plural --- .../others/mysql/utf8mb4-conversion-02.sql | 4 +- .../sql/updates/mysql/4.0.0-2018-07-19.sql | 18 + .../updates/postgresql/4.0.0-2018-07-19.sql | 18 + .../com_installer/Model/UpdateModel.php | 3 + .../com_joomlaupdate/Model/UpdateModel.php | 23 +- .../Controller/TemplateController.php | 110 ++++- .../com_templates/Model/TemplateModel.php | 464 +++++++++++++++++- .../com_templates/Model/TemplatesModel.php | 43 +- .../com_templates/View/Template/HtmlView.php | 23 +- .../com_templates/View/Templates/HtmlView.php | 15 +- .../components/com_templates/forms/source.xml | 38 +- .../com_templates/tmpl/template/default.php | 81 ++- .../tmpl/template/default_updated_files.php | 89 ++++ .../com_templates/tmpl/templates/default.php | 56 ++- .../language/en-GB/en-GB.com_templates.ini | 29 +- .../en-GB/en-GB.plg_installer_override.ini | 9 + .../en-GB.plg_installer_override.sys.ini | 7 + .../en-GB.plg_quickicon_overridecheck.ini | 14 + .../en-GB.plg_quickicon_overridecheck.sys.ini | 7 + .../css/admin-templates-default.css | 55 +++ .../js/toolbar-button/toolbar-button.js | 4 +- .../js/admin-template-compare.es6.js | 61 +++ .../js/admin-template-toggle-switch.es6.js | 126 +++++ .../js/overridecheck.es6.js | 69 +++ build/media_src/system/js/core.es6.js | 29 +- build/media_src/system/js/multiselect.es6.js | 2 +- installation/sql/mysql/joomla.sql | 23 + installation/sql/postgresql/joomla.sql | 21 + libraries/cms/html/grid.php | 13 +- libraries/cms/html/jgrid.php | 53 +- libraries/src/Extension/ExtensionHelper.php | 2 + .../src/Toolbar/Button/StandardButton.php | 18 +- libraries/src/Toolbar/ToolbarHelper.php | 5 +- plugins/installer/override/override.php | 402 +++++++++++++++ plugins/installer/override/override.xml | 19 + .../quickicon/overridecheck/overridecheck.php | 110 +++++ .../quickicon/overridecheck/overridecheck.xml | 32 ++ 37 files changed, 2004 insertions(+), 91 deletions(-) create mode 100644 administrator/components/com_admin/sql/updates/mysql/4.0.0-2018-07-19.sql create mode 100644 administrator/components/com_admin/sql/updates/postgresql/4.0.0-2018-07-19.sql create mode 100644 administrator/components/com_templates/tmpl/template/default_updated_files.php create mode 100644 administrator/language/en-GB/en-GB.plg_installer_override.ini create mode 100644 administrator/language/en-GB/en-GB.plg_installer_override.sys.ini create mode 100644 administrator/language/en-GB/en-GB.plg_quickicon_overridecheck.ini create mode 100644 administrator/language/en-GB/en-GB.plg_quickicon_overridecheck.sys.ini create mode 100644 build/media_src/com_templates/js/admin-template-compare.es6.js create mode 100644 build/media_src/com_templates/js/admin-template-toggle-switch.es6.js create mode 100644 build/media_src/plg_quickicon_overridecheck/js/overridecheck.es6.js create mode 100644 plugins/installer/override/override.php create mode 100644 plugins/installer/override/override.xml create mode 100644 plugins/quickicon/overridecheck/overridecheck.php create mode 100644 plugins/quickicon/overridecheck/overridecheck.xml diff --git a/administrator/components/com_admin/sql/others/mysql/utf8mb4-conversion-02.sql b/administrator/components/com_admin/sql/others/mysql/utf8mb4-conversion-02.sql index 4f63f989f60dc..3edcbd3e7978a 100644 --- a/administrator/components/com_admin/sql/others/mysql/utf8mb4-conversion-02.sql +++ b/administrator/components/com_admin/sql/others/mysql/utf8mb4-conversion-02.sql @@ -48,7 +48,7 @@ ALTER TABLE `#__users` MODIFY `name` varchar(400) NOT NULL DEFAULT ''; -- -- Step 2.2: Convert all tables to utf8mb4 chracter set with utf8mb4_unicode_ci collation -- except #__finder_xxx tables, those will have utf8mb4_general_ci collation. --- Note: The database driver for mysql will change utf8mb4 to utf8 if utf8mb4 is not supported +-- Note: The database driver for mysql will change utf8mb4 to utf8 if utf8mb4 is not supported -- ALTER TABLE `#__assets` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; @@ -108,6 +108,7 @@ ALTER TABLE `#__redirect_links` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4 ALTER TABLE `#__schemas` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `#__session` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `#__tags` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE `#__template_overrides` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `#__template_styles` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `#__ucm_base` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `#__ucm_content` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; @@ -200,6 +201,7 @@ ALTER TABLE `#__redirect_links` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_un ALTER TABLE `#__schemas` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `#__session` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `#__tags` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE `#__template_overrides` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `#__template_styles` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `#__ucm_base` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE `#__ucm_content` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; diff --git a/administrator/components/com_admin/sql/updates/mysql/4.0.0-2018-07-19.sql b/administrator/components/com_admin/sql/updates/mysql/4.0.0-2018-07-19.sql new file mode 100644 index 0000000000000..93e69b0ef40bb --- /dev/null +++ b/administrator/components/com_admin/sql/updates/mysql/4.0.0-2018-07-19.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS `#__template_overrides` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `template` varchar(50) NOT NULL DEFAULT '', + `hash_id` varchar(255) NOT NULL DEFAULT '', + `extension_id` int(11) DEFAULT 0, + `state` tinyint(1) NOT NULL DEFAULT 0, + `action` varchar(50) NOT NULL DEFAULT '', + `client_id` tinyint(1) unsigned NOT NULL DEFAULT 0, + `created_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (`id`), + KEY `idx_template` (`template`), + KEY `idx_extension_id` (`extension_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; + +INSERT INTO `#__extensions` (`extension_id`, `package_id`, `name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `manifest_cache`, `params`, `checked_out`, `checked_out_time`, `ordering`, `state`, `namespace`) VALUES +(491, 0, 'plg_installer_override', 'plugin', 'override', 'installer', 0, 1, 1, 1, '', '', 0, '0000-00-00 00:00:00', 4, 0, ''), +(492, 0, 'plg_quickicon_overridecheck', 'plugin', 'overridecheck', 'quickicon', 0, 1, 1, 1, '', '', 0, '0000-00-00 00:00:00', 0, 0, ''), diff --git a/administrator/components/com_admin/sql/updates/postgresql/4.0.0-2018-07-19.sql b/administrator/components/com_admin/sql/updates/postgresql/4.0.0-2018-07-19.sql new file mode 100644 index 0000000000000..67174b4a65058 --- /dev/null +++ b/administrator/components/com_admin/sql/updates/postgresql/4.0.0-2018-07-19.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS "#__template_overrides" ( + "id" serial NOT NULL, + "template" varchar(50) DEFAULT '' NOT NULL, + "hash_id" varchar(255) DEFAULT '' NOT NULL, + "extension_id" bigint DEFAULT 0, + "state" smallint DEFAULT 0 NOT NULL, + "action" varchar(50) DEFAULT '' NOT NULL, + "client_id" smallint DEFAULT 0 NOT NULL, + "created_date" timestamp without time zone DEFAULT '1970-01-01 00:00:00' NOT NULL, + "modified_date" timestamp without time zone DEFAULT '1970-01-01 00:00:00' NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "#__template_overrides_idx_template" ON "#__template_overrides" ("template"); +CREATE INDEX "#__template_overrides_idx_extension_id" ON "#__template_overrides" ("extension_id"); + +INSERT INTO "#__extensions" ("extension_id", "package_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "manifest_cache", "params", "checked_out", "checked_out_time", "ordering", "state", "namespace") VALUES +(491, 0, 'plg_installer_override', 'plugin', 'override', 'installer', 0, 1, 1, 1, '', '', 0, '1970-01-01 00:00:00', 4, 0, ''), +(492, 0, 'plg_quickicon_overridecheck', 'plugin', 'overridecheck', 'quickicon', 0, 1, 1, 1, '', '', 0, '1970-01-01 00:00:00', 0, 0, ''); diff --git a/administrator/components/com_installer/Model/UpdateModel.php b/administrator/components/com_installer/Model/UpdateModel.php index 2f3f5c40ab91c..64daf2c894d68 100644 --- a/administrator/components/com_installer/Model/UpdateModel.php +++ b/administrator/components/com_installer/Model/UpdateModel.php @@ -404,6 +404,9 @@ public function update($uids, $minimum_stability = Updater::STABILITY_STABLE) */ private function install($update) { + // Load overrides plugin. + PluginHelper::importPlugin('installer'); + $app = Factory::getApplication(); if (!isset($update->get('downloadurl')->_data)) diff --git a/administrator/components/com_joomlaupdate/Model/UpdateModel.php b/administrator/components/com_joomlaupdate/Model/UpdateModel.php index b5d1ae48e08b6..98393e0e114a9 100644 --- a/administrator/components/com_joomlaupdate/Model/UpdateModel.php +++ b/administrator/components/com_joomlaupdate/Model/UpdateModel.php @@ -27,6 +27,7 @@ use Joomla\CMS\Log\Log; use Joomla\CMS\Http\HttpFactory; use Joomla\CMS\Installer\Installer; +use Joomla\CMS\Plugin\PluginHelper; /** * Joomla! update overview Model @@ -389,7 +390,8 @@ protected function downloadPackage($url, $target) } /** - * Create restoration file. + * Create restoration file and trigger onJoomlaBeforeUpdate event, which find the updated core files + * which have changed during the update, where there are override for. * * @param string $basename Optional base path to the file. * @@ -399,11 +401,17 @@ protected function downloadPackage($url, $target) */ public function createRestorationFile($basename = null) { + // Load overrides plugin. + PluginHelper::importPlugin('installer'); + // Get a password $password = UserHelper::genRandomPassword(32); $app = Factory::getApplication(); $app->setUserState('com_joomlaupdate.password', $password); + // Trigger event before joomla update. + $app->triggerEvent('onJoomlaBeforeUpdate'); + // Do we have to use FTP? $method = Factory::getApplication()->getUserStateFromRequest('com_joomlaupdate.method', 'method', 'direct', 'cmd'); @@ -826,7 +834,8 @@ public function finaliseUpgrade() } /** - * Removes the extracted package file. + * Removes the extracted package file and trigger onJoomlaAfterUpdate event, which find the updated core files + * which have changed during the update, where there are override for. * * @return void * @@ -834,11 +843,19 @@ public function finaliseUpgrade() */ public function cleanUp() { + // Load overrides plugin. + PluginHelper::importPlugin('installer'); + + $app = Factory::getApplication(); + + // Trigger event after joomla update. + $app->triggerEvent('onJoomlaAfterUpdate'); + // Remove the update package. $config = Factory::getConfig(); $tempdir = $config->get('tmp_path'); - $file = Factory::getApplication()->getUserState('com_joomlaupdate.file', null); + $file = $app->getUserState('com_joomlaupdate.file', null); $target = $tempdir . '/' . $file; if (!@unlink($target)) diff --git a/administrator/components/com_templates/Controller/TemplateController.php b/administrator/components/com_templates/Controller/TemplateController.php index dbe92b27489b6..171f6d0310f57 100644 --- a/administrator/components/com_templates/Controller/TemplateController.php +++ b/administrator/components/com_templates/Controller/TemplateController.php @@ -11,14 +11,16 @@ defined('_JEXEC') or die; +use Joomla\CMS\Client\ClientHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Controller\BaseController; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; -use Joomla\Component\Installer\Administrator\Model\InstallModel; -use Joomla\CMS\Language\Text; +use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Router\Route; use Joomla\CMS\Session\Session; -use Joomla\CMS\Client\ClientHelper; -use Joomla\CMS\Factory; +use Joomla\Component\Installer\Administrator\Model\InstallModel; +use Joomla\Utilities\ArrayHelper; /** * Template style controller class. @@ -44,6 +46,9 @@ public function __construct($config = array(), MVCFactoryInterface $factory = nu // Apply, Save & New, and Save As copy should be standard on forms. $this->registerTask('apply', 'save'); + $this->registerTask('unpublish', 'publish'); + $this->registerTask('publish', 'publish'); + $this->registerTask('deleteOverrideHistory', 'publish'); } /** @@ -74,6 +79,64 @@ public function close() $this->setRedirect(Route::_($url, false)); } + /** + * Marked as Checked/Unchecked of override history. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function publish() + { + // Check for request forgeries. + Session::checkToken() or jexit(Text::_('JINVALID_TOKEN')); + + $app = Factory::getApplication(); + $file = $this->input->get('file'); + $id = $this->input->get('id'); + + $ids = $this->input->get('cid', array(), 'array'); + $values = array('publish' => 1, 'unpublish' => 0, 'deleteOverrideHistory' => -3); + $task = $this->getTask(); + $value = ArrayHelper::getValue($values, $task, 0, 'int'); + + if (empty($ids)) + { + $this->setMessage(Text::_('COM_TEMPLATES_ERROR_NO_FILE_SELECTED'), 'warning'); + } + else + { + /* @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */ + $model = $this->getModel(); + + // Change the state of the records. + if (!$model->publish($ids, $value, $id)) + { + $this->setMessage(implode('
', $model->getErrors()), 'warning'); + } + else + { + if ($value === 1) + { + $ntext = 'COM_TEMPLATES_N_OVERRIDE_CHECKED'; + } + elseif ($value === 0) + { + $ntext = 'COM_TEMPLATES_N_OVERRIDE_UNCHECKED'; + } + elseif ($value === -3) + { + $ntext = 'COM_TEMPLATES_N_OVERRIDE_DELETED'; + } + + $this->setMessage(Text::plural($ntext, count($ids))); + } + } + + $url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file; + $this->setRedirect(Route::_($url, false)); + } + /** * Method for copying the template. * @@ -738,4 +801,43 @@ public function extractArchive() $this->setRedirect(Route::_($url, false)); } } + + /** + * Fetch and report updates in \JSON format, for AJAX requests + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function ajax() + { + $app = $this->app; + + if (!Session::checkToken('get')) + { + $app->setHeader('status', 403, true); + $app->sendHeaders(); + echo Text::_('JINVALID_TOKEN'); + $app->close(); + } + + // Checks status of installer override plugin. + if (!PluginHelper::isEnabled('installer', 'override')) + { + $error = array('installerOverride' => 'disabled'); + + echo json_encode($error); + + $app->close(); + } + + /* @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */ + $model = $this->getModel(); + + $result = $model->getUpdatedList(true, true); + + echo json_encode($result); + + $app->close(); + } } diff --git a/administrator/components/com_templates/Model/TemplateModel.php b/administrator/components/com_templates/Model/TemplateModel.php index fe80d7005cfd4..ddabf0cf0b256 100644 --- a/administrator/components/com_templates/Model/TemplateModel.php +++ b/administrator/components/com_templates/Model/TemplateModel.php @@ -13,15 +13,16 @@ use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\Date\Date; +use Joomla\CMS\Factory; +use Joomla\CMS\Filesystem\File; +use Joomla\CMS\Filesystem\Folder; +use Joomla\CMS\Filesystem\Path; +use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Model\FormModel; use Joomla\CMS\Plugin\PluginHelper; -use Joomla\Component\Templates\Administrator\Helper\TemplateHelper; -use Joomla\CMS\Language\Text; -use Joomla\CMS\Filesystem\File; use Joomla\CMS\Uri\Uri; -use Joomla\CMS\Filesystem\Path; -use Joomla\CMS\Filesystem\Folder; -use Joomla\CMS\Factory; +use Joomla\Component\Templates\Administrator\Helper\TemplateHelper; /** * Template model class. @@ -69,6 +70,303 @@ protected function getFile($path, $name) } } + /** + * Method to store file information. + * + * @param string $path The base path. + * @param string $name The file name. + * @param stdClass $template The std class object of template. + * + * @return object StdClass object. + * + * @since __DEPLOY_VERSION__ + */ + protected function storeFileInfo($path, $name, $template) + { + $temp = new \stdClass; + $temp->id = base64_encode($path . $name); + $temp->client = $template->client_id; + $temp->template = $template->element; + $temp->extension_id = $template->extension_id; + + if ($coreFile = $this->getCoreFile($path . $name, $template->client_id)) + { + $temp->coreFile = md5_file($coreFile); + } + else + { + $temp->coreFile = null; + } + + return $temp; + } + + /** + * Method to get all template list. + * + * @return object stdClass object + * + * @since __DEPLOY_VERSION__ + */ + public function getTemplateList() + { + // Get a db connection. + $db = Factory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the required fields from the table + $query->select( + $this->getState( + 'list.select', + 'a.extension_id, a.name, a.element, a.client_id' + ) + ); + + $query->from($db->quoteName('#__extensions', 'a')) + ->where($db->quoteName('a.enabled') . ' = 1') + ->where($db->quoteName('a.type') . ' = ' . $db->quote('template')); + + // Reset the query. + $db->setQuery($query); + + // Load the results as a list of stdClass objects. + $results = $db->loadObjectList(); + + return $results; + } + + /** + * Method to get all updated file list. + * + * @param boolean $state The optional parameter if you want unchecked list. + * @param boolean $all The optional parameter if you want all list. + * @param boolean $cleanup The optional parameter if you want to clean record which is no more required. + * + * @return object stdClass object + * + * @since __DEPLOY_VERSION__ + */ + public function getUpdatedList($state = false, $all = false, $cleanup = false) + { + // Get a db connection. + $db = Factory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the required fields from the table + $query->select( + $this->getState( + 'list.select', + 'a.template, a.hash_id, a.extension_id, a.state, a.action, a.client_id, a.created_date, a.modified_date' + ) + ); + + $template = $this->getTemplate(); + + $query->from($db->quoteName('#__template_overrides', 'a')); + + if (!$all) + { + $query->where('extension_id = ' . $db->quote($template->extension_id)); + } + + if ($state) + { + $query->where('state = 0'); + } + + $query->order($db->quoteName('a.modified_date') . ' DESC'); + + // Reset the query. + $db->setQuery($query); + + // Load the results as a list of stdClass objects. + $pks = $db->loadObjectList(); + + if ($state) + { + return $pks; + } + + $results = array(); + + foreach ($pks as $pk) + { + $client = ApplicationHelper::getClientInfo($pk->client_id); + $path = Path::clean($client->path . '/templates/' . $pk->template . base64_decode($pk->hash_id)); + + if (file_exists($path)) + { + $results[] = $pk; + } + elseif ($cleanup) + { + $cleanupIds = array(); + $cleanupIds[] = $pk->hash_id; + $this->publish($cleanupIds, -3, $pk->extension_id); + } + } + + return $results; + } + + /** + * Method to get a list of all the core files of override files. + * + * @return array A array of all core files. + * + * @since __DEPLOY_VERSION__ + */ + public function getCoreList() + { + // Get list of all templates + $templates = $this->getTemplateList(); + + // Initialize the array variable to store core file list. + $this->coreFileList = array(); + + $app = Factory::getApplication(); + + foreach ($templates as $template) + { + $client = ApplicationHelper::getClientInfo($template->client_id); + $element = Path::clean($client->path . '/templates/' . $template->element . '/'); + $path = Path::clean($element . 'html/'); + + if (is_dir($path)) + { + $this->prepareCoreFiles($path, $element, $template); + } + else + { + $app->enqueueMessage(Text::_('COM_TEMPLATES_ERROR_TEMPLATE_FOLDER_NOT_FOUND'), 'error'); + + return false; + } + } + + // Sort list of stdClass array. + usort( + $this->coreFileList, + function ($a, $b) + { + return strcmp($a->id, $b->id); + } + ); + + return $this->coreFileList; + } + + /** + * Prepare core files. + * + * @param string $dir The path of the directory to scan. + * @param string $element The path of the template element. + * @param stdClass $template The stdClass object of template. + * + * @return array + * + * @since __DEPLOY_VERSION__ + */ + public function prepareCoreFiles($dir, $element, $template) + { + $dirFiles = scandir($dir); + + foreach ($dirFiles as $key => $value) + { + if (in_array($value, array('.', '..', 'node_modules'))) + { + continue; + } + + if (is_dir($dir . $value)) + { + $relativePath = str_replace($element, '', $dir . $value); + $this->prepareCoreFiles($dir . $value . '/', $element, $template); + } + else + { + $ext = pathinfo($dir . $value, PATHINFO_EXTENSION); + $allowedFormat = $this->checkFormat($ext); + + if ($allowedFormat === true) + { + $relativePath = str_replace($element, '', $dir); + $info = $this->storeFileInfo('/' . $relativePath, $value, $template); + + if ($info) + { + $this->coreFileList[] = $info; + } + } + } + } + + return; + } + + /** + * Method to update status of list. + * + * @param array $ids The base path. + * @param array $value The file name. + * @param integer $exid The template extension id. + * + * @return integer Number of files changed. + * + * @since __DEPLOY_VERSION__ + */ + public function publish($ids, $value, $exid) + { + $db = Factory::getDbo(); + + foreach ($ids as $id) + { + if ($value === -3) + { + $deleteQuery = $db->getQuery(true) + ->delete($db->quoteName('#__template_overrides')) + ->where($db->quoteName('hash_id') . ' = ' . $db->quote($id)) + ->where($db->quoteName('extension_id') . ' = ' . $db->quote($exid)); + + try + { + // Set the query using our newly populated query object and execute it. + $db->setQuery($deleteQuery); + $result = $db->execute(); + } + catch (\RuntimeException $e) + { + return $e; + } + } + elseif ($value === 1 || $value === 0) + { + $updateQuery = $db->getQuery(true) + ->update($db->quoteName('#__template_overrides')) + ->set($db->quoteName('state') . ' = ' . $db->quote($value)) + ->where($db->quoteName('hash_id') . ' = ' . $db->quote($id)) + ->where($db->quoteName('extension_id') . ' = ' . $db->quote($exid)); + + try + { + // Set the query using our newly populated query object and execute it. + $db->setQuery($updateQuery); + $result = $db->execute(); + } + catch (\RuntimeException $e) + { + return $e; + } + } + } + + return $result; + } + /** * Method to get a list of all the files to edit in a template. * @@ -107,6 +405,9 @@ public function getFiles() return false; } + + // Clean up override history + $this->getUpdatedList(false, true, true); } return $result; @@ -154,6 +455,148 @@ public function getDirectoryTree($dir) return $result; } + /** + * Method to get the core file of override file + * + * @param string $file Override file + * @param integer $client_id Client Id + * + * @return string $corefile The full path and file name for the target file, or boolean false if the file is not found in any of the paths. + * + * @since __DEPLOY_VERSION__ + */ + public function getCoreFile($file, $client_id) + { + $app = Factory::getApplication(); + $filePath = Path::clean($file); + $explodeArray = explode(DIRECTORY_SEPARATOR, $filePath); + + // Only allow html/ folder + if ($explodeArray['1'] !== 'html') + { + return false; + } + + $fileName = basename($filePath); + $type = $explodeArray['2']; + $client = ApplicationHelper::getClientInfo($client_id); + + $componentPath = Path::clean($client->path . '/components/'); + $modulePath = Path::clean($client->path . '/modules/'); + $layoutPath = Path::clean(JPATH_ROOT . '/layouts/'); + + // For modules + if (stristr($type, 'mod_') !== false) + { + $folder = $explodeArray['2']; + $htmlPath = Path::clean($modulePath . $folder . '/tmpl/'); + $fileName = $this->getSafeName($fileName); + $coreFile = Path::find($htmlPath, $fileName); + + return $coreFile; + } + elseif (stristr($type, 'com_') !== false) + { + // For components + $folder = $explodeArray['2']; + $subFolder = $explodeArray['3']; + $fileName = $this->getSafeName($fileName); + + // The new scheme, if a view has a tmpl folder + $newHtmlPath = Path::clean($componentPath . $folder . '/tmpl/' . $subFolder . '/'); + + if (!$coreFile = Path::find($newHtmlPath, $fileName)) + { + // The old scheme, the views are directly in the component/tmpl folder + $oldHtmlPath = Path::clean($componentPath . $folder . '/views/' . $subFolder . '/tmpl/'); + $coreFile = Path::find($oldHtmlPath, $fileName); + + return $coreFile; + } + + return $coreFile; + } + elseif (stristr($type, 'layouts') !== false) + { + // For Jlayouts + $subtype = $explodeArray['3']; + + if (stristr($subtype, 'com_')) + { + $folder = $explodeArray['3']; + $subFolder = array_slice($explodeArray, 4, -1); + $subFolder = implode(DIRECTORY_SEPARATOR, $subFolder); + $htmlPath = Path::clean($componentPath . $folder . '/layouts/' . $subFolder); + $fileName = $this->getSafeName($fileName); + $coreFile = Path::find($htmlPath, $fileName); + + return $coreFile; + } + elseif (stristr($subtype, 'joomla') || stristr($subtype, 'libraries') || stristr($subtype, 'plugins')) + { + $subFolder = array_slice($explodeArray, 3, -1); + $subFolder = implode(DIRECTORY_SEPARATOR, $subFolder); + $htmlPath = Path::clean($layoutPath . $subFolder); + $fileName = $this->getSafeName($fileName); + $coreFile = Path::find($htmlPath, $fileName); + + return $coreFile; + } + } + + return false; + } + + /** + * Creates a safe file name for the given name. + * + * @param string $name The filename + * + * @return string $fileName The filtered name without Date + * + * @since __DEPLOY_VERSION__ + */ + private function getSafeName($name) + { + if (preg_match('/[0-9]/', $name)) + { + // Get the extension + $extension = File::getExt($name); + + // Remove ( Date ) from file + $explodeArray = explode('-', $name); + $size = count($explodeArray); + $date = $explodeArray[$size - 2] . '-' . str_replace('.' . $extension, '', $explodeArray[$size - 1]); + + if ($this->validateDate($date)) + { + $nameWithoutExtension = implode('-', array_slice($explodeArray, 0, -2)); + + // Filtered name + $name = $nameWithoutExtension . '.' . $extension; + } + } + + return $name; + } + + /** + * Validate Date in file name. + * + * @param string $date Date to validate. + * + * @return boolean Return true if date is valid and false if not. + * + * @since __DEPLOY_VERSION__ + */ + private function validateDate($date) + { + $format = 'Ymd-His'; + $valid = Date::createFromFormat($format, $date); + + return $valid && $valid->format($format) === $date; + } + /** * Method to auto-populate the model state. * @@ -457,8 +900,15 @@ public function &getSource() if (file_exists($filePath)) { $item->extension_id = $this->getState('extension.id'); - $item->filename = $fileName; + $item->filename = Path::clean($fileName); $item->source = file_get_contents($filePath); + $item->filePath = Path::clean($filePath); + + if ($coreFile = $this->getCoreFile($fileName, $this->template->client_id)) + { + $item->coreFile = $coreFile; + $item->core = file_get_contents($coreFile); + } } else { diff --git a/administrator/components/com_templates/Model/TemplatesModel.php b/administrator/components/com_templates/Model/TemplatesModel.php index da83eaea09512..bcb54866fa55a 100644 --- a/administrator/components/com_templates/Model/TemplatesModel.php +++ b/administrator/components/com_templates/Model/TemplatesModel.php @@ -13,8 +13,9 @@ use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Component\ComponentHelper; -use Joomla\CMS\MVC\Model\ListModel; +use Joomla\CMS\Factory; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; +use Joomla\CMS\MVC\Model\ListModel; use Joomla\Component\Templates\Administrator\Helper\TemplatesHelper; /** @@ -68,11 +69,51 @@ public function getItems() { $client = ApplicationHelper::getClientInfo($item->client_id); $item->xmldata = TemplatesHelper::parseXMLTemplateFile($client->path, $item->element); + $num = $this->updated($item->extension_id); + + if ($num) + { + $item->updated = $num; + } } return $items; } + /** + * Check if template extension have any updated override. + * + * @param integer $exid Extension id of template. + * + * @return boolean False if records not found/else integer. + * + * @since __DEPLOY_VERSION__ + */ + public function updated($exid) + { + $db = Factory::getDbo(); + + // Select the required fields from the table + $query = $db->getQuery(true) + ->select('a.template') + ->from($db->quoteName('#__template_overrides', 'a')) + ->where('extension_id = ' . $db->quote($exid)) + ->where('state = 0'); + + // Reset the query. + $db->setQuery($query); + + // Load the results as a list of stdClass objects. + $num = count($db->loadObjectList()); + + if ($num > 0) + { + return $num; + } + + return false; + } + /** * Build an SQL query to load the list data. * diff --git a/administrator/components/com_templates/View/Template/HtmlView.php b/administrator/components/com_templates/View/Template/HtmlView.php index ee7339b2e5ac2..adde38c1eaf31 100644 --- a/administrator/components/com_templates/View/Template/HtmlView.php +++ b/administrator/components/com_templates/View/Template/HtmlView.php @@ -12,13 +12,14 @@ defined('_JEXEC') or die; use Joomla\CMS\Component\ComponentHelper; -use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use Joomla\CMS\Factory; +use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; +use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Toolbar\ToolbarHelper; use Joomla\CMS\Toolbar\Toolbar; use Joomla\CMS\Uri\Uri; -use Joomla\CMS\Factory; -use Joomla\CMS\Filter\InputFilter; /** * View to edit a template style. @@ -123,6 +124,15 @@ class HtmlView extends BaseHtmlView */ protected $archive; + /** + * The state of installer override plugin. + * + * @var array + * + * @since __DEPLOY_VERSION__ + */ + protected $pluginState; + /** * Execute and display a template script. * @@ -141,6 +151,8 @@ public function display($tpl = null) $this->state = $this->get('State'); $this->template = $this->get('Template'); $this->preview = $this->get('Preview'); + $this->pluginState = PluginHelper::isEnabled('installer', 'override'); + $this->updatedList = $this->get('UpdatedList'); $params = ComponentHelper::getParams('com_templates'); $imageTypes = explode(',', $params->get('image_formats')); @@ -277,6 +289,11 @@ protected function addToolbar() } } + if (count($this->updatedList) !== 0 && $this->pluginState) + { + ToolbarHelper::custom('template.deleteOverrideHistory', 'delete', 'move', 'COM_TEMPLATES_BUTTON_DELETE_LIST_ENTRY', true, 'updateForm'); + } + if ($this->type == 'home') { ToolbarHelper::cancel('template.cancel', 'JTOOLBAR_CLOSE'); diff --git a/administrator/components/com_templates/View/Templates/HtmlView.php b/administrator/components/com_templates/View/Templates/HtmlView.php index 6cbe943572c22..1214cfbd867aa 100644 --- a/administrator/components/com_templates/View/Templates/HtmlView.php +++ b/administrator/components/com_templates/View/Templates/HtmlView.php @@ -13,10 +13,11 @@ use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Helper\ContentHelper; -use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; -use Joomla\Component\Templates\Administrator\Helper\TemplatesHelper; use Joomla\CMS\Language\Text; +use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Toolbar\ToolbarHelper; +use Joomla\Component\Templates\Administrator\Helper\TemplatesHelper; /** * View class for a list of template styles. @@ -79,6 +80,15 @@ class HtmlView extends BaseHtmlView */ public $preview; + /** + * The state of installer override plugin. + * + * @var array + * + * @since __DEPLOY_VERSION__ + */ + protected $pluginState; + /** * Execute and display a template script. * @@ -98,6 +108,7 @@ public function display($tpl = null) $this->activeFilters = $this->get('ActiveFilters'); $this->preview = ComponentHelper::getParams('com_templates')->get('template_positions_display'); $this->file = base64_encode('home'); + $this->pluginState = PluginHelper::isEnabled('installer', 'override'); TemplatesHelper::addSubmenu('templates'); diff --git a/administrator/components/com_templates/forms/source.xml b/administrator/components/com_templates/forms/source.xml index 823841544041f..c6493937c37e7 100644 --- a/administrator/components/com_templates/forms/source.xml +++ b/administrator/components/com_templates/forms/source.xml @@ -3,7 +3,7 @@
+ + + + + + + + + + + +
diff --git a/administrator/components/com_templates/tmpl/template/default.php b/administrator/components/com_templates/tmpl/template/default.php index dbe6b92e8556a..90b0a1b8f6251 100644 --- a/administrator/components/com_templates/tmpl/template/default.php +++ b/administrator/components/com_templates/tmpl/template/default.php @@ -9,19 +9,32 @@ defined('_JEXEC') or die; +use Joomla\CMS\Factory; +use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; -use Joomla\CMS\Router\Route; +use Joomla\CMS\Layout\FileLayout; use Joomla\CMS\Layout\LayoutHelper; +use Joomla\CMS\Router\Route; use Joomla\CMS\Session\Session; -use Joomla\CMS\Factory; -use Joomla\CMS\HTML\HTMLHelper; // Include the component HTML helpers. HTMLHelper::addIncludePath(JPATH_COMPONENT . '/helpers/html'); + +Text::script('COM_TEMPLATES_LAYOUTS_DIFFVIEW_SHOW_CORE'); +Text::script('COM_TEMPLATES_LAYOUTS_DIFFVIEW_HIDE_CORE'); +Text::script('COM_TEMPLATES_LAYOUTS_DIFFVIEW_SHOW_DIFF'); +Text::script('COM_TEMPLATES_LAYOUTS_DIFFVIEW_HIDE_DIFF'); + +HTMLHelper::_('script', 'vendor/diff/diff.min.js', array('version' => 'auto', 'relative' => true)); +HTMLHelper::_('script', 'com_templates/admin-template-compare.min.js', array('version' => 'auto', 'relative' => true)); +HTMLHelper::_('script', 'com_templates/admin-template-toggle-switch.min.js', array('version' => 'auto', 'relative' => true)); + HTMLHelper::_('behavior.formvalidator'); HTMLHelper::_('behavior.keepalive'); HTMLHelper::_('behavior.tabstate'); +HTMLHelper::_('behavior.multiselect', 'updateForm'); + $input = Factory::getApplication()->input; @@ -56,7 +69,7 @@ 'editor')); ?>
-
+
type == 'file') : ?>

source->filename, $this->template->element); ?>

@@ -70,6 +83,14 @@
+ type == 'file' && !empty($this->source->coreFile)) : ?> +
+
+ form->getInput('show_core'); ?> + form->getInput('show_diff'); ?> +
+
+
@@ -90,15 +111,41 @@ type == 'file') : ?> -
-
- form->getInput('source'); ?> +
+
+ source->filename); ?> + +

+ + +
+ form->getInput('source'); ?> +
+ + + form->getInput('extension_id'); ?> + form->getInput('filename'); ?> +
- - - form->getInput('extension_id'); ?> - form->getInput('filename'); ?> - + source->coreFile)) : ?> + source->coreFile); ?> + source->filePath); ?> +
+

+
+ form->getInput('core'); ?> +
+
+
+

+
+
+
+
+
+
+ +
type == 'archive') : ?> @@ -268,7 +315,15 @@ loadTemplate('description'); ?> + + +pluginState) : ?> + + loadTemplate('updated_files'); ?> + + + - \ No newline at end of file + diff --git a/administrator/components/com_templates/tmpl/template/default_updated_files.php b/administrator/components/com_templates/tmpl/template/default_updated_files.php new file mode 100644 index 0000000000000..2e0a5137ad2e9 --- /dev/null +++ b/administrator/components/com_templates/tmpl/template/default_updated_files.php @@ -0,0 +1,89 @@ +input; +?> + +
+
+
+ updatedList) !== 0) : ?> + + + + + + + + + + + + + updatedList as $i => $value) : ?> + + + + + + + + + + +
+ + + + + + + + + + + +
+ hash_id, false, 'cid', 'cb', '', 'updateForm'); ?> + + state, $i, 'template.', 1, 'cb', null, null, 'updateForm'); ?> + + hash_id); ?> + + created_date; ?> + 0 ? HTMLHelper::_('date', $created_date, Text::_('DATE_FORMAT_FILTER_DATETIME')) : '-'; ?> + + modified_date === Factory::getDbo()->getNullDate()) : ?> + + + modified_date; ?> + 0 ? HTMLHelper::_('date', $modified_date, Text::_('DATE_FORMAT_FILTER_DATETIME')) : '-'; ?> + + + action; ?> +
+ + + + + + + + + +
+
+
diff --git a/administrator/components/com_templates/tmpl/templates/default.php b/administrator/components/com_templates/tmpl/templates/default.php index cf4684ecd5480..a363b2bfb2e64 100644 --- a/administrator/components/com_templates/tmpl/templates/default.php +++ b/administrator/components/com_templates/tmpl/templates/default.php @@ -9,17 +9,23 @@ defined('_JEXEC') or die; +use Joomla\CMS\Factory; +use Joomla\CMS\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Uri\Uri; + // Include the component HTML helpers. -JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html'); +HTMLHelper::addIncludePath(JPATH_COMPONENT . '/helpers/html'); -JHtml::_('behavior.multiselect'); +HTMLHelper::_('behavior.multiselect'); -$user = JFactory::getUser(); +$user = Factory::getUser(); $listOrder = $this->escape($this->state->get('list.ordering')); $listDirn = $this->escape($this->state->get('list.direction')); ?> -
+
sidebar; ?> @@ -32,41 +38,46 @@ - + - + - + - + - + + pluginState) : ?> + + + + items as $i => $item) : ?> - element, $item->client_id); ?> - element, $item->client_id); ?> + element, $item->client_id); ?> + element, $item->client_id); ?> - - name)); ?> + + name)); ?>
preview && $item->client_id == '0') : ?> - - + + client_id == '1') : ?> - + - +
@@ -89,6 +100,15 @@ + pluginState) : ?> + + updated)) : ?> + updated); ?> + + + + + @@ -101,7 +121,7 @@ - +
diff --git a/administrator/language/en-GB/en-GB.com_templates.ini b/administrator/language/en-GB/en-GB.com_templates.ini index 8be1a8a198ff3..e9e2ac01e13d7 100644 --- a/administrator/language/en-GB/en-GB.com_templates.ini +++ b/administrator/language/en-GB/en-GB.com_templates.ini @@ -14,6 +14,7 @@ COM_TEMPLATES_BUTTON_CREATE="Create" COM_TEMPLATES_BUTTON_CROP="Crop" COM_TEMPLATES_BUTTON_DELETE="Delete" COM_TEMPLATES_BUTTON_DELETE_FILE="Delete File" +COM_TEMPLATES_BUTTON_DELETE_LIST_ENTRY="Delete List Entry" COM_TEMPLATES_BUTTON_EXTRACT_ARCHIVE="Extract Here" COM_TEMPLATES_BUTTON_FILE="New File" COM_TEMPLATES_BUTTON_FOLDERS="Manage Folders" @@ -63,6 +64,7 @@ COM_TEMPLATES_ERROR_IMAGE_FILE_NOT_FOUND="Image file not found." COM_TEMPLATES_ERROR_INDEX_DELETE="The file index.php can't be deleted. Make changes in the editor if you want to change the file." COM_TEMPLATES_ERROR_INVALID_FROM_NAME="Template to copy from can't be found." COM_TEMPLATES_ERROR_INVALID_TEMPLATE_NAME="Invalid template name. Please use only letters, numbers, dashes and underscores." +COM_TEMPLATES_ERROR_NO_FILE_SELECTED="No file selected." COM_TEMPLATES_ERROR_RENAME_INDEX="The file index.php can't be renamed." COM_TEMPLATES_ERROR_ROOT_DELETE="The root folder can't be deleted." COM_TEMPLATES_ERROR_SAVE_DISABLED_TEMPLATE="Unable to save a style associated to a disabled template." @@ -90,7 +92,9 @@ COM_TEMPLATES_FILE_ARCHIVE_EXTRACT_SUCCESS="Successfully extracted the archive f COM_TEMPLATES_FILE_ARCHIVE_EXTRACT_FAIL="Failed to extract the archive file." COM_TEMPLATES_FILE_CREATE_ERROR="An error occurred creating the file." COM_TEMPLATES_FILE_CREATE_SUCCESS="File created." -COM_TEMPLATES_FILE_CONTENT_PREVIEW="File Content Preview" +COM_TEMPLATES_FILE_COMPARE_PANE="Diff between original and overridden file" +COM_TEMPLATES_FILE_CONTENT_PREVIEW="File Content Preview." +COM_TEMPLATES_FILE_CORE_PANE="Original file (readonly)" COM_TEMPLATES_FILE_COPY_FAIL="Failed to copy the file." COM_TEMPLATES_FILE_COPY_SUCCESS="The current file was copied as %s." COM_TEMPLATES_FILE_CROP_ERROR="Failed to crop image." @@ -103,6 +107,7 @@ COM_TEMPLATES_FILE_NEW_NAME_LABEL="Copied File Name" COM_TEMPLATES_FILE_EXISTS="File with the same name already exists." COM_TEMPLATES_FILE_INFO="File Information" COM_TEMPLATES_FILE_NAME="File Name" +COM_TEMPLATES_FILE_OVERRIDE_PANE="Overridden file (editable)" COM_TEMPLATES_FILE_PERMISSIONS="The File Permissions are %s" COM_TEMPLATES_FILE_RENAME_ERROR="An error occurred renaming the file." COM_TEMPLATES_FILE_RENAME_SUCCESS="File renamed." @@ -144,6 +149,10 @@ COM_TEMPLATES_IMAGE_WIDTH="Width" COM_TEMPLATES_INVALID_FILE_NAME="Invalid file name. Please choose a file name with a-z, A-Z, 0-9, - and _." COM_TEMPLATES_INVALID_FILE_TYPE="File type not selected." COM_TEMPLATES_INVALID_FOLDER_NAME="Invalid folder name. Please choose a folder name with a-z, A-Z, 0-9, - and _." +COM_TEMPLATES_LAYOUTS_DIFFVIEW_HIDE_CORE="Hide Original" +COM_TEMPLATES_LAYOUTS_DIFFVIEW_HIDE_DIFF="Hide Diff" +COM_TEMPLATES_LAYOUTS_DIFFVIEW_SHOW_CORE="Show Original" +COM_TEMPLATES_LAYOUTS_DIFFVIEW_SHOW_DIFF="Show Diff" COM_TEMPLATES_MANAGER_ADD_STYLE="Templates: Add Style" COM_TEMPLATES_MANAGER_EDIT_STYLE="Templates: Edit Style" COM_TEMPLATES_MANAGE_FOLDERS="Manage Folders" @@ -162,6 +171,9 @@ COM_TEMPLATES_MSG_MANAGE_NO_STYLES="There are no styles installed matching your COM_TEMPLATES_MSG_MANAGE_NO_TEMPLATES="There are no templates installed matching your query." COM_TEMPLATES_N_ITEMS_DELETED="%d template styles deleted." COM_TEMPLATES_N_ITEMS_DELETED_1="Template style deleted." +COM_TEMPLATES_N_OVERRIDE_CHECKED="%d Updated files list entry marked as checked." +COM_TEMPLATES_N_OVERRIDE_DELETED="%d Updated files list entry deleted." +COM_TEMPLATES_N_OVERRIDE_UNCHECKED="%d Updated files list entry marked as unchecked." COM_TEMPLATES_NEW_FILE_HEADER="Create or Upload a new file." COM_TEMPLATES_NEW_FILE_NAME="New File Name" COM_TEMPLATES_NEW_FILE_SELECT="Select a file type" @@ -169,9 +181,17 @@ COM_TEMPLATES_NEW_FILE_TYPE="File Type" COM_TEMPLATES_NO_TEMPLATE_SELECTED="No template selected." COM_TEMPLATES_OPTION_NONE=":: None ::" COM_TEMPLATES_OPTION_SELECT_MENU_ITEM="- Select Menu Item -" +COM_TEMPLATES_OVERRIDE_ACTION="Update Action" +COM_TEMPLATES_OVERRIDE_CHECKED="Checked" COM_TEMPLATES_OVERRIDE_CREATED="Override created in " +COM_TEMPLATES_OVERRIDE_CREATED_DATE="Added to the list" +COM_TEMPLATES_OVERRIDE_CORE_REMOVED="Original file removed" COM_TEMPLATES_OVERRIDE_FAILED="Failed to create override." +COM_TEMPLATES_OVERRIDE_MODIFIED_DATE="Last change via Update" COM_TEMPLATES_OVERRIDE_SUCCESS="Successfully created the override." +COM_TEMPLATES_OVERRIDE_TEMPLATE_FILE="Template File" +COM_TEMPLATES_OVERRIDE_UPTODATE="Overriden files are up to date. Nothing has been changed in the last extension or Joomla update." +COM_TEMPLATES_OVERRIDES="Override files" COM_TEMPLATES_OVERRIDES_COMPONENTS="Components" COM_TEMPLATES_OVERRIDES_MODULES="Modules" COM_TEMPLATES_OVERRIDES_LAYOUTS="Layouts" @@ -194,8 +214,10 @@ COM_TEMPLATES_SUCCESS_HOME_UNSET="Default style unset." COM_TEMPLATES_TAB_DESCRIPTION="Template Description" COM_TEMPLATES_TAB_EDITOR="Editor" COM_TEMPLATES_TAB_OVERRIDES="Create Overrides" +COM_TEMPLATES_TAB_UPDATED_FILES="Updated Files" COM_TEMPLATES_TEMPLATE_CLOSE="Close" COM_TEMPLATES_TEMPLATE_COPY="Copy Template" +COM_TEMPLATES_TEMPLATE_CORE_FILENAME="Original file "%s"." COM_TEMPLATES_TEMPLATE_DESCRIPTION="Template description." COM_TEMPLATES_TEMPLATE_DETAILS="%s Details and Files" COM_TEMPLATES_TEMPLATE_FILENAME="Editing file "%s" in template "%s"." @@ -210,6 +232,11 @@ COM_TEMPLATES_TEMPLATE_PREVIEW="Preview" COM_TEMPLATES_TEMPLATES_FILTER_SEARCH_DESC="Search in template name or folder name." COM_TEMPLATES_TOGGLE_FULL_SCREEN="Press Ctrl-Q to toggle Full Screen editing." COM_TEMPLATES_TOOLBAR_SET_HOME="Default" +COM_TEMPLATES_N_CONFLICT="%d Changes found" +COM_TEMPLATES_N_CONFLICT_1="Change found" +COM_TEMPLATES_UPTODATE="Up to date" COM_TEMPLATES_WARNING_FORMAT_WILL_NOT_BE_VISIBLE="You have created a new file with the extension '%s'. This is supported but as you did not have that file extension in the list of supported formats this can't be displayed. Please double check the options for Templates and add the format if needed." COM_TEMPLATES_XML_DESCRIPTION="This component manages templates" +JLIB_HTML_PUBLISH_ITEM="Mark as checked" +JLIB_HTML_UNPUBLISH_ITEM="Mark as not checked" JLIB_RULES_SETTING_NOTES="Changes apply to this component only.
Inherited - a Global Configuration setting or higher level setting is applied.
Denied always wins - whatever is set at the Global or higher level and applies to all child elements.
Allowed will enable the action for this component unless overruled by a Global Configuration setting." diff --git a/administrator/language/en-GB/en-GB.plg_installer_override.ini b/administrator/language/en-GB/en-GB.plg_installer_override.ini new file mode 100644 index 0000000000000..772b6d0506b0e --- /dev/null +++ b/administrator/language/en-GB/en-GB.plg_installer_override.ini @@ -0,0 +1,9 @@ +; Joomla! Project +; Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved. +; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php +; Note : All ini files need to be saved as UTF-8 + +PLG_INSTALLER_OVERRIDE="Installer - Override" +PLG_INSTALLER_OVERRIDE_PLUGIN_XML_DESCRIPTION="This plugin enables notifications and handling of overrides after an update in case of changes." +PLG_INSTALLER_N_OVERRIDE_FILE_UPDATED="%s Overridden files have changed." +PLG_INSTALLER_N_OVERRIDE_FILE_UPDATED_1="Overridden file has changed." diff --git a/administrator/language/en-GB/en-GB.plg_installer_override.sys.ini b/administrator/language/en-GB/en-GB.plg_installer_override.sys.ini new file mode 100644 index 0000000000000..dd8e2244f22f6 --- /dev/null +++ b/administrator/language/en-GB/en-GB.plg_installer_override.sys.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved. +; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php +; Note : All ini files need to be saved as UTF-8 + +PLG_INSTALLER_OVERRIDE="Installer - Override" +PLG_INSTALLER_OVERRIDE_PLUGIN_XML_DESCRIPTION="This plugin enables notifications and handling of overrides after an update in case of changes." diff --git a/administrator/language/en-GB/en-GB.plg_quickicon_overridecheck.ini b/administrator/language/en-GB/en-GB.plg_quickicon_overridecheck.ini new file mode 100644 index 0000000000000..83f535b2cb816 --- /dev/null +++ b/administrator/language/en-GB/en-GB.plg_quickicon_overridecheck.ini @@ -0,0 +1,14 @@ +; Joomla! Project +; Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved. +; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php +; Note : All ini files need to be saved as UTF-8 + +PLG_QUICKICON_OVERRIDECHECK="Quick Icon - Joomla! Overrides Update Notification" +PLG_QUICKICON_OVERRIDECHECK_CHECKING="Checking overrides ..." +PLG_QUICKICON_OVERRIDECHECK_ERROR="Error on checking overrides." +PLG_QUICKICON_OVERRIDECHECK_ERROR_ENABLE="Enable Installer - override plugin." +PLG_QUICKICON_OVERRIDECHECK_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_OVERRIDECHECK_GROUP_LABEL="Group" +PLG_QUICKICON_OVERRIDECHECK_OVERRIDEFOUND="%s Override(s) to check." +PLG_QUICKICON_OVERRIDECHECK_UPTODATE="Overrides are up to date." +PLG_QUICKICON_OVERRIDECHECK_XML_DESCRIPTION="Checks overrides on update and enables notifications when you visit the Control Panel page.
Warning! You must have installer override plugin enabled to see results." diff --git a/administrator/language/en-GB/en-GB.plg_quickicon_overridecheck.sys.ini b/administrator/language/en-GB/en-GB.plg_quickicon_overridecheck.sys.ini new file mode 100644 index 0000000000000..b48f637a51985 --- /dev/null +++ b/administrator/language/en-GB/en-GB.plg_quickicon_overridecheck.sys.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved. +; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php +; Note : All ini files need to be saved as UTF-8 + +PLG_QUICKICON_OVERRIDECHECK="Quick Icon - Joomla! Overrides Update Notification" +PLG_QUICKICON_OVERRIDECHECK_XML_DESCRIPTION="Checks overrides on update and enables notifications when you visit the Control Panel page.
Warning! You must have installer override plugin enabled to see results." diff --git a/build/media/com_templates/css/admin-templates-default.css b/build/media/com_templates/css/admin-templates-default.css index 89f889c3df52d..18cba450a533c 100644 --- a/build/media/com_templates/css/admin-templates-default.css +++ b/build/media/com_templates/css/admin-templates-default.css @@ -7,11 +7,13 @@ background: #08c; color: #fff; } + .selected:hover, .selected:focus { background: #08c !important; color: #fff; } + #deleteFolder { margin: 0; } @@ -29,3 +31,56 @@ .treeselect a { display: block; } + +.diffview { + white-space: pre-wrap; + word-wrap: break-word; + display: inline; + padding: 4px; + border-radius: .2em; +} + +.diff-pane { + height: 350px; + border: 0.5px solid grey; + overflow: auto; + padding: 10px; + background-color: #FFF; +} + +#diff-main.active { + display: block; +} + +#diff-main { + display: none; +} + +#core-pane.active { + display: block; +} + +#core-pane { + display: none; +} + +#core-pane .CodeMirror-code { + background-color: #F0F0EE; +} + +#jform_show_core { + display: inline; +} + +#jform_show_diff { + display: inline; + margin-left: 3.5em; +} + +#toggle-buttons { + margin-right: 5em; +} + +.switcher-label-0, .switcher-label-1 { + white-space: nowrap; +} diff --git a/build/media/webcomponents/js/toolbar-button/toolbar-button.js b/build/media/webcomponents/js/toolbar-button/toolbar-button.js index 1fb59c7765825..8ea1820852c25 100755 --- a/build/media/webcomponents/js/toolbar-button/toolbar-button.js +++ b/build/media/webcomponents/js/toolbar-button/toolbar-button.js @@ -32,8 +32,8 @@ connectedCallback() { // Check whether we have a form - const formSelector = this.form || '#adminForm'; - this.formElement = document.querySelector(formSelector); + const formSelector = this.form || 'adminForm'; + this.formElement = document.getElementById(formSelector); if (this.listSelection) { if (!this.formElement) { diff --git a/build/media_src/com_templates/js/admin-template-compare.es6.js b/build/media_src/com_templates/js/admin-template-compare.es6.js new file mode 100644 index 0000000000000..1b7245d6419bd --- /dev/null +++ b/build/media_src/com_templates/js/admin-template-compare.es6.js @@ -0,0 +1,61 @@ +/** + * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ +(() => { + 'use strict'; + + document.addEventListener('DOMContentLoaded', () => { + const decodeHtmlspecialChars = (text) => { + const map = { + '&': '&', + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'", + '’': '’', + '‘': '‘', + '–': '–', + '—': '—', + '…': '…', + '”': '”', + }; + + /* eslint-disable */ + return text.replace(/\&[\w\d\#]{2,5}\;/g, (m) => { const n = map[m]; return n; }); + }; + + const compare = (original, changed) => { + const display = changed.nextElementSibling; + let color = ''; + let pre = null; + const diff = JsDiff.diffLines(original.innerHTML, changed.innerHTML); + const fragment = document.createDocumentFragment(); + + /* eslint-enable */ + + diff.forEach((part) => { + if (part.added) { + color = '#a6f3a6'; + } else if (part.removed) { + color = '#f8cbcb'; + } else { + color = ''; + } + pre = document.createElement('pre'); + pre.style.backgroundColor = color; + pre.className = 'diffview'; + pre.appendChild(document.createTextNode(decodeHtmlspecialChars(part.value))); + fragment.appendChild(pre); + }); + + display.appendChild(fragment); + }; + + const diffs = [].slice.call(document.querySelectorAll('#original')); + for (let i = 0, l = diffs.length; i < l; i += 1) { + compare(diffs[i], diffs[i].nextElementSibling); + } + }); +})(); diff --git a/build/media_src/com_templates/js/admin-template-toggle-switch.es6.js b/build/media_src/com_templates/js/admin-template-toggle-switch.es6.js new file mode 100644 index 0000000000000..0ad9dfcf98f40 --- /dev/null +++ b/build/media_src/com_templates/js/admin-template-toggle-switch.es6.js @@ -0,0 +1,126 @@ +/** + * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ +(() => { + 'use strict'; + + const showDiffChangedOff = () => { + const diffMain = document.getElementById('diff-main'); + + if (diffMain) { + diffMain.classList.remove('active'); + + if (typeof Storage !== 'undefined') { + localStorage.removeItem('diffSwitchState'); + } + } + }; + + const showDiffChangedOn = () => { + const diffMain = document.getElementById('diff-main'); + + if (diffMain) { + diffMain.classList.add('active'); + + if (typeof Storage !== 'undefined') { + localStorage.setItem('diffSwitchState', 'checked'); + } + } + }; + + const showCoreChangedOff = () => { + const override = document.getElementById('override-pane'); + const corePane = document.getElementById('core-pane'); + + if (corePane && override) { + corePane.classList.remove('active'); + override.className = 'col-md-12'; + + if (typeof Storage !== 'undefined') { + localStorage.removeItem('coreSwitchState'); + } + } + }; + + const showCoreChangedOn = () => { + const override = document.getElementById('override-pane'); + const corePane = document.getElementById('core-pane'); + + if (corePane && override) { + corePane.classList.add('active'); + override.className = 'col-md-6'; + + if (Joomla.editors.instances.jform_core) { + Joomla.editors.instances.jform_core.refresh(); + } + + if (typeof Storage !== 'undefined') { + localStorage.setItem('coreSwitchState', 'checked'); + } + } + }; + + document.addEventListener('DOMContentLoaded', () => { + const JformShowDiff = document.getElementById('jform_show_diff'); + const JformShowCore = document.getElementById('jform_show_core'); + + if (JformShowDiff) { + JformShowDiff.addEventListener('joomla.switcher.on', showDiffChangedOn); + JformShowDiff.addEventListener('joomla.switcher.off', showDiffChangedOff); + } + + if (JformShowCore) { + JformShowCore.addEventListener('joomla.switcher.on', showCoreChangedOn); + JformShowCore.addEventListener('joomla.switcher.off', showCoreChangedOff); + } + + // Callback executed when JformShowCore was found + function handleJformShowCore() { + JformShowCore.newActive = 1; + JformShowCore.switch(); + } + + if (typeof Storage !== 'undefined' && localStorage.getItem('coreSwitchState') && JformShowCore) { + // Set up the mutation observer + const observerJformShowCore = new MutationObserver(((mutations, me) => { + if (JformShowDiff) { + handleJformShowCore(); + me.disconnect(); + } + })); + + // Start observing + observerJformShowCore.observe(JformShowCore, { + childList: true, + subtree: true, + }); + + showCoreChangedOn(); + } + + // Callback executed when JformShowDiff was found + function handleJformShowDiff() { + JformShowDiff.newActive = 1; + JformShowDiff.switch(); + } + + if (typeof Storage !== 'undefined' && localStorage.getItem('diffSwitchState') && JformShowDiff) { + // Set up the mutation observer + const observerJformShowDiff = new MutationObserver(((mutations, me) => { + if (JformShowDiff) { + handleJformShowDiff(); + me.disconnect(); + } + })); + + // Start observing + observerJformShowDiff.observe(JformShowDiff, { + childList: true, + subtree: true, + }); + + showDiffChangedOn(); + } + }); +})(); diff --git a/build/media_src/plg_quickicon_overridecheck/js/overridecheck.es6.js b/build/media_src/plg_quickicon_overridecheck/js/overridecheck.es6.js new file mode 100644 index 0000000000000..721f3db425897 --- /dev/null +++ b/build/media_src/plg_quickicon_overridecheck/js/overridecheck.es6.js @@ -0,0 +1,69 @@ +/** + * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +// Ajax call to get the override status. +(() => { + 'use strict'; + + // Add a listener on content loaded to initiate the check. + + document.addEventListener('DOMContentLoaded', () => { + if (Joomla.getOptions('js-override-check')) { + const options = Joomla.getOptions('js-override-check'); + Joomla.request({ + url: options.ajaxUrl, + method: 'GET', + data: '', + perform: true, + onSuccess: (response) => { + const link = document.getElementById('plg_quickicon_overridecheck'); + const linkSpan = link.querySelectorAll('span.j-links-link'); + const updateInfoList = JSON.parse(response); + + if (updateInfoList.installerOverride !== 'disabled') { + if (updateInfoList instanceof Array) { + if (updateInfoList.length === 0) { + // No overrides found + link.classList.add('success'); + for (let i = 0, len = linkSpan.length; i < len; i += 1) { + linkSpan[i].innerHTML = Joomla.JText._('PLG_QUICKICON_OVERRIDECHECK_UPTODATE'); + } + } else { + // Scroll to page top + window.scrollTo(0, 0); + + link.classList.add('danger'); + for (let i = 0, len = linkSpan.length; i < len; i += 1) { + linkSpan[i].innerHTML = Joomla.JText._('PLG_QUICKICON_OVERRIDECHECK_OVERRIDEFOUND').replace('%s', `${updateInfoList.length}`); + } + } + } else { + // An error occurred + link.classList.add('danger'); + for (let i = 0, len = linkSpan.length; i < len; i += 1) { + linkSpan[i].innerHTML = Joomla.JText._('PLG_QUICKICON_OVERRIDECHECK_ERROR'); + } + } + } else { + link.classList.add('danger'); + link.setAttribute('href', `index.php?option=com_plugins&task=plugin.edit&extension_id=${options.pluginId}`); + for (let i = 0, len = linkSpan.length; i < len; i += 1) { + linkSpan[i].innerHTML = Joomla.JText._('PLG_QUICKICON_OVERRIDECHECK_ERROR_ENABLE'); + } + } + }, + onError: () => { + // An error occurred + const link = document.getElementById('plg_quickicon_overridecheck'); + const linkSpan = link.querySelectorAll('span.j-links-link'); + link.classList.add('danger'); + for (let i = 0, len = linkSpan.length; i < len; i += 1) { + linkSpan[i].innerHTML = Joomla.JText._('PLG_QUICKICON_OVERRIDECHECK_ERROR'); + } + }, + }); + } + }); +})(); diff --git a/build/media_src/system/js/core.es6.js b/build/media_src/system/js/core.es6.js index 1483e9e7816b7..541803398cd00 100644 --- a/build/media_src/system/js/core.es6.js +++ b/build/media_src/system/js/core.es6.js @@ -161,9 +161,13 @@ Joomla.Modal = { * @returns {void} */ Joomla.submitbutton = (task, formSelector, validate) => { - const form = document.querySelector(formSelector || 'form.form-validate'); + let form = document.querySelector(formSelector || 'form.form-validate'); let newValidate = validate; + if (typeof formSelector === 'string' && form === null) { + form = document.querySelector(`#${formSelector}`); + } + if (form) { if (newValidate === undefined || newValidate === null) { const pressbutton = task.split('.'); @@ -596,6 +600,8 @@ Joomla.Modal = { let newForm = form; if (typeof newForm === 'undefined') { newForm = document.getElementById('adminForm'); + } else if (typeof form === 'string') { + newForm = document.getElementById(form); } newForm.boxchecked.value = isitchecked @@ -643,6 +649,8 @@ Joomla.Modal = { let newForm = form; if (typeof newForm === 'undefined') { newForm = document.getElementById('adminForm'); + } else if (typeof form === 'string') { + newForm = document.getElementById(form); } newForm.filter_order.value = order; @@ -655,12 +663,19 @@ Joomla.Modal = { * * @param {string} id The id * @param {string} task The task + * @param {string} form The optional form * * @return {boolean} */ - Joomla.listItemTask = (id, task) => { - const form = document.adminForm; - const cb = form[id]; + Joomla.listItemTask = (id, task, form = null) => { + let newForm = form; + if (form !== null) { + newForm = document.getElementById(form); + } else { + newForm = document.adminForm; + } + + const cb = newForm[id]; let i = 0; let cbx; @@ -671,7 +686,7 @@ Joomla.Modal = { // eslint-disable-next-line no-constant-condition while (true) { - cbx = form[`cb${i}`]; + cbx = newForm[`cb${i}`]; if (!cbx) { break; @@ -683,8 +698,8 @@ Joomla.Modal = { } cb.checked = true; - form.boxchecked.value = 1; - Joomla.submitform(task); + newForm.boxchecked.value = 1; + Joomla.submitform(task, newForm); return false; }; diff --git a/build/media_src/system/js/multiselect.es6.js b/build/media_src/system/js/multiselect.es6.js index 2277484e4c5e2..aa2b2b05a9d43 100644 --- a/build/media_src/system/js/multiselect.es6.js +++ b/build/media_src/system/js/multiselect.es6.js @@ -82,7 +82,7 @@ this.boxes[currentCheckBox].checked = !this.boxes[currentCheckBox].checked; isChecked = this.boxes[currentCheckBox].checked; - Joomla.isChecked(this.boxes[currentCheckBox].checked); + Joomla.isChecked(this.boxes[currentCheckBox].checked, this.tableEl.id); } this.changeBg(this.rows[currentCheckBox - 1], isChecked); diff --git a/installation/sql/mysql/joomla.sql b/installation/sql/mysql/joomla.sql index f88a9304edcf4..270d59e5dd346 100644 --- a/installation/sql/mysql/joomla.sql +++ b/installation/sql/mysql/joomla.sql @@ -674,6 +674,8 @@ INSERT INTO `#__extensions` (`extension_id`, `package_id`, `name`, `type`, `elem (488, 0, 'plg_system_httpheaders', 'plugin', 'httpheaders', 'system', 0, 1, 1, 0, '', '{}', 0, '0000-00-00 00:00:00', 0, 0, ''), (489, 0, 'plg_sampledata_multilang', 'plugin', 'multilang', 'sampledata', 0, 1, 1, 0, '', '', 0, '0000-00-00 00:00:00', 0, 0, ''), (490, 0, 'plg_extension_namespacemap', 'plugin', 'namespacemap', 'extension', 0, 1, 1, 1, '', '{}', 0, '0000-00-00 00:00:00', 0, 0, ''), +(491, 0, 'plg_installer_override', 'plugin', 'override', 'installer', 0, 1, 1, 1, '', '', 0, '0000-00-00 00:00:00', 4, 0, ''), +(492, 0, 'plg_quickicon_overridecheck', 'plugin', 'overridecheck', 'quickicon', 0, 1, 1, 1, '', '', 0, '0000-00-00 00:00:00', 0, 0, ''), (509, 0, 'atum', 'template', 'atum', '', 1, 1, 1, 0, '', '', 0, '0000-00-00 00:00:00', 0, 0, ''), (510, 0, 'cassiopeia', 'template', 'cassiopeia', '', 0, 1, 1, 0, '', '{"logoFile":"","fluidContainer":"0","sidebarLeftWidth":"3","sidebarRightWidth":"3"}', 0, '0000-00-00 00:00:00', 0, 0, ''), (600, 802, 'English (en-GB)', 'language', 'en-GB', '', 0, 1, 1, 1, '', '', 0, '0000-00-00 00:00:00', 0, 0, ''), @@ -1569,6 +1571,27 @@ INSERT INTO `#__tags` (`id`, `parent_id`, `lft`, `rgt`, `level`, `path`, `title` -- -------------------------------------------------------- +-- +-- Table structure for table `#__template_overrides` +-- + +CREATE TABLE IF NOT EXISTS `#__template_overrides` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `template` varchar(50) NOT NULL DEFAULT '', + `hash_id` varchar(255) NOT NULL DEFAULT '', + `extension_id` int(11) DEFAULT 0, + `state` tinyint(1) NOT NULL DEFAULT 0, + `action` varchar(50) NOT NULL DEFAULT '', + `client_id` tinyint(1) unsigned NOT NULL DEFAULT 0, + `created_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (`id`), + KEY `idx_template` (`template`), + KEY `idx_extension_id` (`extension_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + -- -- Table structure for table `#__template_styles` -- diff --git a/installation/sql/postgresql/joomla.sql b/installation/sql/postgresql/joomla.sql index 846639efd0aa8..9e4686dd526f8 100644 --- a/installation/sql/postgresql/joomla.sql +++ b/installation/sql/postgresql/joomla.sql @@ -683,6 +683,8 @@ INSERT INTO "#__extensions" ("extension_id", "package_id", "name", "type", "elem (488, 0, 'plg_system_httpheaders', 'plugin', 'httpheaders', 'system', 0, 1, 1, 0, '', '{}', 0, '1970-01-01 00:00:00', 0, 0, ''), (489, 0, 'plg_sampledata_multilang', 'plugin', 'multilang', 'sampledata', 0, 1, 1, 0, '', '', 0, '1970-01-01 00:00:00', 0, 0, ''), (490, 0, 'plg_extension_namespacemap', 'plugin', 'namespacemap', 'extension', 0, 1, 1, 1, '', '{}', 0, '1970-01-01 00:00:00', 0, 0, ''), +(491, 0, 'plg_installer_override', 'plugin', 'override', 'installer', 0, 1, 1, 1, '', '', 0, '1970-01-01 00:00:00', 4, 0, ''), +(492, 0, 'plg_quickicon_overridecheck', 'plugin', 'overridecheck', 'quickicon', 0, 1, 1, 1, '', '', 0, '1970-01-01 00:00:00', 0, 0, ''), (600, 802, 'English (en-GB)', 'language', 'en-GB', '', 0, 1, 1, 1, '', '', 0, '1970-01-01 00:00:00', 0, 0, ''), (601, 802, 'English (en-GB)', 'language', 'en-GB', '', 1, 1, 1, 1, '', '', 0, '1970-01-01 00:00:00', 0, 0, ''), (700, 0, 'files_joomla', 'file', 'joomla', '', 0, 1, 1, 1, '', '', 0, '1970-01-01 00:00:00', 0, 0, ''), @@ -1571,6 +1573,25 @@ INSERT INTO "#__tags" ("id", "parent_id", "lft", "rgt", "level", "path", "title" SELECT setval('#__tags_id_seq', 2, false); +-- +-- Table structure for table `#__template_overrides` +-- + +CREATE TABLE IF NOT EXISTS "#__template_overrides" ( + "id" serial NOT NULL, + "template" varchar(50) DEFAULT '' NOT NULL, + "hash_id" varchar(255) DEFAULT '' NOT NULL, + "extension_id" bigint DEFAULT 0, + "state" smallint DEFAULT 0 NOT NULL, + "action" varchar(50) DEFAULT '' NOT NULL, + "client_id" smallint DEFAULT 0 NOT NULL, + "created_date" timestamp without time zone DEFAULT '1970-01-01 00:00:00' NOT NULL, + "modified_date" timestamp without time zone DEFAULT '1970-01-01 00:00:00' NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "#__template_overrides_idx_template" ON "#__template_overrides" ("template"); +CREATE INDEX "#__template_overrides_idx_extension_id" ON "#__template_overrides" ("extension_id"); + -- -- Table structure for table `#__template_styles` -- diff --git a/libraries/cms/html/grid.php b/libraries/cms/html/grid.php index 770b0845d3ebf..2ad8c8ae5dfec 100644 --- a/libraries/cms/html/grid.php +++ b/libraries/cms/html/grid.php @@ -111,15 +111,24 @@ public static function checkall($name = 'checkall-toggle', $tip = 'JGLOBAL_CHECK * @param string $name The name of the form element * @param string $stub The name of stub identifier * @param string $title The name of the item + * @param string $formId An optional form selector. * * @return mixed String of html with a checkbox if item is not checked out, null if checked out. * * @since 1.5 */ - public static function id($rowNum, $recId, $checkedOut = false, $name = 'cid', $stub = 'cb', $title = '') + public static function id($rowNum, $recId, $checkedOut = false, $name = 'cid', $stub = 'cb', $title = '', $formId = null) { + if ($formId !== null) + { + return $checkedOut ? '' : '' + . ''; + } + return $checkedOut ? '' : '' + . ' ' . htmlspecialchars($title, ENT_COMPAT, 'UTF-8') . '' . ''; } diff --git a/libraries/cms/html/jgrid.php b/libraries/cms/html/jgrid.php index 8a3474641e304..13cc6ada89b41 100644 --- a/libraries/cms/html/jgrid.php +++ b/libraries/cms/html/jgrid.php @@ -35,14 +35,15 @@ abstract class JHtmlJGrid * @param string $inactive_class An optional inactive HTML class * @param boolean $enabled An optional setting for access control on the action. * @param boolean $translate An optional setting for translation. - * @param string $checkbox An optional prefix for checkboxes. + * @param string $checkbox An optional prefix for checkboxes. + * @param string $formId An optional form selector. * * @return string The HTML markup * * @since 1.6 */ public static function action($i, $task, $prefix = '', $text = '', $active_title = '', $inactive_title = '', $tip = false, $active_class = '', - $inactive_class = '', $enabled = true, $translate = true, $checkbox = 'cb') + $inactive_class = '', $enabled = true, $translate = true, $checkbox = 'cb', $formId = null) { if (is_array($prefix)) { @@ -68,7 +69,17 @@ public static function action($i, $task, $prefix = '', $text = '', $active_title if ($enabled) { $html[] = '