diff --git a/config/services.yaml b/config/services.yaml index e6826abb..f69af332 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -124,6 +124,7 @@ services: OpenDxp\Bundle\AdminBundle\Service\Email\UnusableRecipientDetector: ~ OpenDxp\Bundle\AdminBundle\Service\Email\EmailLogListingFactory: ~ + OpenDxp\Bundle\AdminBundle\Service\Email\EmailLogCleaner: ~ OpenDxp\Bundle\AdminBundle\Service\Login\LoginPageService: ~ diff --git a/public/js/opendxp/settings/email/log.js b/public/js/opendxp/settings/email/log.js index 190e5669..225d12be 100644 --- a/public/js/opendxp/settings/email/log.js +++ b/public/js/opendxp/settings/email/log.js @@ -18,6 +18,7 @@ opendxp.registerNS('opendxp.settings.email.log'); opendxp.settings.email.log = Class.create({ filterField: null, + cleanupUrl: null, exportPrepareUrl: null, exportProcessUrl: null, exportDownloadUrl: null, @@ -26,6 +27,7 @@ opendxp.settings.email.log = Class.create({ initialize: function(document) { this.document = document; + this.cleanupUrl = Routing.generate('opendxp_admin_email_cleanupemaillogs'); this.exportPrepareUrl = Routing.generate('opendxp_admin_email_exportemaillogs_prepare'); this.exportProcessUrl = Routing.generate('opendxp_admin_email_exportemaillogs'); this.exportDownloadUrl = Routing.generate('opendxp_admin_email_exportemaillogs_download'); @@ -421,7 +423,9 @@ opendxp.settings.email.log = Class.create({ tooltip: t('delete'), icon: '/bundles/opendxpadmin/img/flat-color-icons/delete.svg', handler: function (grid, rowIndex) { + let data = grid.getStore().getAt(rowIndex); + opendxp.helpers.deleteConfirm(t('email_log'), data.id, function () { var rec = grid.getStore().getAt(rowIndex); Ext.Ajax.request({ @@ -430,21 +434,18 @@ opendxp.settings.email.log = Class.create({ success: function(response){ var data = Ext.decode( response.responseText ); if(!data.success){ - Ext.Msg.alert(t('error'), - t('error_deleting_item')); + Ext.Msg.alert(t('error'), t('error_deleting_item')); } grid.getStore().reload(); }, failure: function () { - Ext.Msg.alert(t('error'), - t('error_deleting_item')); + Ext.Msg.alert(t('error'), t('error_deleting_item')); }, params: { id : rec.get('id') } }); }.bind(this)); - }.bind(this) }] } @@ -471,20 +472,30 @@ opendxp.settings.email.log = Class.create({ this.selectionColumn = new Ext.selection.CheckboxModel(); + var toolbarItems = ['->']; + + // cleaning up belongs to the global email panel only! + if (!this.document) { + toolbarItems.push({ + text: t('cleanup'), + iconCls: 'opendxp_icon_cleanup', + handler: this.showCleanupWindow.bind(this) + }, '-'); + } + + toolbarItems.push({ + text: t('export_csv'), + iconCls: 'opendxp_icon_export', + handler: this.doExport.bind(this) + }, '-', { + text: t('filter') + '/' + t('search'), + xtype: 'tbtext', + style: 'margin: 0 10px 0 0;' + }, this.filterField); + var toolbar = Ext.create('Ext.Toolbar', { cls: 'opendxp_main_toolbar', - items: [ - '->', - { - text: t('export_csv'), - iconCls: 'opendxp_icon_export', - handler: this.doExport.bind(this) - }, '-', { - text: t('filter') + '/' + t('search'), - xtype: 'tbtext', - style: 'margin: 0 10px 0 0;' - },this.filterField - ] + items: toolbarItems }); this.grid = new Ext.grid.GridPanel({ @@ -511,6 +522,73 @@ opendxp.settings.email.log = Class.create({ return this.grid; }, + showCleanupWindow: function () { + + var daysField = new Ext.form.field.Number({ + fieldLabel: t('email_log_cleanup_older_than_days'), + value: 30, + minValue: 1, + allowBlank: false, + allowDecimals: false, + labelWidth: 280, + anchor: '100%' + }); + + var win = new Ext.Window({ + title: t('email_log_cleanup'), + iconCls: 'opendxp_icon_cleanup', + width: 480, + modal: true, + bodyStyle: 'padding: 10px;', + layout: 'anchor', + items: [daysField], + buttons: [{ + text: t('cancel'), + handler: function () { + win.close(); + } + }, { + text: t('cleanup'), + iconCls: 'opendxp_icon_cleanup', + handler: function () { + if (!daysField.isValid()) { + return; + } + + this.cleanup(daysField.getValue(), win); + }.bind(this) + }] + }); + + win.show(); + }, + + cleanup: function (olderThanDays, win) { + + win.setLoading(t('please_wait')); + + Ext.Ajax.request({ + url: this.cleanupUrl, + method: 'DELETE', + params: { + olderThanDays: olderThanDays + }, + callback: function () { + win.setLoading(false); + }, + success: function (response) { + var rdata = Ext.decode(response.responseText); + win.close(); + + if (rdata) { + opendxp.helpers.showNotification(t('success'), sprintf(t('email_log_cleanup_success'), rdata.deleted), 'success'); + } + + this.store.reload(); + }.bind(this) + }); + }, + doExport: function () { var params = {}; diff --git a/src/Controller/Admin/EmailController.php b/src/Controller/Admin/EmailController.php index a3106633..ac917a03 100644 --- a/src/Controller/Admin/EmailController.php +++ b/src/Controller/Admin/EmailController.php @@ -22,6 +22,8 @@ use OpenDxp\Bundle\AdminBundle\Handler\Email\Blocklist\DeleteBlocklistEntry\DeleteBlocklistEntryHandler; use OpenDxp\Bundle\AdminBundle\Handler\Email\Blocklist\UpdateBlocklistEntry\UpdateBlocklistEntryHandler; use OpenDxp\Bundle\AdminBundle\Handler\Email\BlocklistPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Email\CleanupEmailLogs\CleanupEmailLogsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Email\CleanupEmailLogs\CleanupEmailLogsPayload; use OpenDxp\Bundle\AdminBundle\Handler\Email\DeleteEmailLog\DeleteEmailLogHandler; use OpenDxp\Bundle\AdminBundle\Handler\Email\DoEmailLogExport\DoEmailLogExportHandler; use OpenDxp\Bundle\AdminBundle\Handler\Email\DoEmailLogExport\DoEmailLogExportPayload; @@ -160,6 +162,15 @@ public function showEmailLogDetailsAction( return $this->apiJson($handler($payload), envelope: false); } + #[IsGranted(CorePermission::Emails->value)] + #[Route('/cleanup-email-logs', name: 'opendxp_admin_email_cleanupemaillogs', methods: ['DELETE'])] + public function cleanupEmailLogsAction( + CleanupEmailLogsHandler $handler, + CleanupEmailLogsPayload $payload, + ): JsonResponse { + return $this->apiJson($handler($payload)); + } + #[IsGranted(CorePermission::Emails->value)] #[Route('/delete-email-log', name: 'opendxp_admin_email_deleteemaillog', methods: ['DELETE'])] public function deleteEmailLogAction( diff --git a/src/Handler/Email/CleanupEmailLogs/CleanupEmailLogsHandler.php b/src/Handler/Email/CleanupEmailLogs/CleanupEmailLogsHandler.php new file mode 100644 index 00000000..9353751c --- /dev/null +++ b/src/Handler/Email/CleanupEmailLogs/CleanupEmailLogsHandler.php @@ -0,0 +1,42 @@ +olderThanDays < CleanupEmailLogsPayload::MINIMUM_DAYS) { + throw new AdminOperationFailedException( + sprintf( + 'Email logs can only be cleaned up from an age of %d day(s) on, got %d.', + CleanupEmailLogsPayload::MINIMUM_DAYS, + $payload->olderThanDays + ) + ); + } + + return new CleanupEmailLogsResult(deleted: $this->cleaner->deleteOlderThanDays($payload->olderThanDays)); + } +} diff --git a/src/Handler/Email/CleanupEmailLogs/CleanupEmailLogsPayload.php b/src/Handler/Email/CleanupEmailLogs/CleanupEmailLogsPayload.php new file mode 100644 index 00000000..ce6025f4 --- /dev/null +++ b/src/Handler/Email/CleanupEmailLogs/CleanupEmailLogsPayload.php @@ -0,0 +1,37 @@ +request->getString('olderThanDays'), + ); + } +} diff --git a/src/Handler/Email/CleanupEmailLogs/CleanupEmailLogsResult.php b/src/Handler/Email/CleanupEmailLogs/CleanupEmailLogsResult.php new file mode 100644 index 00000000..c7c5a8f0 --- /dev/null +++ b/src/Handler/Email/CleanupEmailLogs/CleanupEmailLogsResult.php @@ -0,0 +1,27 @@ +getTimestamp(); + $deleted = 0; + + do { + $list = new Email\Log\Listing(); + $list->setCondition('sentDate < ?', [$cutoff]); + + $list->setOrderKey('id'); + $list->setOrder('ASC'); + $list->setLimit(self::PAGE_SIZE); + + $entries = $list->getEmailLogs(); + + foreach ($entries as $entry) { + $entry->delete(); + $deleted++; + } + + if ($entries !== [] && $onProgress !== null) { + $onProgress($deleted); + } + + } while ($entries !== []); + + return $deleted; + } +} diff --git a/translations/admin.de.yaml b/translations/admin.de.yaml index 1766bbfa..bd668042 100644 --- a/translations/admin.de.yaml +++ b/translations/admin.de.yaml @@ -331,6 +331,9 @@ email_log_resend_window_error_message: 'Ein Fehler ist aufgetreten. Die E-Mail k email_log_resend_window_msg: 'Bitte bestätige, dass du die E-Mail wiederholt an alle Empfänger versenden möchtest.' email_log_resend_window_success_message: 'Die E-Mail wurde erfolgreich an alle Empfänger versendet.' email_log_sent_Date: 'Datum gesendet' +email_log_cleanup: 'Email-Logs bereinigen' +email_log_cleanup_older_than_days: 'Einträge löschen, die älter sind als (Tage)' +email_log_cleanup_success: '%s Email-Log-Einträge gelöscht.' email_log_export_confirmation: 'Sie sind dabei, %s Email-Log-Einträge zu exportieren. Möchten Sie fortfahren?' email_log_subject: 'Subject' email_log_to: 'An' diff --git a/translations/admin.en.yaml b/translations/admin.en.yaml index b799165e..1c99cfe6 100644 --- a/translations/admin.en.yaml +++ b/translations/admin.en.yaml @@ -331,6 +331,9 @@ email_log_resend_window_error_message: 'An error occurred. The email has not bee email_log_resend_window_msg: 'Please confirm that you want to send the email again to all recipients.' email_log_resend_window_success_message: 'The email has been sent successfully to all recipients.' email_log_sent_Date: 'Date sent' +email_log_cleanup: 'Clean Up Email Logs' +email_log_cleanup_older_than_days: 'Delete entries older than (days)' +email_log_cleanup_success: '%s email log entries deleted.' email_log_export_confirmation: 'You are about to export %s email log entries. Do you want to continue?' email_log_subject: 'Subject' email_log_to: 'To'