From 8a985929ed84cdb458a13c66b25f84e41133b24f Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 14 Apr 2024 15:29:16 +0200 Subject: Adding upstream version 1.0.1. Signed-off-by: Daniel Baumann --- library/Reporting/Web/Controller.php | 11 +- library/Reporting/Web/Flatpickr.php | 77 -------- library/Reporting/Web/Forms/DecoratedElement.php | 17 -- .../Web/Forms/Decorator/CompatDecorator.php | 63 ------- library/Reporting/Web/Forms/ReportForm.php | 179 ++++++++++++++----- library/Reporting/Web/Forms/ScheduleForm.php | 195 +++++++++++---------- library/Reporting/Web/Forms/SendForm.php | 8 +- library/Reporting/Web/Forms/TemplateForm.php | 190 +++++++++++--------- library/Reporting/Web/Forms/TimeframeForm.php | 195 ++++++++++++++++----- .../Web/ReportsTimeframesAndTemplatesTabs.php | 22 +-- library/Reporting/Web/Widget/CompatDropdown.php | 1 + library/Reporting/Web/Widget/CoverPage.php | 18 +- library/Reporting/Web/Widget/HeaderOrFooter.php | 10 +- library/Reporting/Web/Widget/Template.php | 44 ++--- 14 files changed, 558 insertions(+), 472 deletions(-) delete mode 100644 library/Reporting/Web/Flatpickr.php delete mode 100644 library/Reporting/Web/Forms/DecoratedElement.php delete mode 100644 library/Reporting/Web/Forms/Decorator/CompatDecorator.php (limited to 'library/Reporting/Web') diff --git a/library/Reporting/Web/Controller.php b/library/Reporting/Web/Controller.php index 5040183..1123332 100644 --- a/library/Reporting/Web/Controller.php +++ b/library/Reporting/Web/Controller.php @@ -1,20 +1,11 @@ hasBeenSubmitted() - && ((isset($form->valid) && $form->valid === true) - || $form->isValid()) - ) { - $this->redirectNow($url); - } - } } diff --git a/library/Reporting/Web/Flatpickr.php b/library/Reporting/Web/Flatpickr.php deleted file mode 100644 index 5f6605d..0000000 --- a/library/Reporting/Web/Flatpickr.php +++ /dev/null @@ -1,77 +0,0 @@ -allowInput = $state; - - return $this; - } - - protected function assembleElement() - { - if (version_compare(Version::VERSION, '2.9.0', '>=')) { - $element = parent::assembleElement(); - } else { - $element = $this->formElement; - } - - if (version_compare(Version::VERSION, '2.10.0', '<')) { - $element->getAttributes()->set('data-use-flatpickr-fallback', true); - } else { - $element->getAttributes()->set('data-use-datetime-picker', true); - } - - if (! $this->allowInput) { - return $element; - } - - $element->getAttributes() - ->set('data-input', true) - ->set('data-flatpickr-wrap', true) - ->set('data-flatpickr-allow-input', true) - ->set('data-flatpickr-click-opens', 'false'); - - return [ - $element, - Html::tag('button', ['type' => 'button', 'class' => 'icon-calendar', 'data-toggle' => true]), - Html::tag('button', ['type' => 'button', 'class' => 'icon-cancel', 'data-clear' => true]) - ]; - } - - protected function assemble() - { - if (version_compare(Version::VERSION, '2.9.0', '>=')) { - parent::assemble(); - return; - } - - if ($this->formElement->hasBeenValidated() && ! $this->formElement->isValid()) { - $this->getAttributes()->add('class', 'has-error'); - } - - $this->add(array_filter([ - $this->assembleLabel(), - $this->assembleElement(), - $this->assembleDescription(), - $this->assembleErrors() - ])); - } -} diff --git a/library/Reporting/Web/Forms/DecoratedElement.php b/library/Reporting/Web/Forms/DecoratedElement.php deleted file mode 100644 index 2578681..0000000 --- a/library/Reporting/Web/Forms/DecoratedElement.php +++ /dev/null @@ -1,17 +0,0 @@ -createElement($type, $name, $attributes); - $decorator->decorate($element); - $this->registerElement($element); - $this->add($element); - } -} diff --git a/library/Reporting/Web/Forms/Decorator/CompatDecorator.php b/library/Reporting/Web/Forms/Decorator/CompatDecorator.php deleted file mode 100644 index b2eb536..0000000 --- a/library/Reporting/Web/Forms/Decorator/CompatDecorator.php +++ /dev/null @@ -1,63 +0,0 @@ -getAttributes()->has('id')) { - $checkbox->setAttribute('id', base64_encode(random_bytes(8))); - } - - $checkbox->getAttributes()->add('class', 'sr-only'); - - $classes = ['toggle-switch']; - if ($checkbox->getAttributes()->get('disabled')->getValue()) { - $classes[] = 'disabled'; - } - - return [ - $checkbox, - new HtmlElement('label', Attributes::create([ - 'class' => $classes, - 'aria-hidden' => 'true', - 'for' => $checkbox->getAttributes()->get('id')->getValue() - ]), new HtmlElement('span', Attributes::create(['class' => 'toggle-slider']))) - ]; - } - - protected function assembleElementCompat() - { - if ($this->formElement instanceof CheckboxElement) { - return $this->createCheckboxCompat($this->formElement); - } - - return $this->formElement; - } - - protected function assemble() - { - if (version_compare(Version::VERSION, '2.9.0', '>=')) { - parent::assemble(); - return; - } - - if ($this->formElement->hasBeenValidated() && ! $this->formElement->isValid()) { - $this->getAttributes()->add('class', 'has-error'); - } - - $this->add(array_filter([ - $this->assembleLabel(), - $this->assembleElementCompat(), - $this->assembleDescription(), - $this->assembleErrors() - ])); - } -} diff --git a/library/Reporting/Web/Forms/ReportForm.php b/library/Reporting/Web/Forms/ReportForm.php index 6b1e692..40e376e 100644 --- a/library/Reporting/Web/Forms/ReportForm.php +++ b/library/Reporting/Web/Forms/ReportForm.php @@ -1,4 +1,5 @@ id = $id; + $form = new static(); + $form->id = $id; + + return $form; + } + + public function getId(): ?int + { + return $this->id; + } + + /** + * Set the label of the submit button + * + * @param string $label + * + * @return $this + */ + public function setSubmitButtonLabel(string $label): self + { + $this->submitButtonLabel = $label; return $this; } - protected function assemble() + /** + * Get the label of the submit button + * + * @return string + */ + public function getSubmitButtonLabel(): string + { + if ($this->submitButtonLabel !== null) { + return $this->submitButtonLabel; + } + + return $this->id === null ? $this->translate('Create Report') : $this->translate('Update Report'); + } + + /** + * Set whether the create and show submit button should be rendered + * + * @param bool $renderCreateAndShowButton + * + * @return $this + */ + public function setRenderCreateAndShowButton(bool $renderCreateAndShowButton): self { - $this->setDefaultElementDecorator(new CompatDecorator()); + $this->renderCreateAndShowButton = $renderCreateAndShowButton; + return $this; + } + + public function hasBeenSubmitted(): bool + { + return $this->hasBeenSent() && ( + $this->getPopulatedValue('submit') + || $this->getPopulatedValue('create_show') + || $this->getPopulatedValue('remove') + ); + } + + protected function assemble() + { $this->addElement('text', 'name', [ - 'required' => true, - 'label' => 'Name' + 'required' => true, + 'label' => $this->translate('Name'), + 'description' => $this->translate( + 'A unique name of this report. It is used when exporting to pdf, json or csv format' + . ' and also when listing the reports in the cli' + ), + 'validators' => [ + 'Callback' => function ($value, CallbackValidator $validator) { + if ($value !== null && strpos($value, '..') !== false) { + $validator->addMessage( + $this->translate('Double dots are not allowed in the report name') + ); + + return false; + } + + return true; + } + ] ]); $this->addElement('select', 'timeframe', [ - 'required' => true, - 'label' => 'Timeframe', - 'options' => [null => 'Please choose'] + $this->listTimeframes(), - 'class' => 'autosubmit' + 'required' => true, + 'class' => 'autosubmit', + 'label' => $this->translate('Timeframe'), + 'options' => [null => $this->translate('Please choose')] + Database::listTimeframes(), + 'description' => $this->translate( + 'Specifies the time frame in which this report is to be generated' + ) ]); $this->addElement('select', 'template', [ - 'label' => 'Template', - 'options' => [null => 'Please choose'] + $this->listTemplates() + 'label' => $this->translate('Template'), + 'options' => [null => $this->translate('Please choose')] + Database::listTemplates(), + 'description' => $this->translate( + 'Specifies the template to use when exporting this report to pdf. (Default Icinga template)' + ) ]); $this->addElement('select', 'reportlet', [ - 'required' => true, - 'label' => 'Report', - 'options' => [null => 'Please choose'] + $this->listReports(), - 'class' => 'autosubmit' + 'required' => true, + 'class' => 'autosubmit', + 'label' => $this->translate('Report'), + 'options' => [null => $this->translate('Please choose')] + $this->listReports(), + 'description' => $this->translate('Specifies the type of the reportlet to be generated') ]); $values = $this->getValues(); @@ -63,7 +153,7 @@ class ReportForm extends CompatForm // $config->populate($this->getValues()); /** @var \Icinga\Module\Reporting\Hook\ReportHook $reportlet */ - $reportlet = new $values['reportlet']; + $reportlet = new $values['reportlet'](); $reportlet->initConfigForm($config); @@ -73,40 +163,43 @@ class ReportForm extends CompatForm } $this->addElement('submit', 'submit', [ - 'label' => $this->id === null ? 'Create Report' : 'Update Report' + 'label' => $this->getSubmitButtonLabel() ]); if ($this->id !== null) { /** @var FormSubmitElement $removeButton */ $removeButton = $this->createElement('submit', 'remove', [ - 'label' => 'Remove Report', + 'label' => $this->translate('Remove Report'), 'class' => 'btn-remove', 'formnovalidate' => true ]); $this->registerElement($removeButton); - $this->getElement('submit')->getWrapper()->prepend($removeButton); - - if ($removeButton->hasBeenPressed()) { - $this->getDb()->delete('report', ['id = ?' => $this->id]); - // Stupid cheat because ipl/html is not capable of multiple submit buttons - $this->getSubmitButton()->setValue($this->getSubmitButton()->getButtonLabel()); - $this->callOnSuccess = false; - $this->valid = true; + /** @var HtmlDocument $wrapper */ + $wrapper = $this->getElement('submit')->getWrapper(); + $wrapper->prepend($removeButton); + } elseif ($this->renderCreateAndShowButton) { + $createAndShow = $this->createElement('submit', 'create_show', [ + 'label' => $this->translate('Create and Show'), + ]); + $this->registerElement($createAndShow); - return; - } + /** @var HtmlDocument $wrapper */ + $wrapper = $this->getElement('submit')->getWrapper(); + $wrapper->prepend($createAndShow); } } public function onSuccess() { - if ($this->callOnSuccess === false) { + $db = Database::get(); + + if ($this->getPopulatedValue('remove')) { + $db->delete('report', ['id = ?' => $this->id]); + return; } - $db = $this->getDb(); - $values = $this->getValues(); $now = time() * 1000; @@ -155,14 +248,16 @@ class ReportForm extends CompatForm foreach ($values as $name => $value) { $db->insert('config', [ - 'reportlet_id' => $reportletId, - 'name' => $name, - 'value' => $value, - 'ctime' => $now, - 'mtime' => $now + 'reportlet_id' => $reportletId, + 'name' => $name, + 'value' => $value, + 'ctime' => $now, + 'mtime' => $now ]); } $db->commitTransaction(); + + $this->id = $reportId; } } diff --git a/library/Reporting/Web/Forms/ScheduleForm.php b/library/Reporting/Web/Forms/ScheduleForm.php index 47f3ee3..72c4767 100644 --- a/library/Reporting/Web/Forms/ScheduleForm.php +++ b/library/Reporting/Web/Forms/ScheduleForm.php @@ -1,96 +1,98 @@ report = $report; + $this->scheduleElement = new ScheduleElement('schedule_element'); + /** @var Web $app */ + $app = Icinga::app(); + $this->scheduleElement->setIdProtector([$app->getRequest(), 'protectId']); + } - $schedule = $report->getSchedule(); + public function getPartUpdates(): array + { + return $this->scheduleElement->prepareMultipartUpdate($this->getRequest()); + } + + /** + * Create a new form instance with the given report + * + * @param Report $report + * + * @return static + */ + public static function fromReport(Report $report): self + { + $form = new static(); + $form->report = $report; + $schedule = $report->getSchedule(); if ($schedule !== null) { - $this->setId($schedule->getId()); + $config = $schedule->getConfig(); + $config['action'] = $schedule->getAction(); + + /** @var Frequency $type */ + $type = $config['frequencyType']; + $config['schedule_element'] = $type::fromJson($config['frequency']); - $values = [ - 'start' => $schedule->getStart()->format('Y-m-d\\TH:i:s'), - 'frequency' => $schedule->getFrequency(), - 'action' => $schedule->getAction() - ] + $schedule->getConfig(); + unset($config['frequency']); + unset($config['frequencyType']); - $this->populate($values); + $form->populate($config); } - return $this; + return $form; } - public function setId($id) + public function hasBeenSubmitted(): bool { - $this->id = $id; - - return $this; + return $this->hasBeenSent() && ( + $this->getPopulatedValue('submit') + || $this->getPopulatedValue('remove') + || $this->getPopulatedValue('send') + ); } protected function assemble() { - $this->setDefaultElementDecorator(new CompatDecorator()); - - $frequency = [ - 'minutely' => 'Minutely', - 'hourly' => 'Hourly', - 'daily' => 'Daily', - 'weekly' => 'Weekly', - 'monthly' => 'Monthly' - ]; - - if (version_compare(Version::VERSION, '2.9.0', '>=')) { - $this->addElement('localDateTime', 'start', [ - 'required' => true, - 'label' => t('Start'), - 'placeholder' => t('Choose date and time') - ]); - } else { - $this->addDecoratedElement((new Flatpickr())->setAllowInput(false), 'text', 'start', [ - 'required' => true, - 'label' => t('Start'), - 'placeholder' => t('Choose date and time') - ]); - } - - $this->addElement('select', 'frequency', [ - 'required' => true, - 'label' => 'Frequency', - 'options' => [null => 'Please choose'] + $frequency, - ]); - $this->addElement('select', 'action', [ - 'required' => true, - 'label' => 'Action', - 'options' => [null => 'Please choose'] + $this->listActions(), - 'class' => 'autosubmit' + 'required' => true, + 'class' => 'autosubmit', + 'options' => array_merge([null => $this->translate('Please choose')], $this->listActions()), + 'label' => $this->translate('Action'), + 'description' => $this->translate('Specifies an action to be triggered by the scheduler') ]); $values = $this->getValues(); @@ -99,8 +101,8 @@ class ScheduleForm extends CompatForm $config = new Form(); // $config->populate($this->getValues()); - /** @var \Icinga\Module\Reporting\Hook\ActionHook $action */ - $action = new $values['action']; + /** @var ActionHook $action */ + $action = new $values['action'](); $action->initConfigForm($config, $this->report); @@ -109,67 +111,80 @@ class ScheduleForm extends CompatForm } } + $this->addHtml(HtmlElement::create('div', ['class' => 'schedule-element-separator'])); + $this->addElement($this->scheduleElement); + + $schedule = $this->report->getSchedule(); $this->addElement('submit', 'submit', [ - 'label' => $this->id === null ? 'Create Schedule' : 'Update Schedule' + 'label' => $schedule === null ? $this->translate('Create Schedule') : $this->translate('Update Schedule') ]); - if ($this->id !== null) { + if ($schedule !== null) { + $sendButton = $this->createElement('submit', 'send', [ + 'label' => $this->translate('Send Report Now'), + 'formnovalidate' => true + ]); + $this->registerElement($sendButton); + + /** @var HtmlDocument $wrapper */ + $wrapper = $this->getElement('submit')->getWrapper(); + $wrapper->prepend($sendButton); + /** @var FormSubmitElement $removeButton */ $removeButton = $this->createElement('submit', 'remove', [ - 'label' => 'Remove Schedule', + 'label' => $this->translate('Remove Schedule'), 'class' => 'btn-remove', 'formnovalidate' => true ]); $this->registerElement($removeButton); - $this->getElement('submit')->getWrapper()->prepend($removeButton); - - if ($removeButton->hasBeenPressed()) { - $this->getDb()->delete('schedule', ['id = ?' => $this->id]); - - // Stupid cheat because ipl/html is not capable of multiple submit buttons - $this->getSubmitButton()->setValue($this->getSubmitButton()->getButtonLabel()); - $this->valid = true; - - return; - } + $wrapper->prepend($removeButton); } } public function onSuccess() { - $db = $this->getDb(); + $db = Database::get(); + $schedule = $this->report->getSchedule(); + if ($this->getPopulatedValue('remove')) { + $db->delete('schedule', ['id = ?' => $schedule->getId()]); - $values = $this->getValues(); + return; + } - $now = time() * 1000; + $values = $this->getValues(); + if ($this->getPopulatedValue('send')) { + $action = new $values['action'](); + $action->execute($this->report, $values); - if (! $values['start'] instanceof DateTime) { - $values['start'] = DateTime::createFromFormat('Y-m-d H:i:s', $values['start']); + return; } - $data = [ - 'start' => $values['start']->getTimestamp() * 1000, - 'frequency' => $values['frequency'], - 'action' => $values['action'], - 'mtime' => $now - ]; - - unset($values['start']); - unset($values['frequency']); + $action = $values['action']; unset($values['action']); + unset($values['schedule_element']); - $data['config'] = json_encode($values); + $frequency = $this->scheduleElement->getValue(); + $values['frequency'] = Json::encode($frequency); + $values['frequencyType'] = get_php_type($frequency); + $config = Json::encode($values); $db->beginTransaction(); - if ($this->id === null) { - $db->insert('schedule', $data + [ + if ($schedule === null) { + $now = (new DateTime())->getTimestamp() * 1000; + $db->insert('schedule', [ 'author' => Auth::getInstance()->getUser()->getUsername(), 'report_id' => $this->report->getId(), - 'ctime' => $now + 'ctime' => $now, + 'mtime' => $now, + 'action' => $action, + 'config' => $config ]); } else { - $db->update('schedule', $data, ['id = ?' => $this->id]); + $db->update('schedule', [ + 'action' => $action, + 'config' => $config + ], ['id = ?' => $schedule->getId()]); } $db->commitTransaction(); diff --git a/library/Reporting/Web/Forms/SendForm.php b/library/Reporting/Web/Forms/SendForm.php index 03b691c..e3cf3ec 100644 --- a/library/Reporting/Web/Forms/SendForm.php +++ b/library/Reporting/Web/Forms/SendForm.php @@ -1,18 +1,16 @@ setDefaultElementDecorator(new CompatDecorator()); - (new SendMail())->initConfigForm($this, $this->report); $this->addElement('submit', 'submit', [ - 'label' => 'Send Report' + 'label' => $this->translate('Send Report') ]); } diff --git a/library/Reporting/Web/Forms/TemplateForm.php b/library/Reporting/Web/Forms/TemplateForm.php index bb062bb..4cd44a9 100644 --- a/library/Reporting/Web/Forms/TemplateForm.php +++ b/library/Reporting/Web/Forms/TemplateForm.php @@ -1,23 +1,21 @@ template; } - public function setTemplate($template) + /** + * Create a new form instance with the given report + * + * @param $template + * + * @return static + */ + public static function fromTemplate($template): self { - $this->template = $template; + $form = new static(); + + $template->settings = Json::decode($template->settings, true); + $form->template = $template; if ($template->settings) { - $this->populate(array_filter($template->settings, function ($value) { + /** @var array $settings */ + $settings = $template->settings; + $form->populate(array_filter($settings, function ($value) { // Don't populate files return ! is_array($value); })); } - return $this; + return $form; } - protected function assemble() + public function hasBeenSubmitted(): bool { - $this->setDefaultElementDecorator(new CompatDecorator()); + return $this->hasBeenSent() && ($this->getPopulatedValue('submit') || $this->getPopulatedValue('remove')); + } + protected function assemble() + { $this->setAttribute('enctype', 'multipart/form-data'); $this->add(Html::tag('h2', 'Template Settings')); $this->addElement('text', 'name', [ - 'label' => 'Name', - 'placeholder' => 'Template name', + 'label' => $this->translate('Name'), + 'placeholder' => $this->translate('Template name'), 'required' => true ]); - $this->add(Html::tag('h2', 'Cover Page Settings')); + $this->add(Html::tag('h2', $this->translate('Cover Page Settings'))); - $this->addElement(new FileElement('cover_page_background_image', [ - 'label' => 'Background Image', - 'accept' => 'image/png, image/jpeg' - ])); + $this->addElement('file', 'cover_page_background_image', [ + 'label' => $this->translate('Background Image'), + 'accept' => ['image/png', 'image/jpeg', 'image/jpg'], + 'destination' => sys_get_temp_dir() + ]); - if ($this->template !== null - && isset($this->template->settings['cover_page_background_image']) + if ( + $this->template !== null + && isset($this->template->settings['cover_page_background_image']) ) { $this->add(Html::tag( 'p', - ['style' => ['margin-left: 14em;']], - 'Upload a new background image to override the existing one' + ['class' => 'override-uploaded-file-hint'], + $this->translate('Upload a new background image to override the existing one') )); $this->addElement('checkbox', 'remove_cover_page_background_image', [ - 'label' => 'Remove background image' + 'label' => $this->translate('Remove background image') ]); } - $this->addElement(new FileElement('cover_page_logo', [ - 'label' => 'Logo', - 'accept' => 'image/png, image/jpeg' - ])); + $this->addElement('file', 'cover_page_logo', [ + 'label' => $this->translate('Logo'), + 'accept' => ['image/png', 'image/jpeg', 'image/jpg'], + 'destination' => sys_get_temp_dir() + ]); - if ($this->template !== null + if ( + $this->template !== null && isset($this->template->settings['cover_page_logo']) ) { $this->add(Html::tag( 'p', - ['style' => ['margin-left: 14em;']], - 'Upload a new logo to override the existing one' + ['class' => 'override-uploaded-file-hint'], + $this->translate('Upload a new logo to override the existing one') )); $this->addElement('checkbox', 'remove_cover_page_logo', [ - 'label' => 'Remove Logo' + 'label' => $this->translate('Remove Logo') ]); } $this->addElement('textarea', 'title', [ - 'label' => 'Title', - 'placeholder' => 'Report title' + 'label' => $this->translate('Title'), + 'placeholder' => $this->translate('Report title') ]); $this->addElement('text', 'color', [ - 'label' => 'Color', - 'placeholder' => 'CSS color code' + 'label' => $this->translate('Color'), + 'placeholder' => $this->translate('CSS color code') ]); - $this->add(Html::tag('h2', 'Header Settings')); + $this->add(Html::tag('h2', $this->translate('Header Settings'))); - $this->addColumnSettings('header_column1', 'Column 1'); - $this->addColumnSettings('header_column2', 'Column 2'); - $this->addColumnSettings('header_column3', 'Column 3'); + $this->addColumnSettings('header_column1', $this->translate('Column 1')); + $this->addColumnSettings('header_column2', $this->translate('Column 2')); + $this->addColumnSettings('header_column3', $this->translate('Column 3')); - $this->add(Html::tag('h2', 'Footer Settings')); + $this->add(Html::tag('h2', $this->translate('Footer Settings'))); - $this->addColumnSettings('footer_column1', 'Column 1'); - $this->addColumnSettings('footer_column2', 'Column 2'); - $this->addColumnSettings('footer_column3', 'Column 3'); + $this->addColumnSettings('footer_column1', $this->translate('Column 1')); + $this->addColumnSettings('footer_column2', $this->translate('Column 2')); + $this->addColumnSettings('footer_column3', $this->translate('Column 3')); $this->addElement('submit', 'submit', [ - 'label' => $this->template === null ? 'Create Template' : 'Update Template' + 'label' => $this->template === null + ? $this->translate('Create Template') + : $this->translate('Update Template') ]); if ($this->template !== null) { /** @var FormSubmitElement $removeButton */ $removeButton = $this->createElement('submit', 'remove', [ - 'label' => 'Remove Template', + 'label' => $this->translate('Remove Template'), 'class' => 'btn-remove', 'formnovalidate' => true ]); $this->registerElement($removeButton); - $this->getElement('submit')->getWrapper()->prepend($removeButton); - if ($removeButton->hasBeenPressed()) { - $this->getDb()->delete('template', ['id = ?' => $this->template->id]); - - // Stupid cheat because ipl/html is not capable of multiple submit buttons - $this->getSubmitButton()->setValue($this->getSubmitButton()->getButtonLabel()); - $this->callOnSuccess = false; - $this->valid = true; - - return; - } + /** @var HtmlDocument $wrapper */ + $wrapper = $this->getElement('submit')->getWrapper(); + $wrapper->prepend($removeButton); } } public function onSuccess() { - if ($this->callOnSuccess === false) { + if ($this->getPopulatedValue('remove')) { + Database::get()->delete('template', ['id = ?' => $this->template->id]); + return; } @@ -153,20 +166,17 @@ class TemplateForm extends CompatForm $settings = $this->getValues(); try { - /** @var $uploadedFile \GuzzleHttp\Psr7\UploadedFile */ - foreach ($this->getRequest()->getUploadedFiles() as $name => $uploadedFile) { - if ($uploadedFile->getError() === UPLOAD_ERR_NO_FILE) { - continue; + foreach ($settings as $name => $setting) { + if ($setting instanceof UploadedFile) { + $settings[$name] = [ + 'mime_type' => $setting->getClientMediaType(), + 'size' => $setting->getSize(), + 'content' => base64_encode((string) $setting->getStream()) + ]; } - - $settings[$name] = [ - 'mime_type' => $uploadedFile->getClientMediaType(), - 'size' => $uploadedFile->getSize(), - 'content' => base64_encode((string) $uploadedFile->getStream()) - ]; } - $db = $this->getDb(); + $db = Database::get(); $now = time() * 1000; @@ -179,19 +189,21 @@ class TemplateForm extends CompatForm 'mtime' => $now ]); } else { - if (isset($settings['remove_cover_page_background_image'])) { + if ($this->getValue('remove_cover_page_background_image', 'n') === 'y') { unset($settings['cover_page_background_image']); unset($settings['remove_cover_page_background_image']); - } elseif (! isset($settings['cover_page_background_image']) + } elseif ( + ! isset($settings['cover_page_background_image']) && isset($this->template->settings['cover_page_background_image']) ) { $settings['cover_page_background_image'] = $this->template->settings['cover_page_background_image']; } - if (isset($settings['remove_cover_page_logo'])) { + if ($this->getValue('remove_cover_page_logo', 'n') === 'y') { unset($settings['cover_page_logo']); unset($settings['remove_cover_page_logo']); - } elseif (! isset($settings['cover_page_logo']) + } elseif ( + ! isset($settings['cover_page_logo']) && isset($this->template->settings['cover_page_logo']) ) { $settings['cover_page_logo'] = $this->template->settings['cover_page_logo']; @@ -204,7 +216,8 @@ class TemplateForm extends CompatForm if ($settings[$type] === 'image') { $value = "{$headerOrFooter}_column{$i}_value"; - if (! isset($settings[$value]) + if ( + ! isset($settings[$value]) && isset($this->template->settings[$value]) ) { $settings[$value] = $this->template->settings[$value]; @@ -219,7 +232,7 @@ class TemplateForm extends CompatForm 'mtime' => $now ], ['id = ?' => $this->template->id]); } - } catch (\Exception $e) { + } catch (Exception $e) { die($e->getMessage()); } } @@ -240,20 +253,31 @@ class TemplateForm extends CompatForm ] ]); + $valueType = $this->getValue($type, 'none'); + $populated = $this->getPopulatedValue($value); + if ( + ($valueType === 'image' && ! $populated instanceof UploadedFile) + || ($valueType !== 'image' && $populated instanceof UploadedFile) + ) { + $this->clearPopulatedValue($value); + } + switch ($this->getValue($type, 'none')) { case 'image': - $this->addElement(new FileElement($value, [ - 'label' => 'Image', - 'accept' => 'image/png, image/jpeg' - ])); + $this->addElement('file', $value, [ + 'label' => 'Image', + 'accept' => ['image/png', 'image/jpeg', 'image/jpg'], + 'destination' => sys_get_temp_dir() + ]); - if ($this->template !== null + if ( + $this->template !== null && $this->template->settings[$type] === 'image' && isset($this->template->settings[$value]) ) { $this->add(Html::tag( 'p', - ['style' => ['margin-left: 14em;']], + ['class' => 'override-uploaded-file-hint'], 'Upload a new image to override the existing one' )); } @@ -270,7 +294,7 @@ class TemplateForm extends CompatForm 'page_of' => 'Page Number + Total Number of Pages', 'date' => 'Date' ], - 'value' => 'report_title' + 'value' => 'report_title' ]); break; case 'text': diff --git a/library/Reporting/Web/Forms/TimeframeForm.php b/library/Reporting/Web/Forms/TimeframeForm.php index 3d78709..37ea34f 100644 --- a/library/Reporting/Web/Forms/TimeframeForm.php +++ b/library/Reporting/Web/Forms/TimeframeForm.php @@ -1,86 +1,203 @@ id = $id; + $form = new static(); + + $form->id = $id; - return $this; + return $form; } - protected function assemble() + public function hasBeenSubmitted(): bool { - $this->setDefaultElementDecorator(new CompatDecorator()); + return $this->hasBeenSent() && ($this->getPopulatedValue('submit') || $this->getPopulatedValue('remove')); + } + protected function assemble() + { $this->addElement('text', 'name', [ - 'required' => true, - 'label' => 'Name' + 'required' => true, + 'label' => $this->translate('Name'), + 'description' => $this->translate('A unique name of this timeframe') ]); - $flatpickr = new Flatpickr(); + $default = new DateTime('00:00:00'); + $start = $this->getPopulatedValue('start', $default); + if (! $start instanceof DateTime) { + $datetime = DateTime::createFromFormat(LocalDateTimeElement::FORMAT, $start); + if ($datetime) { + $start = $datetime; + } + } - $this->addDecoratedElement($flatpickr, 'text', 'start', [ - 'required' => true, - 'label' => 'Start', - 'placeholder' => 'Select a start date or provide a textual datetime description', - 'data-flatpickr-default-hour' => '00' + $relativeStart = $this->getPopulatedValue('relative-start', $start instanceof DateTime ? 'n' : 'y'); + $this->addElement('checkbox', 'relative-start', [ + 'required' => false, + 'class' => 'autosubmit', + 'value' => $relativeStart, + 'label' => $this->translate('Relative Start') ]); - $this->addDecoratedElement($flatpickr, 'text', 'end', [ - 'required' => true, - 'label' => 'End', - 'placeholder' => 'Select a end date or provide a textual datetime description', - 'data-flatpickrDefaultHour' => '23', - 'data-flatpickrDefaultMinute' => '59', - 'data-flatpickrDefaultSeconds' => '59' - ]); + if ($relativeStart === 'n') { + if (! $start instanceof DateTime) { + $start = $default; + $this->clearPopulatedValue('start'); + } + + $this->addElement( + new LocalDateTimeElement('start', [ + 'required' => true, + 'value' => $start, + 'label' => $this->translate('Start'), + 'description' => $this->translate('Specifies the start time of this timeframe') + ]) + ); + } else { + $this->addElement('text', 'start', [ + 'required' => true, + 'label' => $this->translate('Start'), + 'placeholder' => $this->translate('First day of this month'), + 'description' => $this->translate('Specifies the start time of this timeframe'), + 'validators' => [ + new CallbackValidator(function ($value, CallbackValidator $validator) { + if ($value !== null) { + try { + new DateTime($value); + } catch (Exception $_) { + $validator->addMessage($this->translate('Invalid textual date time')); + + return false; + } + } + + return true; + }) + ] + ]); + } + + $default = new DateTime('23:59:59'); + $end = $this->getPopulatedValue('end', $default); + if (! $end instanceof DateTime) { + $datetime = DateTime::createFromFormat(LocalDateTimeElement::FORMAT, $end); + if ($datetime) { + $end = $datetime; + } + } + + $relativeEnd = $this->getPopulatedValue('relative-end', $end instanceof DateTime ? 'n' : 'y'); + if ($relativeStart === 'y') { + $this->addElement('checkbox', 'relative-end', [ + 'required' => false, + 'class' => 'autosubmit', + 'value' => $relativeEnd, + 'label' => $this->translate('Relative End') + ]); + } + + if ($relativeEnd === 'n' || $relativeStart === 'n') { + if (! $end instanceof DateTime) { + $end = $default; + $this->clearPopulatedValue('end'); + } + + $this->addElement( + new LocalDateTimeElement('end', [ + 'required' => true, + 'value' => $end, + 'label' => $this->translate('End'), + 'description' => $this->translate('Specifies the end time of this timeframe') + ]) + ); + } else { + $this->addElement('text', 'end', [ + 'required' => true, + 'label' => $this->translate('End'), + 'placeholder' => $this->translate('Last day of this month'), + 'description' => $this->translate('Specifies the end time of this timeframe'), + 'validators' => [ + new CallbackValidator(function ($value, CallbackValidator $validator) { + if ($value !== null) { + try { + new DateTime($value); + } catch (Exception $_) { + $validator->addMessage($this->translate('Invalid textual date time')); + + return false; + } + } + + return true; + }) + ] + ]); + } $this->addElement('submit', 'submit', [ - 'label' => $this->id === null ? 'Create Time Frame' : 'Update Time Frame' + 'label' => $this->id === null + ? $this->translate('Create Time Frame') + : $this->translate('Update Time Frame') ]); if ($this->id !== null) { /** @var FormSubmitElement $removeButton */ $removeButton = $this->createElement('submit', 'remove', [ - 'label' => 'Remove Time Frame', + 'label' => $this->translate('Remove Time Frame'), 'class' => 'btn-remove', 'formnovalidate' => true ]); $this->registerElement($removeButton); - $this->getElement('submit')->getWrapper()->prepend($removeButton); - if ($removeButton->hasBeenPressed()) { - $this->getDb()->delete('timeframe', ['id = ?' => $this->id]); - - // Stupid cheat because ipl/html is not capable of multiple submit buttons - $this->getSubmitButton()->setValue($this->getSubmitButton()->getButtonLabel()); - $this->valid = true; - - return; - } + /** @var HtmlDocument $wrapper */ + $wrapper = $this->getElement('submit')->getWrapper(); + $wrapper->prepend($removeButton); } } public function onSuccess() { - $db = $this->getDb(); + $db = Database::get(); + + if ($this->getPopulatedValue('remove')) { + $db->delete('timeframe', ['id = ?' => $this->id]); + + return; + } $values = $this->getValues(); + if ($values['start'] instanceof DateTime) { + $values['start'] = $values['start']->format(LocalDateTimeElement::FORMAT); + } + + if ($values['end'] instanceof DateTime) { + $values['end'] = $values['end']->format(LocalDateTimeElement::FORMAT); + } $now = time() * 1000; diff --git a/library/Reporting/Web/ReportsTimeframesAndTemplatesTabs.php b/library/Reporting/Web/ReportsTimeframesAndTemplatesTabs.php index afb8b14..fee6770 100644 --- a/library/Reporting/Web/ReportsTimeframesAndTemplatesTabs.php +++ b/library/Reporting/Web/ReportsTimeframesAndTemplatesTabs.php @@ -1,4 +1,5 @@ getTabs(); + $tabs->getAttributes()->set('data-base-target', '_main'); $tabs->add('reports', [ - 'title' => $this->translate('Show reports'), - 'label' => $this->translate('Reports'), - 'url' => 'reporting/reports' + 'title' => $this->translate('Show reports'), + 'label' => $this->translate('Reports'), + 'url' => 'reporting/reports' ]); $tabs->add('timeframes', [ - 'title' => $this->translate('Show time frames'), - 'label' => $this->translate('Time Frames'), - 'url' => 'reporting/timeframes' + 'title' => $this->translate('Show time frames'), + 'label' => $this->translate('Time Frames'), + 'url' => 'reporting/timeframes' ]); $tabs->add('templates', [ - 'title' => $this->translate('Show templates'), - 'label' => $this->translate('Templates'), - 'url' => 'reporting/templates' + 'title' => $this->translate('Show templates'), + 'label' => $this->translate('Templates'), + 'url' => 'reporting/templates' ]); return $tabs; diff --git a/library/Reporting/Web/Widget/CompatDropdown.php b/library/Reporting/Web/Widget/CompatDropdown.php index cdd7b40..f5d4b03 100644 --- a/library/Reporting/Web/Widget/CompatDropdown.php +++ b/library/Reporting/Web/Widget/CompatDropdown.php @@ -1,4 +1,5 @@ hasBackgroundImage()) { - $this - ->getAttributes() - ->add('style', "background-image: url('" . Template::getDataUrl($this->getBackgroundImage()) . "');"); + $coverPageBackground = (new StyleWithNonce()) + ->setModule('reporting') + ->addFor($this, [ + 'background-image' => sprintf("url('%s')", Template::getDataUrl($this->getBackgroundImage())) + ]); + + $this->addHtml($coverPageBackground); } $content = Html::tag('div', ['class' => 'cover-page-content']); - if ($this->hasColor()) { - $content->getAttributes()->add('style', "color: {$this->getColor()};"); + $coverPageLogo = (new StyleWithNonce()) + ->setModule('reporting') + ->addFor($content, ['color' => $this->getColor()]); + + $content->addHtml($coverPageLogo); } if ($this->hasLogo()) { diff --git a/library/Reporting/Web/Widget/HeaderOrFooter.php b/library/Reporting/Web/Widget/HeaderOrFooter.php index dcb37e7..3ec9a7f 100644 --- a/library/Reporting/Web/Widget/HeaderOrFooter.php +++ b/library/Reporting/Web/Widget/HeaderOrFooter.php @@ -10,9 +10,9 @@ class HeaderOrFooter extends HtmlDocument { use Macros; - const HEADER = 'header'; + public const HEADER = 'header'; - const FOOTER = 'footer'; + public const FOOTER = 'footer'; protected $type; @@ -64,9 +64,9 @@ class HeaderOrFooter extends HtmlDocument protected function createColumn(array $data, $key) { - $typeKey = "${key}_type"; - $valueKey = "${key}_value"; - $type = isset($data[$typeKey]) ? $data[$typeKey] : null; + $typeKey = "{$key}_type"; + $valueKey = "{$key}_value"; + $type = $data[$typeKey] ?? null; switch ($type) { case 'text': diff --git a/library/Reporting/Web/Widget/Template.php b/library/Reporting/Web/Widget/Template.php index e780a3d..0f07703 100644 --- a/library/Reporting/Web/Widget/Template.php +++ b/library/Reporting/Web/Widget/Template.php @@ -1,17 +1,15 @@ from('template') - ->columns('*') - ->where(['id = ?' => $id]); - - $row = $template->getDb()->select($select)->fetch(); - - if ($row === false) { - return null; - } - - $row->settings = json_decode($row->settings, true); + $templateModel->settings = json_decode($templateModel->settings, true); $coverPage = (new CoverPage()) - ->setColor($row->settings['color']) - ->setTitle($row->settings['title']); + ->setColor($templateModel->settings['color']) + ->setTitle($templateModel->settings['title']); - if (isset($row->settings['cover_page_background_image'])) { - $coverPage->setBackgroundImage($row->settings['cover_page_background_image']); + if (isset($templateModel->settings['cover_page_background_image'])) { + $coverPage->setBackgroundImage($templateModel->settings['cover_page_background_image']); } - if (isset($row->settings['cover_page_logo'])) { - $coverPage->setLogo($row->settings['cover_page_logo']); + if (isset($templateModel->settings['cover_page_logo'])) { + $coverPage->setLogo($templateModel->settings['cover_page_logo']); } $template ->setCoverPage($coverPage) - ->setHeader(new HeaderOrFooter(HeaderOrFooter::HEADER, $row->settings)) - ->setFooter(new HeaderOrFooter(HeaderOrFooter::FOOTER, $row->settings)); + ->setHeader(new HeaderOrFooter(HeaderOrFooter::HEADER, $templateModel->settings)) + ->setFooter(new HeaderOrFooter(HeaderOrFooter::FOOTER, $templateModel->settings)); return $template; } -- cgit v1.2.3