Skip to content

Commit

Permalink
TASK: Improve export when form definition changes, allow setting node…
Browse files Browse the repository at this point in the history
…TypesIgnoredInExport

This package only stores the FormElement identifier at the time of form
submission in its entry data. This has some drawbacks:

* If a field identifier is added or removed later, it breaks the export
* The field identifier could be a uuid which makes it hard to read

This change improves this as follows:

* A unique list of each field identifier of each submission is compiled
* The ContentRepository is used to look up the actual field labels if
  the form still exists.
* There is a fallback to the field identifier if the form or field don't
  exist anymore.

Furthermore, the export contains too much information, such as Sections,
Captchas (if configured) and possibly sensible information such as
Passwords.

A new configuration nodeTypesIgnoredInExport is added to prevent the output
of fields configured. The following types are excluded by default:

- Neos.Form.Builder:Section
- Neos.Form.Builder:StaticText
- Neos.Form.Builder:Password
- Neos.Form.Builder:PasswordWithConfirmation
  • Loading branch information
lorenzulrich committed May 18, 2022
1 parent d4c7c6a commit 895cdba
Show file tree
Hide file tree
Showing 4 changed files with 394 additions and 106 deletions.
180 changes: 77 additions & 103 deletions Classes/Controller/DatabaseStorageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
*
* This file is part of the Flow Framework Package "Wegmeister.DatabaseStorage".
*
* PHP version 7
*
* @category Controller
* @package Wegmeister\DatabaseStorage
* @author Benjamin Klix <[email protected]>
Expand All @@ -19,8 +17,6 @@
use Neos\Flow\Annotations as Flow;
use Neos\Flow\I18n\Translator;
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\ResourceManagement\ResourceManager;
use Neos\Flow\ResourceManagement\PersistentResource;

use Wegmeister\DatabaseStorage\Domain\Model\DatabaseStorage;
use Wegmeister\DatabaseStorage\Domain\Repository\DatabaseStorageRepository;
Expand All @@ -29,6 +25,7 @@
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
use Wegmeister\DatabaseStorage\Service\DatabaseStorageService;

/**
* The Database Storage controller
Expand All @@ -45,23 +42,23 @@ class DatabaseStorageController extends ActionController
protected static $types = [
'Xls' => [
'extension' => 'xls',
'mimeType' => 'application/vnd.ms-excel',
'mimeType' => 'application/vnd.ms-excel',
],
'Xlsx' => [
'extension' => 'xlsx',
'mimeType' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'mimeType' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
],
'Ods' => [
'extension' => 'ods',
'mimeType' => 'application/vnd.oasis.opendocument.spreadsheet',
'mimeType' => 'application/vnd.oasis.opendocument.spreadsheet',
],
'Csv' => [
'extension' => 'csv',
'mimeType' => 'text/csv',
'mimeType' => 'text/csv',
],
'Html' => [
'extension' => 'html',
'mimeType' => 'text/html',
'mimeType' => 'text/html',
],
];

Expand All @@ -74,12 +71,9 @@ class DatabaseStorageController extends ActionController
protected $databaseStorageRepository;

/**
* Instance of the resource manager.
*
* @Flow\Inject
* @var ResourceManager
* @var DatabaseStorageService
*/
protected $resourceManager;
protected $databaseStorageService;

/**
* Instance of the translator interface.
Expand All @@ -96,7 +90,6 @@ class DatabaseStorageController extends ActionController
*/
protected $settings;


/**
* Inject the settings
*
Expand All @@ -119,7 +112,6 @@ public function indexAction()
$this->view->assign('identifiers', $this->databaseStorageRepository->findStorageidentifiers());
}


/**
* List entries of a given storage identifier.
*
Expand All @@ -130,30 +122,34 @@ public function indexAction()
*/
public function showAction(string $identifier)
{
$this->databaseStorageService = new DatabaseStorageService($identifier);

$entries = $this->databaseStorageRepository->findByStorageidentifier($identifier);
$titles = [];
if (isset($entries[0])) {
foreach ($entries[0]->getProperties() as $title => $value) {
$titles[] = $title;
}
foreach ($entries as $entry) {
$properties = $entry->getProperties();
$formElementLabels = $this->databaseStorageService->getFormElementLabels(
$entries
);

foreach ($properties as &$value) {
$value = $this->getStringValue($value);
}
if (empty($entries)) {
$this->redirect('index');
}

$entry->setProperties($properties);
/** @var DatabaseStorage $entry */
foreach ($entries as $entry) {
$values = [];
foreach ($formElementLabels as $formElementLabel) {
$values[$formElementLabel] = $this->databaseStorageService->getValueFromEntryProperty(
$entry,
$formElementLabel
);
}
$this->view->assign('identifier', $identifier);
$this->view->assign('titles', $titles);
$this->view->assign('entries', $entries);
$this->view->assign('datetimeFormat', $this->settings['datetimeFormat']);
} else {
$this->redirect('index');
$entry->setProperties($values);
}
}

$this->view->assign('identifier', $identifier);
$this->view->assign('titles', $formElementLabels);
$this->view->assign('entries', $entries);
$this->view->assign('datetimeFormat', $this->settings['datetimeFormat']);
}

/**
* Delete an entry from the list of identifiers.
Expand All @@ -166,16 +162,24 @@ public function deleteAction(DatabaseStorage $entry)
{
$identifier = $entry->getStorageidentifier();
$this->databaseStorageRepository->remove($entry);
$this->addFlashMessage($this->translator->translateById('storage.flashmessage.entryRemoved', [], null, null, 'Main', 'Wegmeister.DatabaseStorage'));
$this->addFlashMessage(
$this->translator->translateById(
'storage.flashmessage.entryRemoved',
[],
null,
null,
'Main',
'Wegmeister.DatabaseStorage'
)
);
$this->redirect('show', null, null, ['identifier' => $identifier]);
}


/**
* Delete all entries for the given identifier.
*
* @param string $identifier The storage identifier for the entries to be removed.
* @param bool $redirect Redirect to index?
* @param bool $redirect Redirect to index?
*
* @return void
*/
Expand All @@ -192,18 +196,26 @@ public function deleteAllAction(string $identifier, bool $redirect = false)

if ($redirect) {
// TODO: Translate flash message.
$this->addFlashMessage($this->translator->translateById('storage.flashmessage.entriesRemoved', [], null, null, 'Main', 'Wegmeister.DatabaseStorage'));
$this->addFlashMessage(
$this->translator->translateById(
'storage.flashmessage.entriesRemoved',
[],
null,
null,
'Main',
'Wegmeister.DatabaseStorage'
)
);
$this->redirect('index');
}
}


