diff --git a/administrator/components/com_finder/src/Controller/IndexController.php b/administrator/components/com_finder/src/Controller/IndexController.php index 234e84323cdce..f63edbf4c0327 100644 --- a/administrator/components/com_finder/src/Controller/IndexController.php +++ b/administrator/components/com_finder/src/Controller/IndexController.php @@ -11,8 +11,11 @@ \defined('_JEXEC') or die; +use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Component\Finder\Administrator\Indexer\Indexer; /** * Index controller class for Finder. @@ -37,6 +40,31 @@ public function getModel($name = 'Index', $prefix = 'Administrator', $config = a return parent::getModel($name, $prefix, $config); } + /** + * Method to optimise the index by removing orphaned entries. + * + * @return boolean True on success. + * + * @since __DEPLOY_VERSION__ + */ + public function optimise() + { + $this->checkToken(); + + // Optimise the index by first running the garbage collection + PluginHelper::importPlugin('finder'); + $this->app->triggerEvent('onFinderGarbageCollection'); + + // Now run the optimisation method from the indexer + $indexer = new Indexer; + $indexer->optimize(); + + $message = Text::_('COM_FINDER_INDEX_OPTIMISE_FINISHED'); + $this->setRedirect('index.php?option=com_finder&view=index', $message); + + return true; + } + /** * Method to purge all indexed links from the database. * diff --git a/administrator/components/com_finder/src/Indexer/Adapter.php b/administrator/components/com_finder/src/Indexer/Adapter.php index 6d7f7a108243c..66f7271585f5a 100644 --- a/administrator/components/com_finder/src/Indexer/Adapter.php +++ b/administrator/components/com_finder/src/Indexer/Adapter.php @@ -262,6 +262,38 @@ public function onBuildIndex() return true; } + /** + * Method to remove outdated index entries + * + * @return integer + * + * @since __DEPLOY_VERSION__ + */ + public function onFinderGarbageCollection() + { + $db = $this->db; + $type_id = $this->getTypeId(); + + $query = $db->getQuery(true); + $subquery = $db->getQuery(true); + $subquery->select('CONCAT(' . $db->quote($this->getUrl('', $this->extension, $this->layout)) . ', id)') + ->from($db->quoteName($this->table)); + $query->select($db->quoteName('l.link_id')) + ->from($db->quoteName('#__finder_links', 'l')) + ->where($db->quoteName('l.type_id') . ' = ' . $type_id) + ->where($db->quoteName('l.url') . ' LIKE ' . $db->quote($this->getUrl('%', $this->extension, $this->layout))) + ->where($db->quoteName('l.url') . ' NOT IN (' . $subquery . ')'); + $db->setQuery($query); + $items = $db->loadColumn(); + + foreach ($items as $item) + { + $this->indexer->remove($item); + } + + return count($items); + } + /** * Method to change the value of a content item's property in the links * table. This is used to synchronize published and access states that diff --git a/administrator/components/com_finder/src/Indexer/Indexer.php b/administrator/components/com_finder/src/Indexer/Indexer.php index a6daa68dff41e..d5fc15427a2bd 100644 --- a/administrator/components/com_finder/src/Indexer/Indexer.php +++ b/administrator/components/com_finder/src/Indexer/Indexer.php @@ -713,7 +713,35 @@ public function optimize() $db->setQuery($query); $db->execute(); - // Remove the orphaned taxonomy nodes. + // Delete all broken links. (Links missing the object) + $query = $db->getQuery(true) + ->delete('#__finder_links') + ->where($db->quoteName('object') . ' = ' . $db->quote('')); + $db->setQuery($query); + $db->execute(); + + // Delete all orphaned mappings of terms to links + $query2 = $db->getQuery(true) + ->select($db->quoteName('link_id')) + ->from($db->quoteName('#__finder_links')); + $query = $db->getQuery(true) + ->delete($db->quoteName('#__finder_links_terms')) + ->where($db->quoteName('link_id') . ' NOT IN (' . $query2 . ')'); + $db->setQuery($query); + $db->execute(); + + // Delete all orphaned terms + $query2 = $db->getQuery(true) + ->select($db->quoteName('term_id')) + ->from($db->quoteName('#__finder_links_terms')); + $query = $db->getQuery(true) + ->delete($db->quoteName('#__finder_terms')) + ->where($db->quoteName('term_id') . ' NOT IN (' . $query2 . ')'); + $db->setQuery($query); + $db->execute(); + + // Delete all orphaned taxonomies + Taxonomy::removeOrphanMaps(); Taxonomy::removeOrphanNodes(); // Optimize the tables. diff --git a/administrator/components/com_finder/src/Indexer/Taxonomy.php b/administrator/components/com_finder/src/Indexer/Taxonomy.php index c19653cb52443..c2436b908fc65 100644 --- a/administrator/components/com_finder/src/Indexer/Taxonomy.php +++ b/administrator/components/com_finder/src/Indexer/Taxonomy.php @@ -385,6 +385,31 @@ public static function removeMaps($linkId) return true; } + /** + * Method to remove orphaned taxonomy maps + * + * @return integer The number of deleted rows. + * + * @since __DEPLOY_VERSION__ + * @throws \RuntimeException on database error. + */ + public static function removeOrphanMaps() + { + // Delete all orphaned maps + $db = Factory::getDbo(); + $query2 = $db->getQuery(true) + ->select($db->quoteName('link_id')) + ->from($db->quoteName('#__finder_links')); + $query = $db->getQuery(true) + ->delete($db->quoteName('#__finder_taxonomy_map')) + ->where($db->quoteName('link_id') . ' NOT IN (' . $query2 . ')'); + $db->setQuery($query); + $db->execute(); + $count = $db->getAffectedRows(); + + return $count; + } + /** * Method to remove orphaned taxonomy nodes and branches. * diff --git a/administrator/components/com_finder/src/View/Index/HtmlView.php b/administrator/components/com_finder/src/View/Index/HtmlView.php index fc0967692bc3a..3bdfa77b23359 100644 --- a/administrator/components/com_finder/src/View/Index/HtmlView.php +++ b/administrator/components/com_finder/src/View/Index/HtmlView.php @@ -202,13 +202,27 @@ protected function addToolbar() if ($canDo->get('core.delete')) { - ToolbarHelper::deleteList('', 'index.delete'); - ToolbarHelper::divider(); + $toolbar->confirmButton('', 'JTOOLBAR_DELETE', 'index.delete') + ->icon('icon-delete') + ->message('COM_FINDER_INDEX_CONFIRM_DELETE_PROMPT') + ->listCheck(true); + $toolbar->divider(); } if ($canDo->get('core.edit.state')) { - ToolbarHelper::trash('index.purge', 'COM_FINDER_INDEX_TOOLBAR_PURGE', false); + $dropdown = $toolbar->dropdownButton('maintenance-group'); + $dropdown->text('COM_FINDER_INDEX_TOOLBAR_MAINTENANCE') + ->toggleSplit(false) + ->icon('icon-wrench') + ->buttonClass('btn btn-action'); + + $childBar = $dropdown->getChildToolbar(); + + $childBar->standardButton('cog', 'COM_FINDER_INDEX_TOOLBAR_OPTIMISE', 'index.optimise', false); + $childBar->confirmButton('index.purge', 'COM_FINDER_INDEX_TOOLBAR_PURGE', 'index.purge') + ->icon('icon-trash') + ->message('COM_FINDER_INDEX_CONFIRM_PURGE_PROMPT'); } } diff --git a/administrator/components/com_finder/tmpl/index/default.php b/administrator/components/com_finder/tmpl/index/default.php index 56dbeb757b737..80b33832daa21 100644 --- a/administrator/components/com_finder/tmpl/index/default.php +++ b/administrator/components/com_finder/tmpl/index/default.php @@ -21,13 +21,9 @@ $listDirn = $this->escape($this->state->get('list.direction')); $lang = Factory::getLanguage(); -Text::script('COM_FINDER_INDEX_CONFIRM_PURGE_PROMPT'); -Text::script('COM_FINDER_INDEX_CONFIRM_DELETE_PROMPT'); - /** @var Joomla\CMS\WebAsset\WebAssetManager $wa */ $wa = $this->document->getWebAssetManager(); -$wa->useScript('com_finder.index') - ->useScript('multiselect') +$wa->useScript('multiselect') ->useScript('table.columns'); ?> diff --git a/administrator/language/en-GB/com_finder.ini b/administrator/language/en-GB/com_finder.ini index aad338d0737b1..3a21b7f951016 100644 --- a/administrator/language/en-GB/com_finder.ini +++ b/administrator/language/en-GB/com_finder.ini @@ -136,6 +136,7 @@ COM_FINDER_INDEX_HEADING_LINK_URL_ASC="Raw URL ascending" COM_FINDER_INDEX_HEADING_LINK_URL_DESC="Raw URL descending" COM_FINDER_INDEX_NO_CONTENT="No content matches your search criteria." COM_FINDER_INDEX_NO_DATA="No content has been indexed." +COM_FINDER_INDEX_OPTIMISE_FINISHED="Optimisation finished." COM_FINDER_INDEX_PLUGIN_CONTENT_NOT_ENABLED="The Smart Search Content Plugin is disabled. Changes to content will not update the Smart Search index until the Plugin is enabled." COM_FINDER_INDEX_PLUGIN_CONTENT_NOT_ENABLED_LINK="The %s is disabled. Changes to content will not update the Smart Search index if you do not enable this plugin." COM_FINDER_INDEX_PURGE_FAILED="Failed to delete selected items." @@ -144,6 +145,8 @@ COM_FINDER_INDEX_SEARCH_DESC="Search in title, URL and last updated date." COM_FINDER_INDEX_SEARCH_LABEL="Search Indexed Content" COM_FINDER_INDEX_TABLE_CAPTION="Table of Indexed Content" COM_FINDER_INDEX_TIP="Start the indexer by pressing the button below, or in the toolbar." +COM_FINDER_INDEX_TOOLBAR_MAINTENANCE="Maintenance" +COM_FINDER_INDEX_TOOLBAR_OPTIMISE="Optimise" COM_FINDER_INDEX_TOOLBAR_PURGE="Clear Index" COM_FINDER_INDEX_TOOLBAR_TITLE="Smart Search: Indexed Content" COM_FINDER_INDEX_TYPE_FILTER="Any Type of Content" diff --git a/build/media_source/com_finder/joomla.asset.json b/build/media_source/com_finder/joomla.asset.json index f7f8a4b533f7b..8d562f35ec7d3 100644 --- a/build/media_source/com_finder/joomla.asset.json +++ b/build/media_source/com_finder/joomla.asset.json @@ -84,29 +84,6 @@ "type": "module" } }, - { - "name": "com_finder.index.es5", - "type": "script", - "uri": "com_finder/index-es5.min.js", - "dependencies": [ - "core" - ], - "attributes": { - "nomodule": true, - "defer": true - } - }, - { - "name": "com_finder.index", - "type": "script", - "uri": "com_finder/index.min.js", - "dependencies": [ - "com_finder.index.es5" - ], - "attributes": { - "type": "module" - } - }, { "name": "com_finder.indexer", "type": "style", diff --git a/build/media_source/com_finder/js/index.es6.js b/build/media_source/com_finder/js/index.es6.js deleted file mode 100644 index b409b0c665724..0000000000000 --- a/build/media_source/com_finder/js/index.es6.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @copyright (C) 2018 Open Source Matters, Inc. - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -Joomla = window.Joomla || {}; - -(() => { - 'use strict'; - - document.addEventListener('DOMContentLoaded', () => { - Joomla.submitbutton = (pressbutton) => { - // @todo replace with joomla-alert - if (pressbutton === 'index.purge' && !window.confirm(Joomla.Text._('COM_FINDER_INDEX_CONFIRM_PURGE_PROMPT'))) { - return false; - } - // @todo replace with joomla-alert - if (pressbutton === 'index.delete' && !window.confirm(Joomla.Text._('COM_FINDER_INDEX_CONFIRM_DELETE_PROMPT'))) { - return false; - } - Joomla.submitform(pressbutton); - return true; - }; - }); -})();