diff --git a/.gitignore b/.gitignore
index d314607..273db80 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,3 +61,4 @@ tests/_output/*
/vendor
/config
/.ddev
+/.php-cs-fixer.cache
diff --git a/Classes/Controller/XlsimportController.php b/Classes/Controller/XlsimportController.php
index 48a7b5b..c64119a 100644
--- a/Classes/Controller/XlsimportController.php
+++ b/Classes/Controller/XlsimportController.php
@@ -12,24 +12,28 @@
use PhpOffice\PhpSpreadsheet\Reader\Csv;
use PhpOffice\PhpSpreadsheet\Worksheet\RowCellIterator;
use PhpOffice\PhpSpreadsheet\Worksheet\RowIterator;
+use Psr\Http\Message\ResponseInterface;
+use SUDHAUS7\Xlsimport\Property\TypeConverter\UploadedFileConverter;
use SUDHAUS7\Xlsimport\Utility\AccessUtility;
+use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
use TYPO3\CMS\Backend\Utility\BackendUtility;
-use TYPO3\CMS\Backend\View\BackendTemplateView;
use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException;
use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
+use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\DataHandling\DataHandler;
+use TYPO3\CMS\Core\Http\UploadedFile;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
-use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException;
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
+use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Recordlist\Controller\AccessDeniedException;
/**
@@ -37,12 +41,6 @@
*/
class XlsimportController extends ActionController
{
- /**
- * Backend Template Container
- *
- * @var string
- */
- protected $defaultViewObjectName = BackendTemplateView::class;
/**
* @var LanguageService
*/
@@ -53,21 +51,16 @@ class XlsimportController extends ActionController
*/
protected ResourceFactory $resourceFactory;
+ protected ModuleTemplateFactory $moduleTemplateFactory;
+
public function __construct(
ResourceFactory $resourceFactory,
- LanguageService $languageService
+ LanguageService $languageService,
+ ModuleTemplateFactory $moduleTemplateFactory
) {
$this->resourceFactory = $resourceFactory;
$this->languageService = $languageService;
- }
-
- /**
- * XlsimportController constructor.
- */
- public function initializeObject(): void
- {
- GeneralUtility::makeInstance(PageRenderer::class)
- ->loadRequireJsModule('TYPO3/CMS/Xlsimport/Importer');
+ $this->moduleTemplateFactory = $moduleTemplateFactory;
}
/**
@@ -75,8 +68,9 @@ public function initializeObject(): void
* @throws ExtensionConfigurationExtensionNotConfiguredException
* @throws AccessDeniedException
*/
- public function indexAction(): void
+ public function indexAction(): ResponseInterface
{
+ $moduleTemplate = $this->moduleTemplateFactory->create($this->request);
$page = (int)GeneralUtility::_GET('id');
$minimalPage = [
'uid' => $page,
@@ -122,6 +116,21 @@ public function indexAction(): void
'allowedTables' => $allowedTables,
];
$this->view->assignMultiple($assignedValues);
+ $moduleTemplate
+ ->setTitle('S7 XLS Importer')
+ ->setModuleName('Importer')
+ ->setContent($this->view->render());
+ return $this->htmlResponse($moduleTemplate->renderContent());
+ }
+
+ public function initializeUploadAction(): void
+ {
+ if ($this->arguments->hasArgument('file')) {
+ $this->arguments
+ ->getArgument('file')
+ ->getPropertyMappingConfiguration()
+ ->setTypeConverter(new UploadedFileConverter());
+ }
}
/**
@@ -131,9 +140,30 @@ public function indexAction(): void
* @throws DBALException
* @throws AccessDeniedException
*/
- public function uploadAction(): void
- {
- $page = GeneralUtility::_GET('id');
+ public function uploadAction(
+ string $table,
+ UploadedFile $file = null,
+ bool $deleteRecords = false,
+ bool $encoding = false,
+ bool $retry = false,
+ string $jsonData = ''
+ ): ResponseInterface {
+ // we didn't receive a retry and the file upload failed. Redirect to index
+ if ($file === null && !$retry) {
+ $message = GeneralUtility::makeInstance(
+ FlashMessage::class,
+ $this->languageService->sL('LLL:EXT:xlsimport/Resources/Private/Language/locallang.xlf:error.file.uploadFailed.message'),
+ $this->languageService->sL('LLL:EXT:xlsimport/Resources/Private/Language/locallang.xlf:error.file.uploadFailed.header'),
+ FlashMessage::ERROR,
+ true
+ );
+ $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
+ $messageQueue = $flashMessageService->getMessageQueueByIdentifier();
+ $messageQueue->enqueue($message);
+ $this->redirect('index');
+ }
+ $moduleTemplate = $this->moduleTemplateFactory->create($this->request);
+ $page = (int)GeneralUtility::_GET('id');
$tempPage = [
'uid' => $page,
];
@@ -144,10 +174,7 @@ public function uploadAction(): void
);
}
- $deleteOldRecords = (bool)$this->request->getArgument('deleteRecords');
- $file = $this->request->getArgument('file');
- $table = $this->request->getArgument('table');
- if ($deleteOldRecords && AccessUtility::isAllowedTable($table, $page)) {
+ if ($deleteRecords && AccessUtility::isAllowedTable($table, $page)) {
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$db = GeneralUtility::makeInstance(ConnectionPool::class);
@@ -165,10 +192,6 @@ public function uploadAction(): void
}
}
- $uploadedFile = GeneralUtility::tempnam('xlsimport');
- GeneralUtility::upload_copy_move($file['tmp_name'], $uploadedFile);
-
- $list = $this->getList($uploadedFile);
$uidConfig = [
'uid' => [
'label' => 'uid',
@@ -222,13 +245,35 @@ public function uploadAction(): void
}
unset($column);
+ $fields = [];
+
+ foreach ($tca as $field => $config) {
+ $fields[] = [
+ 'type' => $field,
+ 'label' => $config['label'],
+ ];
+ }
+
+ if (!$retry) {
+ $uploadedFile = GeneralUtility::tempnam('xlsimport');
+ $file->moveTo($uploadedFile);
+ $list = $this->prepareFileForImport($uploadedFile, $encoding);
+ GeneralUtility::unlink_tempfile($uploadedFile);
+ $jsonData = json_encode($list);
+ $fileName = GeneralUtility::tempnam('xlsimport', '.json');
+ GeneralUtility::writeFile($fileName, $jsonData);
+ } else {
+ $list = $this->loadDataFromJsonFile($jsonData);
+ }
+
$assignedValues = [
- 'fields' => $tca,
- 'data' => $list['data'],
+ 'fields' => $fields,
+ 'data' => $list,
+ 'jsonData' => basename($fileName ?? $jsonData),
'page' => $page,
'table' => $table,
'hasPasswordField' => $hasPasswordField,
- 'passwordFields' => implode(',', $passwordFields),
+ 'passwordFields' => $passwordFields,
'addInlineSettings' => [
'FormEngine' => [
'formName' => 'importData',
@@ -236,6 +281,10 @@ public function uploadAction(): void
],
];
$this->view->assignMultiple($assignedValues);
+ $moduleTemplate
+ ->setTitle('S7 XLS Import', LocalizationUtility::translate('prepare', 'xlsimport'))
+ ->setContent($this->view->render());
+ return $this->htmlResponse($moduleTemplate->renderContent());
}
/**
@@ -244,38 +293,34 @@ public function uploadAction(): void
* @throws JsonException
* @throws \TYPO3\CMS\Core\Exception
*/
- public function importAction(): void
- {
+ public function importAction(
+ string $table,
+ string $jsonData,
+ array $fields,
+ array $dataset,
+ bool $passwordOverride = false,
+ array $passwordFields = []
+ ): void {
$page = GeneralUtility::_GET('id');
- $table = $this->request->getArgument('table');
if (!$table || !array_key_exists($table, $GLOBALS['TCA'])) {
$this->redirect('index');
exit;
}
- /** @var array $fields */
- $fields = $this->request->getArgument('fields');
$overrides = [];
if ($this->request->hasArgument('overrides')) {
$overrides = $this->request->getArgument('overrides');
}
- $passwordOverride = (bool)$this->request->getArgument('passwordOverride');
- $passwordFields = GeneralUtility::trimExplode(',', $this->request->getArgument('passwordFields'));
+ // load saved data from JSON
+ $data = $this->loadDataFromJsonFile($jsonData);
- /** @var array $imports */
- $imports = json_decode($this->request->getArgument('dataset'), true, 512, JSON_THROW_ON_ERROR);
- $a = [];
- foreach ($imports as $import) {
- $s = sprintf('%s=%s', $import['name'], urlencode($import['value']));
- $temp = [];
- parse_str($s, $temp);
- foreach ($temp['tx_xlsimport_web_xlsimporttxxlsimport']['dataset'] as $k => $v) {
- foreach ($v as $key => $value) {
- $a[$k][$key] = $value;
- }
+ // remove all data, which should not be imported
+ foreach ($data as $index => $value) {
+ if (!array_key_exists($index, $dataset) || $dataset[$index] != 1) {
+ unset($data[$index]);
}
}
- $imports = $a;
+ $imports = array_values($data);
// unset all fields not assigned to a TCA field
foreach ($fields as $key => $field) {
@@ -286,6 +331,31 @@ public function importAction(): void
unset($fields[$key]);
}
}
+
+ // if fieldlist is empty from now, return data to importer and create flashMessage
+ if (empty($fields)) {
+ $message = GeneralUtility::makeInstance(
+ FlashMessage::class,
+ $this->languageService->sL('LLL:EXT:xlsimport/Resources/Private/Language/locallang.xlf:warning.fieldlist.empty.message'),
+ $this->languageService->sL('LLL:EXT:xlsimport/Resources/Private/Language/locallang.xlf:warning.fieldlist.empty.header'),
+ FlashMessage::WARNING,
+ true
+ );
+ $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
+ $messageQueue = $flashMessageService->getMessageQueueByIdentifier();
+ $messageQueue->enqueue($message);
+ $this->redirect(
+ 'upload',
+ null,
+ null,
+ [
+ 'retry' => true,
+ 'jsonData' => $jsonData,
+ 'table' => $table,
+ ]
+ );
+ }
+
// get override field and take a look inside fieldlist, if defined
foreach ($overrides as $key => $override) {
if (empty($override) | in_array($override, $fields, true)) {
@@ -307,31 +377,29 @@ public function importAction(): void
$table => [],
];
foreach ($imports as $import) {
- if ($import['import']) {
- $insertArray = [];
- $update = false;
- foreach ($fields as $key => $field) {
- if ($field === 'uid') {
- if (!empty($import[$key])) {
- $update = true;
- }
- } else {
- $insertArray[$field] = $import[$key];
- }
- if (!isset($insertArray['pid'])) {
- $insertArray['pid'] = $page;
+ $insertArray = [];
+ $update = false;
+ foreach ($fields as $key => $field) {
+ if ($field === 'uid') {
+ if (!empty($import[$key])) {
+ $update = true;
}
+ } else {
+ $insertArray[$field] = $import[$key];
}
- foreach ($overrides as $key => $override) {
- $insertArray[$key] = $override;
- }
-
- foreach ($passwordFields as $passwordField) {
- $insertArray[$passwordField] = md5(sha1(microtime()));
- }
+ }
+ if (!isset($insertArray['pid'])) {
+ $insertArray['pid'] = $page;
+ }
+ foreach ($overrides as $key => $override) {
+ $insertArray[$key] = $override;
+ }
- $inserts[$table][$update ? $import['uid'] : uniqid('NEW_', true)] = $insertArray;
+ foreach ($passwordFields as $passwordField) {
+ $insertArray[$passwordField] = md5(sha1(microtime()));
}
+
+ $inserts[$table][$update ? $import['uid'] : uniqid('NEW_', true)] = $insertArray;
}
if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['Hooks'])) {
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['Hooks'] as $_classRef) {
@@ -345,7 +413,7 @@ public function importAction(): void
$tce = GeneralUtility::makeInstance(DataHandler::class);
$tce->start($inserts, []);
$tce->process_datamap();
- /** @var FlashMessage $message */
+
$message = GeneralUtility::makeInstance(
FlashMessage::class,
$this->languageService->sL('LLL:EXT:xlsimport/Resources/Private/Language/locallang.xlf:success'),
@@ -353,33 +421,29 @@ public function importAction(): void
FlashMessage::OK,
true
);
- /** @var FlashMessageService $flashMessageService */
+
$flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
$messageQueue = $flashMessageService->getMessageQueueByIdentifier();
$messageQueue->enqueue($message);
+ GeneralUtility::unlink_tempfile($this->buildJsonFileName($jsonData));
$this->redirect('index');
}
/**
- * @param string $fileName
* @return array
* @throws Exception
- * @throws NoSuchArgumentException
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
*/
- protected function getList(string $fileName): array
- {
+ protected function prepareFileForImport(
+ string $fileName,
+ bool $encoding = false
+ ): array {
$aList = [];
- $aList['rows'] = 0;
- $aList['cols'] = 0;
- $aList['data'] = [];
if (is_file($fileName)) {
$inputFileType = IOFactory::identify($fileName);
if ($inputFileType === 'Csv') {
$oReader = new Csv();
- $encoding = (bool)$this->request->getArgument('encoding');
-
if ($encoding === true) {
$oReader->setInputEncoding('CP1252');
}
@@ -388,7 +452,6 @@ protected function getList(string $fileName): array
}
if ($oReader->canRead($fileName)) {
- //$oReader->getReadDataOnly();
$xls = $oReader->load($fileName);
$xls->setActiveSheetIndex(0);
$sheet = $xls->getActiveSheet();
@@ -396,7 +459,7 @@ protected function getList(string $fileName): array
$rowcount = 1;
$colcount = 1;
- foreach ($rowI as $k => $row) {
+ foreach ($rowI as $row) {
$rowcount++;
$cell = new RowCellIterator($sheet, 1, 'A');
@@ -411,12 +474,9 @@ protected function getList(string $fileName): array
}
}
- $aList['rows'] = $rowcount;
- $aList['cols'] = $colcount;
-
for ($y = 1; $y < $rowcount; $y++) {
for ($x = 1; $x <= $colcount; $x++) {
- $aList['data'][$y][$x] = $sheet->getCellByColumnAndRow($x, $y)->getValue();
+ $aList[$y][$x] = $sheet->getCellByColumnAndRow($x, $y)->getValue();
}
}
@@ -443,4 +503,30 @@ private function checkTableAndAccessAllowed(array $possibleTables, int $pid): ar
return $allowedTables;
}
+
+ private function loadDataFromJsonFile(string $jsonFileName): ?array
+ {
+ $jsonFile = $this->buildJsonFileName($jsonFileName);
+ if (is_file($jsonFile)) {
+ return json_decode(file_get_contents($jsonFile), true);
+ }
+ $message = GeneralUtility::makeInstance(
+ FlashMessage::class,
+ $this->languageService->sL('LLL:EXT:xlsimport/Resources/Private/Language/locallang.xlf:error.jsonFile.message'),
+ $this->languageService->sL('LLL:EXT:xlsimport/Resources/Private/Language/locallang.xlf:error.jsonFile.header'),
+ FlashMessage::WARNING,
+ true
+ );
+ $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
+ $messageQueue = $flashMessageService->getMessageQueueByIdentifier();
+ $messageQueue->enqueue($message);
+ $this->redirect('index');
+ return null;
+ }
+
+ private function buildJsonFileName(string $jsonFileName): string
+ {
+ $temporaryPath = Environment::getVarPath() . '/transient/';
+ return sprintf('%s%s', $temporaryPath, $jsonFileName);
+ }
}
diff --git a/Classes/Property/TypeConverter/UploadedFileConverter.php b/Classes/Property/TypeConverter/UploadedFileConverter.php
new file mode 100644
index 0000000..507311d
--- /dev/null
+++ b/Classes/Property/TypeConverter/UploadedFileConverter.php
@@ -0,0 +1,35 @@
+
+ */
+ protected $sourceTypes = ['array'];
+
+ protected $targetType = UploadedFile::class;
+
+ protected $priority = 30;
+
+ /**
+ * @inheritDoc
+ */
+ public function convertFrom(
+ $source,
+ string $targetType,
+ array $convertedChildProperties = [],
+ PropertyMappingConfigurationInterface $configuration = null
+ ) {
+ $uploadedFile = new UploadedFile($source['tmp_name'], $source['size'], $source['error'], $source['name'], $source['type']);
+
+ return $uploadedFile->getError() === UPLOAD_ERR_OK ? $uploadedFile : null;
+ }
+}
diff --git a/Classes/Utility/AccessUtility.php b/Classes/Utility/AccessUtility.php
index 912cfbf..ced9764 100644
--- a/Classes/Utility/AccessUtility.php
+++ b/Classes/Utility/AccessUtility.php
@@ -23,6 +23,13 @@ public static function isAllowedTable(string $possibleTable, int $pageId): bool
public static function isAllowedField(string $table, string $fieldName): bool
{
$allowedFields = BackendUtility::getAllowedFieldsForTable($table);
+ // Disallow PID, as the Backend module selects by choosing the site
+ // in page tree. Allowing import with PID set will cause side effects.
+ // Admins are allowed to do.
+ $pidKey = array_search('pid', $allowedFields);
+ if ($pidKey && !self::getBackendUser()->isAdmin()) {
+ unset($allowedFields[$pidKey]);
+ }
return in_array($fieldName, $allowedFields);
}
private static function checkTableIsAllowedOnPage(string $tableName, int $pageId): bool
diff --git a/Documentation/Configuration/Index.rst b/Documentation/Configuration/Index.rst
index d01b622..3c89d2f 100644
--- a/Documentation/Configuration/Index.rst
+++ b/Documentation/Configuration/Index.rst
@@ -38,4 +38,5 @@ extension configuration file
set" from the "Static Template Files from TYPO3 Extensions" dropdown
list in your TypoScript template.
-Every TCA defined table could be used. Field names are taken by locallang files, so localization is done.
+Every TCA defined table could be used. Field names are taken by locallang
+files, so localization is done.
diff --git a/Documentation/Images/DynamicSelect.png b/Documentation/Images/DynamicSelect.png
index adb6477..d690cdc 100644
Binary files a/Documentation/Images/DynamicSelect.png and b/Documentation/Images/DynamicSelect.png differ
diff --git a/Documentation/Images/ImportView.png b/Documentation/Images/ImportView.png
index d019624..b55a07b 100644
Binary files a/Documentation/Images/ImportView.png and b/Documentation/Images/ImportView.png differ
diff --git a/Documentation/Images/SelectFile.png b/Documentation/Images/SelectFile.png
index e526f53..cffd0e9 100644
Binary files a/Documentation/Images/SelectFile.png and b/Documentation/Images/SelectFile.png differ
diff --git a/Documentation/Images/SelectedValues.png b/Documentation/Images/SelectedValues.png
index 15c439a..bd53bb7 100644
Binary files a/Documentation/Images/SelectedValues.png and b/Documentation/Images/SelectedValues.png differ
diff --git a/Documentation/Images/StandardView.png b/Documentation/Images/StandardView.png
index d2e373c..f0c5c25 100644
Binary files a/Documentation/Images/StandardView.png and b/Documentation/Images/StandardView.png differ
diff --git a/Documentation/Introduction/Index.rst b/Documentation/Introduction/Index.rst
index d0473e4..1aad053 100644
--- a/Documentation/Introduction/Index.rst
+++ b/Documentation/Introduction/Index.rst
@@ -18,7 +18,8 @@ Introduction
What does it do?
^^^^^^^^^^^^^^^^
-This extension allows the import of data to every database table inside TYPO3 defined in TCA configuration.
+This extension allows the import of data to every database table inside TYPO3
+defined in TCA configuration.
.. _screenshots:
diff --git a/Documentation/Settings.cfg b/Documentation/Settings.cfg
index 0cdca78..feee389 100644
--- a/Documentation/Settings.cfg
+++ b/Documentation/Settings.cfg
@@ -1,8 +1,8 @@
[general]
project = (Sudhaus7) Xls Import
-version = 1.0
-release = 1.0
+version = 4.0
+release = 4.0
t3author = Markus Hofmann, Frank Berger, Daniel Simon
copyright = 2020
diff --git a/Documentation/Usage/Index.rst b/Documentation/Usage/Index.rst
index 96c4d0d..1211817 100644
--- a/Documentation/Usage/Index.rst
+++ b/Documentation/Usage/Index.rst
@@ -17,46 +17,56 @@ Usage
The default view of the module.
-Beginning from here you have to select a page inside the pagetree you want to import your data to.
+Beginning from here you can select a page inside the pagetree you want to import your data to.
.. figure:: ../Images/SelectFile.png
:alt: Select table and file view
The view for selecting table and file for importing data.
-Inside this view you can select your table you want to import to. As actual supported files you can use all listed
-above file types.
+Inside this view you can select your table you want to import to. As actual
+supported files you can use all listed above file types.
-The first checkbox allows you to select if the uploaded file is CP1252 encoded. This is especially useful if you encounter problems
-with empty fields after the import due to UTF-8 problems.
+The first checkbox allows you to select if the uploaded file is CP1252 encoded.
+This is especially useful if you encounter problems with empty fields after
+the import due to UTF-8 problems.
-The second checkbox allows you to delete all records in the selected table on the selected page.
+The second checkbox allows you to delete all records in the selected table on
+the selected page.
-For getting data updated instead of added, you have to define a row with the uid of the corresponding TYPO3 record inside
-your Excel sheet. If you set one column to uid, the system tries to update the existing dataset.
+For getting data updated instead of added, you have to define a row with the
+uid of the corresponding TYPO3 record inside your Excel sheet. If you set
+one column to uid, the system tries to update the existing dataset.
Use the update feature carefully.
+The PID insert can only used by TYPO3 Backend Administrators, as the import
+with pid will cause side effects.
+
.. figure:: ../Images/ImportView.png
:alt: View for the pre imported data
The view of the pre imported data with empty selects
-You will get a preview of your data. Now you have to decide, to witch column your data has to be written.
-With the checkbox in the front you can exclude data from getting imported, this is useful for
-header lines and empty lines to avoid problems with the TCA import.
+You will get a preview of your data. Now you have to decide, to witch column
+your data has to be written. With the checkbox in the front you can exclude
+data from getting imported, this is useful for header lines and empty lines
+to avoid problems with the TCA import.
.. figure:: ../Images/DynamicSelect.png
:alt: View for imported data with opened select
An opened select for selecting the data column
-Select for every column to witch column inside the database this column has to be added. If you choose ignore, the
-column is ignored. Selecting uid will update the dataset with the uid inside this column instead of creating a new one.
-Updating a dataset won't take effect on the pid, the data is stored. If no uid is set, a new dataset is created
-at the current page. This allows to mix up updating records and creating new ones with only one import.
+Select for every column to witch column inside the database this column has
+to be added. If you choose ignore, the column is ignored. Selecting uid will
+update the dataset with the uid inside this column instead of creating a new
+one. Updating a dataset won't take effect on the pid, the data is stored. If
+no uid is set, a new dataset is created at the current page. This allows to
+mix up updating records and creating new ones with only one import.
-After a successful import you will get a success message and your data is imported.
+After a successful import you will get a success message and your data is
+imported.
.. figure:: ../Images/ImportedData.png
:alt: Table view from tt_address with imported data
diff --git a/Resources/Private/Language/de.locallang.xlf b/Resources/Private/Language/de.locallang.xlf
index 819d946..ebec94c 100644
--- a/Resources/Private/Language/de.locallang.xlf
+++ b/Resources/Private/Language/de.locallang.xlf
@@ -109,6 +109,31 @@
Erlaubte Tabellen
+
+
+
+ Leere Feldliste
+
+
+
+ Sie haben keine Felder zugeordnet. Bitte korrigieren Sie das in den Auswahlfeldern und senden das Formular erneut ab.
+
+
+
+ Datei hochladen fehlgeschlagen
+
+
+
+ Beim Hochladen ist etwas schiefgelaufen. Bitte versuchen Sie es erneut.
+
+
+
+ Temporäre Datei nicht gefunden
+
+
+
+ Die temporär gespeicherte Datei konnte nicht geladen werden. Bitte versuchen Sie einen erneuten Upload.
+