/**
* Export all entries for a specific identifier as xls.
*
* @param string $identifier The storage identifier that should be exported.
* @param string $writerType The writer type/export format to be used.
* @param bool $exportDateTime Should the datetime be exported?
* @param string $identifier The storage identifier that should be exported.
* @param string $writerType The writer type/export format to be used.
* @param bool $exportDateTime Should the datetime be exported?
*
* @return void
*/
Expand All @@ -213,46 +225,46 @@ public function exportAction(string $identifier, string $writerType = 'Xlsx', bo
throw new WriterException('No writer available for type ' . $writerType . '.', 1521787983);
}

$entries = $this->databaseStorageRepository->findByStorageidentifier($identifier)->toArray();
$this->databaseStorageService = new DatabaseStorageService($identifier);

$entries = $this->databaseStorageRepository->findByStorageidentifier($identifier);

$dataArray = [];

$spreadsheet = new Spreadsheet();

$spreadsheet->getProperties()
->setCreator($this->settings['creator'])
->setTitle($this->settings['title'])
->setSubject($this->settings['subject']);
$spreadsheet->getProperties()->setCreator($this->settings['creator'])->setTitle(
$this->settings['title']
)->setSubject($this->settings['subject']);

$spreadsheet->setActiveSheetIndex(0);
$spreadsheet->getActiveSheet()->setTitle($this->settings['title']);

$titles = [];
$columns = 0;
foreach ($entries[0]->getProperties() as $title => $value) {
$titles[] = $title;
$columns++;
}
$formElementLabels = $this->databaseStorageService->getFormElementLabels(
$entries
);
$columns = count($formElementLabels);

if ($exportDateTime) {
// TODO: Translate title for datetime
$titles[] = 'DateTime';
$formElementLabels['DateTime'] = 'DateTime';
$columns++;
}

$dataArray[] = $titles;

$dataArray[] = $formElementLabels;

/** @var DatabaseStorage $entry */
foreach ($entries as $entry) {
$values = [];

foreach ($entry->getProperties() as $value) {
$values[] = $this->getStringValue($value);
foreach ($formElementLabels as $formElementLabel) {
$values[$formElementLabel] = $this->databaseStorageService->getValueFromEntryProperty(
$entry,
$formElementLabel
);
}

if ($exportDateTime) {
$values[] = $entry->getDateTime()->format($this->settings['datetimeFormat']);
$values['DateTime'] = $entry->getDateTime()->format($this->settings['datetimeFormat']);
}

$dataArray[] = $values;
}

Expand All @@ -265,17 +277,16 @@ public function exportAction(string $identifier, string $writerType = 'Xlsx', bo
$index = $i % 26;
$columnStyle = $spreadsheet->getActiveSheet()->getStyle($prefixKey . chr(65 + $index) . '1');
$columnStyle->getFont()->setBold(true);
$columnStyle->getAlignment()
->setHorizontal(Alignment::HORIZONTAL_CENTER)
->setVertical(Alignment::VERTICAL_CENTER);
$columnStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER)->setVertical(
Alignment::VERTICAL_CENTER
);

if ($index + 1 > 25) {
$prefixIndex++;
$prefixKey = chr($prefixIndex);
}
}


if (ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
Expand All @@ -300,41 +311,4 @@ public function exportAction(string $identifier, string $writerType = 'Xlsx', bo
exit;
}

/**
* Internal function to replace value with a string for export / listing.
*
* @param mixed $value The database column value.
* @param int $indent The level of indentation (for array values).
*
* @return string
*/
protected function getStringValue($value, int $indent = 0): string
{
if ($value instanceof PersistentResource) {
return $this->resourceManager->getPublicPersistentResourceUri($value) ?: '-';
} elseif (is_string($value)) {
return $value;
} elseif (is_object($value) && method_exists($value, '__toString')) {
return (string)$value;
} elseif (isset($value['dateFormat'], $value['date'])) {
$timezone = null;
if (isset($value['timezone'])) {
$timezone = new \DateTimeZone($value['timezone']);
}
$dateTime = \DateTime::createFromFormat($value['dateFormat'], $value['date'], $timezone);
return $dateTime->format($this->settings['datetimeFormat']);
} elseif (is_array($value)) {
foreach ($value as &$innerValue) {
$innerValue = $this->getStringValue($innerValue, $indent + 1);
}
$prefix = str_repeat(' ', $indent * 2) . '- ';
return sprintf(
'%s%s',
$prefix,
implode("\r\n" . $prefix, $value)
);
}

return '-';
}
}
Loading

0 comments on commit 895cdba

Please sign in to comment.