diff --git a/UPGRADE.md b/UPGRADE.md index b5e2adb..a2af307 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,7 @@ # Upgrade Notes +## 4.0.5 +- index queue backend ui [#97](https://github.com/dachcom-digital/pimcore-dynamic-search/pull/97) ## 4.0.4 - provide ds settings in backend ui [#96](https://github.com/dachcom-digital/pimcore-dynamic-search/pull/96) ## 4.0.3 diff --git a/config/pimcore/routing.yaml b/config/pimcore/routing.yaml index 0ca76d0..7aba956 100644 --- a/config/pimcore/routing.yaml +++ b/config/pimcore/routing.yaml @@ -16,6 +16,26 @@ dynamic_search.controller.admin.get_context_full_configuration: options: expose: true +dynamic_search.controller.admin.index_queue.get_info: + path: /admin/dynamic-search/settings/index-queue/info + defaults: { _controller: DynamicSearchBundle\Controller\Admin\SettingsController::indexQueueInfoAction } + options: + expose: true + +dynamic_search.controller.admin.index_queue.queue_all_data: + path: /admin/dynamic-search/settings/index-queue/queue-all-data + methods: [ POST ] + defaults: { _controller: DynamicSearchBundle\Controller\Admin\SettingsController::indexQueueAllDataAction } + options: + expose: true + +dynamic_search.controller.admin.index_queue.clear: + path: /admin/dynamic-search/settings/index-queue/clear + methods: [ POST ] + defaults: { _controller: DynamicSearchBundle\Controller\Admin\SettingsController::clearIndexQueueAction } + options: + expose: true + dynamic_search.controller.json_search: path: /dynamic-search/{contextName}/j-{outputChannelName} defaults: { _controller: DynamicSearchBundle\Controller\SearchController::jsonSearchAction } diff --git a/public/js/backend/settings.js b/public/js/backend/settings.js index 1c781f0..fdf8bb9 100644 --- a/public/js/backend/settings.js +++ b/public/js/backend/settings.js @@ -3,6 +3,10 @@ pimcore.plugin.dynamicSearch.settings = Class.create({ panel: null, + healthStateStore: null, + providerStore: null, + queueInfoStore: null, + initialize: function () { this.buildLayout(); }, @@ -17,7 +21,7 @@ pimcore.plugin.dynamicSearch.settings = Class.create({ this.panel = Ext.create('Ext.panel.Panel', { id: 'dynamic_search_settings', - title: t('dynamic_search_settings'), + title: t('dynamic_search.settings'), iconCls: 'dynamic_search_bundle', border: false, bodyPadding: 10, @@ -26,9 +30,15 @@ pimcore.plugin.dynamicSearch.settings = Class.create({ align: 'stretch' }, closable: true, + tbar: [{ + xtype: 'button', + iconCls: 'pimcore_icon_reload', + handler: this.reload.bind(this) + }], items: [ this.buildStatusPanel(), - this.buildProviderGrid() + this.buildProviderGrid(), + this.buildQueueInfoPanel() ] }); @@ -48,10 +58,136 @@ pimcore.plugin.dynamicSearch.settings = Class.create({ pimcoreSystemPanel.setActiveItem('dynamic_search_settings'); }, + reload: function() { + this.queueInfoStore.reload(); + this.healthStateStore.reload(); + this.providerStore.reload(); + }, + + buildQueueInfoPanel: function() { + this.queueInfoStore = new Ext.data.JsonStore({ + autoDestroy: true, + autoLoad: true, + proxy: { + type: 'ajax', + url: Routing.generate('dynamic_search.controller.admin.index_queue.get_info'), + reader: { + type: 'json', + transform: { + fn: function(data) { + return [data]; + } + } + } + }, + fields: ['tableName', 'count'] + }); + + const contexts = Object.keys(pimcore.globalmanager.get('dynamic_search.context.full_configuration') || {}); + + const performIndexQueueAction = function(action, context) { + Ext.Msg.confirm( + t(`dynamic_search.actions.index_queue.${action}`) + (context ? ': ' + context : ''), + t(`dynamic_search.actions.index_queue.${action}.confirmation.message`), + function (confirmMsg) { + + if (confirmMsg !== 'yes') { + return; + } + + Ext.Ajax.request({ + url: Routing.generate('dynamic_search.controller.admin.index_queue.' + action), + method: 'POST', + params: { + context: context + }, + success: function(response) { + if (response.status === 200) { + this.queueInfoStore.reload(); + pimcore.helpers.showNotification(t('success'), t(`dynamic_search.actions.index_queue.${action}.success`), 'success'); + } else { + pimcore.helpers.showNotification(t('error'), response.responseText, 'error'); + } + }.bind(this) + }); + }.bind(this) + ); + }.bind(this); + + return new Ext.grid.Panel({ + title: t('dynamic_search.settings.index_queue'), + layout: 'table', + hideHeaders: false, + style: 'margin-bottom: 10px', + store: this.queueInfoStore, + columns: [ + { + text: t('dynamic_search.settings.index_queue.table_name'), + sortable: false, + dataIndex: 'tableName', + hidden: false, + flex: 2, + }, + { + text: t('dynamic_search.settings.index_queue.total_queued_items'), + sortable: false, + dataIndex: 'count', + hidden: false, + flex: 1, + renderer: function (value, metaData) { + return '' + value + ''; + } + } + ], + bbar: { + items: [ + { + xtype: 'button', + scale: 'small', + margin: '0 10 0 0', + text: t('dynamic_search.actions.index_queue.queue_all_data'), + icon: '/bundles/pimcoreadmin/img/flat-color-icons/data_recovery.svg', + menu: contexts.map(function(context) { + return { + text: context, + handler: function() { + performIndexQueueAction('queue_all_data', context) + } + } + }) + }, + { + xtype: 'button', + scale: 'small', + margin: '0 10 0 0', + text: t('dynamic_search.actions.index_queue.clear'), + icon: '/bundles/pimcoreadmin/img/flat-color-icons/delete_database.svg', + handler: function() { + performIndexQueueAction('clear', null) + } + } + ] + } + }); + }, + buildStatusPanel: function () { + this.healthStateStore = new Ext.data.JsonStore({ + autoDestroy: true, + autoLoad: true, + proxy: { + type: 'ajax', + url: Routing.generate('dynamic_search.controller.admin.get_state'), + reader: { + type: 'json', + rootProperty: 'lines' + } + }, + fields: ['module', 'title', 'comment', 'icon'] + }); return new Ext.panel.Table({ - title: 'Health Status', + title: t('dynamic_search.settings.health_status'), layout: 'table', viewType: 'tableview', style: 'margin-bottom: 10px', @@ -63,19 +199,7 @@ pimcore.plugin.dynamicSearch.settings = Class.create({ viewConfig: { trackOver: false }, - store: new Ext.data.JsonStore({ - autoDestroy: true, - autoLoad: true, - proxy: { - type: 'ajax', - url: Routing.generate('dynamic_search.controller.admin.get_state'), - reader: { - type: 'json', - rootProperty: 'lines' - } - }, - fields: ['module', 'title', 'comment', 'icon'] - }), + store: this.healthStateStore, columns: [ { sortable: false, @@ -118,9 +242,22 @@ pimcore.plugin.dynamicSearch.settings = Class.create({ }, buildProviderGrid: function () { + this.providerStore = new Ext.data.JsonStore({ + autoDestroy: true, + autoLoad: true, + proxy: { + type: 'ajax', + url: Routing.generate('dynamic_search.controller.admin.get_provider'), + reader: { + type: 'json', + rootProperty: 'provider' + } + }, + fields: ['id', 'path', 'active'] + }); return new Ext.grid.GridPanel({ - title: 'Provider', + title: t('dynamic_search.settings.provider'), layout: 'table', style: 'margin-bottom: 10px', columnLines: true, @@ -129,19 +266,7 @@ pimcore.plugin.dynamicSearch.settings = Class.create({ viewConfig: { trackOver: false }, - store: new Ext.data.JsonStore({ - autoDestroy: true, - autoLoad: true, - proxy: { - type: 'ajax', - url: Routing.generate('dynamic_search.controller.admin.get_provider'), - reader: { - type: 'json', - rootProperty: 'provider' - } - }, - fields: ['id', 'path', 'active'] - }), + store: this.providerStore, columns: [ { @@ -168,6 +293,5 @@ pimcore.plugin.dynamicSearch.settings = Class.create({ } ] }); - } -}); \ No newline at end of file +}); diff --git a/public/js/backend/startup.js b/public/js/backend/startup.js index e1eacd1..3ee7ced 100644 --- a/public/js/backend/startup.js +++ b/public/js/backend/startup.js @@ -17,7 +17,7 @@ class DynamicSearch { callback: function() { searchMenu = new Ext.Action({ id: 'search', - text: t('dynamic_search_settings'), + text: t('dynamic_search.settings'), iconCls: 'dynamic_search_bundle', handler: this.openSettingsPanel.bind(this) }); diff --git a/src/Controller/Admin/SettingsController.php b/src/Controller/Admin/SettingsController.php index 520456c..1b48c23 100644 --- a/src/Controller/Admin/SettingsController.php +++ b/src/Controller/Admin/SettingsController.php @@ -2,11 +2,15 @@ namespace DynamicSearchBundle\Controller\Admin; +use DynamicSearchBundle\Manager\QueueManagerInterface; use DynamicSearchBundle\Provider\Extension\ProviderBundleLocator; use DynamicSearchBundle\Registry\HealthStateRegistryInterface; +use DynamicSearchBundle\Runner\ContextRunnerInterface; use DynamicSearchBundle\State\HealthStateInterface; use Pimcore\Bundle\AdminBundle\Controller\AdminAbstractController; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; class SettingsController extends AdminAbstractController { @@ -51,7 +55,38 @@ public function providerAction(ProviderBundleLocator $providerBundleLocator): Js ]); } - public function contextFullConfigurationAction(ProviderBundleLocator $providerBundleLocator): JsonResponse + public function indexQueueInfoAction(QueueManagerInterface $queueManager): JsonResponse + { + return $this->json([ + 'tableName' => $queueManager->getQueueTableName(), + 'count' => $queueManager->getTotalQueuedItems() + ]); + } + + public function indexQueueAllDataAction(Request $request, ContextRunnerInterface $contextRunner): Response + { + $contextName = $request->get('context'); + + if (empty($contextName)) { + return new Response('no context given', 400); + } + + try { + $contextRunner->runSingleContextCreation($contextName); + } catch (\Throwable $e) { + return new Response($e->getMessage(), 500); + } + + return new Response(); + } + + public function clearIndexQueueAction(QueueManagerInterface $queueManager): Response + { + $queueManager->clearQueue(); + return new Response(); + } + + public function contextFullConfigurationAction(): JsonResponse { return $this->json($this->contextFullConfiguration); } diff --git a/src/Manager/QueueManager.php b/src/Manager/QueueManager.php index 743d2ca..797bb3b 100644 --- a/src/Manager/QueueManager.php +++ b/src/Manager/QueueManager.php @@ -14,12 +14,22 @@ public function __construct( ) {} + public function getQueueTableName(): string + { + return $this->tableName; + } + + public function getTotalQueuedItems(): int + { + $qb = $this->connection->createQueryBuilder(); + $qb->select('COUNT(id)')->from($this->tableName); + return (int)$qb->executeQuery()->fetchOne(); + } + public function clearQueue(): void { try { - $qb = $this->connection->createQueryBuilder(); - $qb->select('COUNT(id)')->from($this->tableName); - $affectedRows = current($qb->executeQuery()->fetchFirstColumn()); + $affectedRows = $this->getTotalQueuedItems(); $sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL($this->tableName); $this->connection->executeStatement($sql); $this->logger->debug(sprintf('data queue cleared. Affected jobs: %d', $affectedRows), 'queue', 'default'); diff --git a/src/Manager/QueueManagerInterface.php b/src/Manager/QueueManagerInterface.php index e24d4fb..0b5192e 100644 --- a/src/Manager/QueueManagerInterface.php +++ b/src/Manager/QueueManagerInterface.php @@ -4,5 +4,7 @@ interface QueueManagerInterface { + public function getQueueTableName(): string; + public function getTotalQueuedItems(): int; public function clearQueue(): void; } diff --git a/src/Runner/ContextRunner.php b/src/Runner/ContextRunner.php index 4fb8bf9..012aa01 100644 --- a/src/Runner/ContextRunner.php +++ b/src/Runner/ContextRunner.php @@ -39,7 +39,6 @@ public function runSingleContextCreation(string $contextName): void { $contextDefinition = $this->setupContextDefinition($contextName, ContextDefinitionInterface::CONTEXT_DISPATCH_TYPE_INDEX); - $this->queueManager->clearQueue(); $this->longProcessService->boot(); $this->dispatchContext($contextDefinition); diff --git a/translations/admin.en.yml b/translations/admin.en.yml index f81b786..6baf085 100755 --- a/translations/admin.en.yml +++ b/translations/admin.en.yml @@ -1 +1,15 @@ -dynamic_search_settings: 'Dynamic Search' \ No newline at end of file +dynamic_search.settings: 'Dynamic Search' + +dynamic_search.settings.health_status: 'Health Status' +dynamic_search.settings.provider: 'Prodiver' +dynamic_search.settings.index_queue: 'Index Queue' +dynamic_search.settings.index_queue.table_name: 'Table name' +dynamic_search.settings.index_queue.total_queued_items: 'Queued items' + +dynamic_search.actions.index_queue.queue_all_data: 'Queue all data for indexing' +dynamic_search.actions.index_queue.queue_all_data.confirmation.message: 'This will queue all data for indexing which might take some time. Continue?' +dynamic_search.actions.index_queue.queue_all_data.success: 'All data successfully added to queue' + +dynamic_search.actions.index_queue.clear: 'Clear index queue' +dynamic_search.actions.index_queue.clear.confirmation.message: 'This will clear all data in the index queue (context independent)! Continue?' +dynamic_search.actions.index_queue.clear.success: 'Successfully cleared index queue'