';
+ return;
+ }
+
+ // Check if the request has been sent through the form
+ check_admin_referer($this->currentSource->getNonce($action));
+
+ // Set variables for further flow control
+ $currentAction = $this->currentSource->getAction($action);
+ $nextAction = $this->currentSource->getNextAction($currentAction);
+
echo '
', esc_html($source->getDescription()));
- if (false !== $firstAction) {
+ if (false !== $firstStep) {
echo '';
}
echo '
';
@@ -79,12 +73,22 @@ protected function echoPageContent()
return;
}
- // Check if the request has been sent through the form
- check_admin_referer($this->currentSource->getNonce($action));
+ list($identifier, $action) = explode(':', $postAction);
+ if (!array_key_exists($identifier, $this->sources)) {
+ wp_die('Invalid source');
+ }
+ $this->currentSource = $this->sources[$identifier];
// Set variables for further flow control
- $currentAction = $this->currentSource->getAction($action);
- $nextAction = $this->currentSource->getNextAction($currentAction);
+ $currentStep = $this->currentSource->getStep($action);
+ if ($currentStep === false) {
+ wp_die('Invalid step');
+ }
+
+ // Check if the request has been sent through the form
+ check_admin_referer($this->currentSource->getNonce($currentStep));
+
+ $nextStep = $this->currentSource->getNextStep($currentStep);
echo '
Content
';
}
diff --git a/src/Import/Sources/AbstractSource.php b/src/Import/Sources/AbstractSource.php
index 30d2a6fd..0e7609d5 100644
--- a/src/Import/Sources/AbstractSource.php
+++ b/src/Import/Sources/AbstractSource.php
@@ -3,6 +3,8 @@
use abrain\Einsatzverwaltung\Exceptions\ImportCheckException;
use abrain\Einsatzverwaltung\Exceptions\ImportException;
+use abrain\Einsatzverwaltung\Import\Step;
+use function esc_attr;
use function sprintf;
/**
@@ -10,11 +12,9 @@
*/
abstract class AbstractSource
{
- protected $actionOrder = array();
protected $args = array();
protected $autoMatchFields = array();
protected $cachedFields;
-
/**
* @var string
*/
@@ -34,6 +34,11 @@ abstract class AbstractSource
protected $problematicFields = array();
+ /**
+ * @var Step[]
+ */
+ protected $steps = array();
+
/**
* AbstractSource constructor.
*
@@ -48,14 +53,16 @@ abstract public function __construct();
abstract public function checkPreconditions();
/**
+ * TODO: The source shouldn't echo anything, but return its requirements in a standardized way
+ *
* Generiert für Argumente, die in der nächsten Action wieder gebraucht werden, Felder, die in das Formular
* eingebaut werden können, damit diese mitgenommen werden
*
- * @param array $nextAction Die nächste Action
+ * @param Step $nextStep
*/
- public function echoExtraFormFields(array $nextAction)
+ public function echoExtraFormFields(Step $nextStep)
{
- if (empty($nextAction)) {
+ if (empty($nextStep)) {
return;
}
@@ -65,9 +72,9 @@ public function echoExtraFormFields(array $nextAction)
echo ' /> Einsatzberichte sofort veröffentlichen';
echo '
Das Setzen dieser Option verlängert die Importzeit deutlich, Benutzung auf eigene Gefahr. Standardmäßig werden die Berichte als Entwurf importiert.
';
- foreach ($nextAction['args'] as $arg) {
+ foreach ($nextStep->getArguments() as $arg) {
if (array_key_exists($arg, $this->args)) {
- echo '';
+ printf('', esc_attr($arg), esc_attr($this->args[$arg]));
}
}
}
@@ -83,30 +90,31 @@ public function getDescription()
}
/**
- * @param string $action
+ * @param Step $step
+ *
* @return string
*/
- public function getActionAttribute(string $action)
+ public function getActionAttribute(Step $step)
{
- return $this->getIdentifier() . ':' . $action;
+ return sprintf("%s:%s", $this->getIdentifier(), $step->getSlug());
}
/**
- * Gibt das Action-Array für $slug zurück
+ * Gets a Step object based on its slug.
*
- * @param string $slug Slug der Action
+ * @param string $slug Slug of the step
*
- * @return array|bool Das Array der Action oder false, wenn es keines für $slug gibt
+ * @return Step|false The Step object or false if there is no step for this slug
*/
- public function getAction(string $slug)
+ public function getStep(string $slug)
{
if (empty($slug)) {
return false;
}
- foreach ($this->actionOrder as $action) {
- if ($action['slug'] == $slug) {
- return $action;
+ foreach ($this->steps as $step) {
+ if ($step->getSlug() == $slug) {
+ return $step;
}
}
@@ -143,17 +151,17 @@ abstract public function getEntries($fields);
abstract public function getFields();
/**
- * Gibt die erste Action der Importquelle zurück
+ * Returns the first step of the import source.
*
- * @return array|bool Ein Array, das die erste Action beschreibt, oder false, wenn es keine Action gibt
+ * @return Step|false The Step object representing the first step or false if there are no steps defined.
*/
- public function getFirstAction()
+ public function getFirstStep()
{
- if (empty($this->actionOrder)) {
+ if (empty($this->steps)) {
return false;
}
- return $this->actionOrder[0];
+ return $this->steps[0];
}
/**
@@ -221,33 +229,39 @@ public function getName()
/**
* Gibt die nächste Action der Importquelle zurück
*
- * @param array $currentAction Array, das die aktuelle Action beschreibt
+ * @param Step $currentStep Array, das die aktuelle Action beschreibt
*
- * @return array|bool Ein Array, das die nächste Action beschreibt, oder false, wenn es keine weitere gibt
+ * @return Step|false Ein Array, das die nächste Action beschreibt, oder false, wenn es keine weitere gibt
*/
- public function getNextAction(array $currentAction)
+ public function getNextStep(Step $currentStep)
{
- if (empty($this->actionOrder)) {
+ if (empty($this->steps)) {
return false;
}
- $key = array_search($currentAction, $this->actionOrder);
+ $key = array_search($currentStep, $this->steps);
+
+ // Make sure the given step was found in the list of steps
+ if ($key === false) {
+ return false;
+ }
- if ($key + 1 >= count($this->actionOrder)) {
+ // Return false if this was the last step
+ if ($key + 1 >= count($this->steps)) {
return false;
}
- return $this->actionOrder[$key + 1];
+ return $this->steps[$key + 1];
}
/**
- * @param string $action
+ * @param Step $step
*
* @return string
*/
- public function getNonce(string $action)
+ public function getNonce(Step $step)
{
- return sprintf("%s_%s", $this->getIdentifier(), $action);
+ return sprintf("%s_%s", $this->getIdentifier(), $step->getSlug());
}
/**
diff --git a/src/Import/Sources/Csv.php b/src/Import/Sources/Csv.php
index 1ba51c2f..1b350b7d 100644
--- a/src/Import/Sources/Csv.php
+++ b/src/Import/Sources/Csv.php
@@ -2,6 +2,7 @@
namespace abrain\Einsatzverwaltung\Import\Sources;
use abrain\Einsatzverwaltung\Exceptions\ImportCheckException;
+use abrain\Einsatzverwaltung\Import\Step;
use abrain\Einsatzverwaltung\Utilities;
use Exception;
@@ -27,26 +28,9 @@ public function __construct()
$this->identifier = 'evw_csv';
$this->name = 'CSV';
- $this->actionOrder = array(
- array(
- 'slug' => 'selectcsvfile',
- 'name' => 'Dateiauswahl',
- 'button_text' => 'Datei auswählen',
- 'args' => array()
- ),
- array(
- 'slug' => 'analysis',
- 'name' => 'Analyse',
- 'button_text' => 'Datei analysieren',
- 'args' => array('csv_file_id', 'has_headlines', 'delimiter')
- ),
- array(
- 'slug' => 'import',
- 'name' => 'Import',
- 'button_text' => 'Import starten',
- 'args' => array('csv_file_id', 'has_headlines', 'delimiter')
- )
- );
+ $this->steps[] = new Step('selectcsvfile', 'Dateiauswahl', 'Datei auswählen');
+ $this->steps[] = new Step('analysis', 'Analyse', 'Datei analysieren', ['csv_file_id', 'has_headlines', 'delimiter']);
+ $this->steps[] = new Step('import', 'Import', 'Import starten', ['csv_file_id', 'has_headlines', 'delimiter']);
}
/**
diff --git a/src/Import/Sources/WpEinsatz.php b/src/Import/Sources/WpEinsatz.php
index f02089eb..e55f9a26 100644
--- a/src/Import/Sources/WpEinsatz.php
+++ b/src/Import/Sources/WpEinsatz.php
@@ -3,6 +3,7 @@
use abrain\Einsatzverwaltung\Exceptions\ImportCheckException;
use abrain\Einsatzverwaltung\Exceptions\ImportException;
+use abrain\Einsatzverwaltung\Import\Step;
use function __;
use function esc_html;
use function join;
@@ -32,20 +33,8 @@ public function __construct()
'Datum' => 'post_date'
);
- $this->actionOrder = array(
- array(
- 'slug' => 'analysis',
- 'name' => 'Analyse',
- 'button_text' => 'Datenbank analysieren',
- 'args' => array()
- ),
- array(
- 'slug' => 'import',
- 'name' => 'Import',
- 'button_text' => 'Import starten',
- 'args' => array()
- )
- );
+ $this->steps[] = new Step('analysis', 'Analyse', 'Datenbank analysieren');
+ $this->steps[] = new Step('import', 'Import', 'Import starten');
}
/**
diff --git a/src/Import/Step.php b/src/Import/Step.php
new file mode 100644
index 00000000..f224c76e
--- /dev/null
+++ b/src/Import/Step.php
@@ -0,0 +1,77 @@
+slug = $slug;
+ $this->title = $title;
+ $this->buttonText = $buttonText;
+ $this->arguments = $arguments;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getArguments(): array
+ {
+ return $this->arguments;
+ }
+
+ /**
+ * @return string
+ */
+ public function getButtonText(): string
+ {
+ return $this->buttonText;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSlug(): string
+ {
+ return $this->slug;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTitle(): string
+ {
+ return $this->title;
+ }
+}
diff --git a/tests/unit/Import/Sources/CsvTest.php b/tests/unit/Import/Sources/CsvTest.php
index ac00afdc..ad2d963e 100644
--- a/tests/unit/Import/Sources/CsvTest.php
+++ b/tests/unit/Import/Sources/CsvTest.php
@@ -8,6 +8,7 @@
* @covers \abrain\Einsatzverwaltung\Import\Sources\AbstractSource
* @covers \abrain\Einsatzverwaltung\Import\Sources\Csv
* @package abrain\Einsatzverwaltung\Import\Sources
+ * @uses \abrain\Einsatzverwaltung\Import\Step
*/
class CsvTest extends TestCase
{
diff --git a/tests/unit/Import/Sources/WpEinsatzTest.php b/tests/unit/Import/Sources/WpEinsatzTest.php
index a3a69930..469138bb 100644
--- a/tests/unit/Import/Sources/WpEinsatzTest.php
+++ b/tests/unit/Import/Sources/WpEinsatzTest.php
@@ -12,6 +12,7 @@
* @covers \abrain\Einsatzverwaltung\Import\Sources\AbstractSource
* @covers \abrain\Einsatzverwaltung\Import\Sources\WpEinsatz
* @package abrain\Einsatzverwaltung\Import\Sources
+ * @uses \abrain\Einsatzverwaltung\Import\Step
*/
class WpEinsatzTest extends UnitTestCase
{
From 5b33e429534523217a96757f05cd5df61392ad22 Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Wed, 25 Nov 2020 16:01:35 +0100
Subject: [PATCH 12/25] Carry over settings from step to step
---
src/Import/Helper.php | 4 ++++
src/Import/Page.php | 27 +++++++++++++++++++++++++++
src/Import/Tool.php | 29 -----------------------------
3 files changed, 31 insertions(+), 29 deletions(-)
diff --git a/src/Import/Helper.php b/src/Import/Helper.php
index 6afa9619..63debc9c 100644
--- a/src/Import/Helper.php
+++ b/src/Import/Helper.php
@@ -49,6 +49,10 @@ public function __construct(Utilities $utilities, Data $data)
{
$this->utilities = $utilities;
$this->data = $data;
+
+ $this->metaFields = IncidentReport::getMetaFields();
+ $this->taxonomies = IncidentReport::getTerms();
+ $this->postFields = IncidentReport::getPostFields();
}
/**
diff --git a/src/Import/Page.php b/src/Import/Page.php
index a97c6f1b..ac60de29 100644
--- a/src/Import/Page.php
+++ b/src/Import/Page.php
@@ -5,6 +5,7 @@
use abrain\Einsatzverwaltung\Import\Sources\AbstractSource;
use abrain\Einsatzverwaltung\Import\Sources\Csv;
use abrain\Einsatzverwaltung\Import\Sources\WpEinsatz;
+use abrain\Einsatzverwaltung\Utilities;
use function array_key_exists;
use function check_admin_referer;
use function esc_attr;
@@ -12,6 +13,7 @@
use function esc_html__;
use function explode;
use function filter_input;
+use function sanitize_text_field;
use function submit_button;
use function wp_die;
use function wp_nonce_field;
@@ -90,6 +92,31 @@ protected function echoPageContent()
$nextStep = $this->currentSource->getNextStep($currentStep);
+ // Read the settings that have been passed from the previous step
+ foreach ($currentStep->getArguments() as $argument) {
+ $value = filter_input(INPUT_POST, $argument, FILTER_SANITIZE_STRING);
+ $this->currentSource->putArg($argument, $value);
+ }
+
+ // Pass settings for date and time to the CSV source
+ // TODO move custom logic into the class of the source
+ if ('evw_csv' == $this->currentSource->getIdentifier()) {
+ if (array_key_exists('import_date_format', $_POST)) {
+ $this->currentSource->putArg('import_date_format', sanitize_text_field($_POST['import_date_format']));
+ }
+
+ if (array_key_exists('import_time_format', $_POST)) {
+ $this->currentSource->putArg('import_time_format', sanitize_text_field($_POST['import_time_format']));
+ }
+ }
+
+ // Carry over the setting whether to publish imported reports immediately
+ $publishReports = filter_input(INPUT_POST, 'import_publish_reports', FILTER_SANITIZE_STRING);
+ $this->currentSource->putArg(
+ 'import_publish_reports',
+ Utilities::sanitizeCheckbox($publishReports)
+ );
+
echo '
Content
';
}
diff --git a/src/Import/Tool.php b/src/Import/Tool.php
index 9996232c..bde78b3d 100644
--- a/src/Import/Tool.php
+++ b/src/Import/Tool.php
@@ -36,35 +36,6 @@ class Tool
public function renderToolPage()
{
$this->helper = new Helper($this->utilities, $this->data);
- $this->helper->metaFields = IncidentReport::getMetaFields();
- $this->helper->taxonomies = IncidentReport::getTerms();
- $this->helper->postFields = IncidentReport::getPostFields();
-
- // Einstellungen an die Importquelle übergeben
- if (array_key_exists('args', $this->currentAction) && is_array($this->currentAction['args'])) {
- foreach ($this->currentAction['args'] as $arg) {
- $value = (array_key_exists($arg, $_POST) ? sanitize_text_field($_POST[$arg]) : null);
- $this->currentSource->putArg($arg, $value);
- }
- }
-
- // Datums- und Zeitformat für CSV-Import übernehmen
- if ('evw_csv' == $this->currentSource->getIdentifier()) {
- if (array_key_exists('import_date_format', $_POST)) {
- $this->currentSource->putArg('import_date_format', sanitize_text_field($_POST['import_date_format']));
- }
-
- if (array_key_exists('import_time_format', $_POST)) {
- $this->currentSource->putArg('import_time_format', sanitize_text_field($_POST['import_time_format']));
- }
- }
-
- // 'Sofort veröffentlichen'-Option übernehmen
- $publishReports = filter_input(INPUT_POST, 'import_publish_reports', FILTER_SANITIZE_STRING);
- $this->currentSource->putArg(
- 'import_publish_reports',
- Utilities::sanitizeCheckbox($publishReports)
- );
echo "
{$this->currentAction['name']}
";
From 536c44ede8212f59b6dbc381be6778867f972a4c Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Mon, 30 Nov 2020 15:58:16 +0100
Subject: [PATCH 13/25] Added switch for the actions and adjusted the file
chooser
---
src/Import/Page.php | 73 ++++++++++++++++++++++++++-
src/Import/Sources/AbstractSource.php | 9 +++-
src/Import/Sources/Csv.php | 59 ++++++++++++++--------
src/Import/Sources/FileSource.php | 23 +++++++++
src/Import/Sources/WpEinsatz.php | 4 +-
src/Import/Tool.php | 69 -------------------------
6 files changed, 141 insertions(+), 96 deletions(-)
create mode 100644 src/Import/Sources/FileSource.php
diff --git a/src/Import/Page.php b/src/Import/Page.php
index ac60de29..a1a28c62 100644
--- a/src/Import/Page.php
+++ b/src/Import/Page.php
@@ -4,8 +4,10 @@
use abrain\Einsatzverwaltung\AdminPage;
use abrain\Einsatzverwaltung\Import\Sources\AbstractSource;
use abrain\Einsatzverwaltung\Import\Sources\Csv;
+use abrain\Einsatzverwaltung\Import\Sources\FileSource;
use abrain\Einsatzverwaltung\Import\Sources\WpEinsatz;
use abrain\Einsatzverwaltung\Utilities;
+use function __;
use function array_key_exists;
use function check_admin_referer;
use function esc_attr;
@@ -13,7 +15,10 @@
use function esc_html__;
use function explode;
use function filter_input;
+use function get_posts;
+use function printf;
use function sanitize_text_field;
+use function sprintf;
use function submit_button;
use function wp_die;
use function wp_nonce_field;
@@ -117,7 +122,73 @@ protected function echoPageContent()
Utilities::sanitizeCheckbox($publishReports)
);
- echo '
Content
';
+ printf("
%s
", esc_html($currentStep->getTitle()));
+
+ switch ($action) {
+ case AbstractSource::STEP_ANALYSIS:
+ echo "Analysiere...";
+ break;
+ case AbstractSource::STEP_CHOOSEFILE:
+ if (!$this->currentSource instanceof FileSource) {
+ $this->printError('The selected source does not import from a file');
+ return;
+ }
+ $this->echoFileChooser($this->currentSource, $nextStep);
+ break;
+ case AbstractSource::STEP_IMPORT:
+ echo "Import...";
+ break;
+ default:
+ $this->printError(sprintf('Action %s is unknown', esc_html($action)));
+ }
+ }
+
+ /**
+ * @param FileSource $source
+ * @param Step $nextStep
+ */
+ private function echoFileChooser(FileSource $source, Step $nextStep)
+ {
+ $mimeType = $source->getMimeType();
+ if (empty($mimeType)) {
+ $this->printError('The MIME type must not be empty');
+ return;
+ }
+
+ echo '
Bitte werfe einen Blick in die Dokumentation, um herauszufinden, welche Anforderungen an die Datei gestellt werden.
';
+
+ echo '
In der Mediathek gefundene Dateien
';
+ echo 'Bevor eine Datei für den Import verwendet werden kann, muss sie in die Mediathek hochgeladen worden sein. Nach erfolgreichem Import kann die Datei gelöscht werden.';
+ $this->printWarning('Der Inhalt der Mediathek ist öffentlich abrufbar. Achte darauf, dass die Importdatei keine sensiblen Daten enthält.');
+
+ $attachments = get_posts(array(
+ 'post_type' => 'attachment',
+ 'post_mime_type' => $mimeType
+ ));
+
+ if (empty($attachments)) {
+ $this->printInfo(sprintf(__('No files of type %s found.', 'einsatzverwaltung'), $mimeType));
+ return;
+ }
+
+ echo '';
}
private function loadSources()
diff --git a/src/Import/Sources/AbstractSource.php b/src/Import/Sources/AbstractSource.php
index 0e7609d5..5da2a8c2 100644
--- a/src/Import/Sources/AbstractSource.php
+++ b/src/Import/Sources/AbstractSource.php
@@ -5,6 +5,7 @@
use abrain\Einsatzverwaltung\Exceptions\ImportException;
use abrain\Einsatzverwaltung\Import\Step;
use function esc_attr;
+use function in_array;
use function sprintf;
/**
@@ -12,6 +13,9 @@
*/
abstract class AbstractSource
{
+ const STEP_ANALYSIS = 'analysis';
+ const STEP_CHOOSEFILE = 'choosefile';
+ const STEP_IMPORT = 'import';
protected $args = array();
protected $autoMatchFields = array();
protected $cachedFields;
@@ -58,11 +62,12 @@ abstract public function checkPreconditions();
* Generiert für Argumente, die in der nächsten Action wieder gebraucht werden, Felder, die in das Formular
* eingebaut werden können, damit diese mitgenommen werden
*
+ * @param string $currentAction
* @param Step $nextStep
*/
- public function echoExtraFormFields(Step $nextStep)
+ public function echoExtraFormFields(string $currentAction, Step $nextStep)
{
- if (empty($nextStep)) {
+ if (empty($nextStep) || !in_array($currentAction, [self::STEP_ANALYSIS, self::STEP_IMPORT])) {
return;
}
diff --git a/src/Import/Sources/Csv.php b/src/Import/Sources/Csv.php
index 1b350b7d..52997507 100644
--- a/src/Import/Sources/Csv.php
+++ b/src/Import/Sources/Csv.php
@@ -5,11 +5,13 @@
use abrain\Einsatzverwaltung\Import\Step;
use abrain\Einsatzverwaltung\Utilities;
use Exception;
+use function __;
+use function in_array;
/**
* Importiert Einsatzberichte aus einer CSV-Datei
*/
-class Csv extends AbstractSource
+class Csv extends FileSource
{
private $dateFormats = array('d.m.Y', 'd.m.y', 'Y-m-d', 'm/d/Y', 'm/d/y');
private $timeFormats = array('H:i', 'G:i', 'H:i:s', 'G:i:s');
@@ -26,11 +28,12 @@ public function __construct()
{
$this->description = 'Importiert Einsatzberichte aus einer CSV-Datei.';
$this->identifier = 'evw_csv';
+ $this->mimeType = 'text/csv';
$this->name = 'CSV';
- $this->steps[] = new Step('selectcsvfile', 'Dateiauswahl', 'Datei auswählen');
- $this->steps[] = new Step('analysis', 'Analyse', 'Datei analysieren', ['csv_file_id', 'has_headlines', 'delimiter']);
- $this->steps[] = new Step('import', 'Import', 'Import starten', ['csv_file_id', 'has_headlines', 'delimiter']);
+ $this->steps[] = new Step(self::STEP_CHOOSEFILE, __('Choose a CSV file', 'einsatzverwaltung'), 'Datei auswählen');
+ $this->steps[] = new Step(self::STEP_ANALYSIS, 'Analyse', 'Datei analysieren', ['file_id', 'has_headlines', 'delimiter']);
+ $this->steps[] = new Step(self::STEP_IMPORT, 'Import', 'Import starten', ['file_id', 'has_headlines', 'delimiter']);
}
/**
@@ -74,28 +77,40 @@ public function checkPreconditions()
/**
* @inheritDoc
*/
- public function echoExtraFormFields($nextAction)
+ public function echoExtraFormFields(string $currentAction, Step $nextStep)
{
- echo '
Die CSV-Datei muss für den Import ein bestimmtes Format aufweisen. Jede Zeile in der Datei steht für einen Einsatzbericht und jede Spalte für ein Feld des Einsatzberichts (z.B. Alarmzeit, Einsatzort, ...). Die Reihenfolge der Spalten ist unerheblich, im nächsten Schritt können die Felder aus der Datei denen in der Einsatzverwaltung zugeordnet werden. Die erste Zeile in der Datei kann als Beschriftung der Spalten verwendet werden.
';
- $this->printDataNotice();
-
- echo '
In der Mediathek gefundene CSV-Dateien
';
- echo 'Bevor eine Datei für den Import verwendet werden kann, muss sie in die Mediathek hochgeladen worden sein. Nach erfolgreichem Import kann die Datei gelöscht werden.';
- $this->utilities->printWarning('Der Inhalt der Mediathek ist öffentlich abrufbar. Achte darauf, dass die Importdatei keine sensiblen Daten enthält.');
-
- $csvAttachments = get_posts(array(
- 'post_type' => 'attachment',
- 'post_mime_type' => 'text/csv'
- ));
-
- if (empty($csvAttachments)) {
- echo '
Die Felder Berichtstext, Berichtstitel, Einsatzleiter, Einsatzort und Mannschaftsstärke sind Freitextfelder.
';
- echo '
Für die Felder Alarmierungsart, Einsatzart, Externe Einsatzmittel und Fahrzeuge wird eine kommagetrennte Liste erwartet. Bisher unbekannte Einträge werden automatisch angelegt, die Einsatzart sollte nur ein einzelner Wert sein.
';
- if ('evw_wpe' == $this->currentSource->getIdentifier()) {
- echo '
Das Feld Einsatzende erwartet eine Datums- und Zeitangabe im Format JJJJ-MM-TT hh:mm:ss (z.B. 2014-04-21 21:48:06). Die Sekundenangabe ist optional.
Die Felder Alarmzeit und Einsatzende erwarten eine Datums- und Zeitangabe, das Format kann bei der Zuordnung der Felder angegeben werden.
';
- }
- echo '
Die Felder Besonderer Einsatz und Fehlalarm erwarten Ja/Nein-Werte. Als Ja interpretiert werden 1 und Ja (Groß- und Kleinschreibung unerheblich), alle anderen Werte einschließlich eines leeren Feldes zählen als Nein.
';
- }
}
From e3109061236d7733911426cdadda9846a107d6be Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Mon, 30 Nov 2020 17:56:28 +0100
Subject: [PATCH 14/25] Add the analysis step again
---
src/Import/Helper.php | 123 --------------------
src/Import/Page.php | 193 +++++++++++++++++++++++++++----
src/Import/Sources/Csv.php | 2 +-
src/Import/Sources/WpEinsatz.php | 4 +-
src/Import/Tool.php | 64 ----------
5 files changed, 176 insertions(+), 210 deletions(-)
diff --git a/src/Import/Helper.php b/src/Import/Helper.php
index 63debc9c..9f16e5c3 100644
--- a/src/Import/Helper.php
+++ b/src/Import/Helper.php
@@ -55,60 +55,6 @@ public function __construct(Utilities $utilities, Data $data)
$this->postFields = IncidentReport::getPostFields();
}
- /**
- * Gibt ein Auswahlfeld zur Zuordnung der Felder in Einsatzverwaltung aus
- *
- * @param array $args {
- * @type string $name Name des Dropdownfelds im Formular
- * @type string $selected Wert der ausgewählten Option
- * @type array $unmatchableFields Felder, die nicht als Importziel auswählbar sein sollen
- * }
- */
- private function dropdownEigeneFelder($args)
- {
- $defaults = array(
- 'name' => null,
- 'selected' => '-',
- 'unmatchableFields' => array()
- );
- $parsedArgs = wp_parse_args($args, $defaults);
-
- if (null === $parsedArgs['name'] || empty($parsedArgs['name'])) {
- _doing_it_wrong(__FUNCTION__, 'Name darf nicht null oder leer sein', '');
- }
-
- $fields = IncidentReport::getFields();
-
- // Felder, die automatisch beschrieben werden, nicht zur Auswahl stellen
- foreach ($parsedArgs['unmatchableFields'] as $ownField) {
- unset($fields[$ownField]);
- }
-
- // Sortieren und ausgeben
- uasort($fields, function ($field1, $field2) {
- return strcmp($field1['label'], $field2['label']);
- });
- $string = '';
-
- echo $string;
- }
-
/**
* @param array $mapping
* @param array $sourceEntry
@@ -354,75 +300,6 @@ public function prepareImport($source, $mapping, &$preparedInsertArgs, &$yearsAf
}
}
- /**
- * Gibt das Formular für die Zuordnung zwischen zu importieren Feldern und denen von Einsatzverwaltung aus
- *
- * @param AbstractSource $source
- * @param array $args {
- * @type array $mapping Zuordnung von zu importieren Feldern auf Einsatzverwaltungsfelder
- * @type array $next_action Array der nächsten Action
- * @type string $nonce_action Wert der Nonce
- * @type string $action_value Wert der action-Variable
- * @type string submit_button_text Beschriftung für den Button unter dem Formular
- * }
- */
- public function renderMatchForm($source, $args)
- {
- $defaults = array(
- 'mapping' => array(),
- 'next_action' => null,
- 'nonce_action' => '',
- 'action_value' => '',
- 'submit_button_text' => 'Import starten'
- );
-
- $parsedArgs = wp_parse_args($args, $defaults);
- $fields = $source->getFields();
-
- $unmatchableFields = $source->getUnmatchableFields();
- if (ReportNumberController::isAutoIncidentNumbers()) {
- $this->utilities->printInfo('Einsatznummern können nur importiert werden, wenn die automatische Verwaltung deaktiviert ist.');
-
- $unmatchableFields[] = 'einsatz_incidentNumber';
- }
-
- echo '';
- }
-
/**
* @param array $preparedInsertArgs
* @param AbstractSource $source
diff --git a/src/Import/Page.php b/src/Import/Page.php
index a1a28c62..ed4cceab 100644
--- a/src/Import/Page.php
+++ b/src/Import/Page.php
@@ -2,13 +2,19 @@
namespace abrain\Einsatzverwaltung\Import;
use abrain\Einsatzverwaltung\AdminPage;
+use abrain\Einsatzverwaltung\Exceptions\ImportCheckException;
+use abrain\Einsatzverwaltung\Exceptions\ImportException;
use abrain\Einsatzverwaltung\Import\Sources\AbstractSource;
use abrain\Einsatzverwaltung\Import\Sources\Csv;
use abrain\Einsatzverwaltung\Import\Sources\FileSource;
use abrain\Einsatzverwaltung\Import\Sources\WpEinsatz;
+use abrain\Einsatzverwaltung\Model\IncidentReport;
+use abrain\Einsatzverwaltung\ReportNumberController;
use abrain\Einsatzverwaltung\Utilities;
use function __;
+use function _n;
use function array_key_exists;
+use function array_keys;
use function check_admin_referer;
use function esc_attr;
use function esc_html;
@@ -16,10 +22,15 @@
use function explode;
use function filter_input;
use function get_posts;
+use function implode;
+use function in_array;
use function printf;
use function sanitize_text_field;
+use function selected;
use function sprintf;
+use function strcmp;
use function submit_button;
+use function uasort;
use function wp_die;
use function wp_nonce_field;
use const FILTER_SANITIZE_STRING;
@@ -31,12 +42,6 @@
*/
class Page extends AdminPage
{
-
- /**
- * @var AbstractSource
- */
- private $currentSource;
-
/**
* @var AbstractSource[]
*/
@@ -84,40 +89,40 @@ protected function echoPageContent()
if (!array_key_exists($identifier, $this->sources)) {
wp_die('Invalid source');
}
- $this->currentSource = $this->sources[$identifier];
+ $currentSource = $this->sources[$identifier];
// Set variables for further flow control
- $currentStep = $this->currentSource->getStep($action);
+ $currentStep = $currentSource->getStep($action);
if ($currentStep === false) {
wp_die('Invalid step');
}
// Check if the request has been sent through the form
- check_admin_referer($this->currentSource->getNonce($currentStep));
+ check_admin_referer($currentSource->getNonce($currentStep));
- $nextStep = $this->currentSource->getNextStep($currentStep);
+ $nextStep = $currentSource->getNextStep($currentStep);
// Read the settings that have been passed from the previous step
foreach ($currentStep->getArguments() as $argument) {
$value = filter_input(INPUT_POST, $argument, FILTER_SANITIZE_STRING);
- $this->currentSource->putArg($argument, $value);
+ $currentSource->putArg($argument, $value);
}
// Pass settings for date and time to the CSV source
// TODO move custom logic into the class of the source
- if ('evw_csv' == $this->currentSource->getIdentifier()) {
+ if ('evw_csv' == $currentSource->getIdentifier()) {
if (array_key_exists('import_date_format', $_POST)) {
- $this->currentSource->putArg('import_date_format', sanitize_text_field($_POST['import_date_format']));
+ $currentSource->putArg('import_date_format', sanitize_text_field($_POST['import_date_format']));
}
if (array_key_exists('import_time_format', $_POST)) {
- $this->currentSource->putArg('import_time_format', sanitize_text_field($_POST['import_time_format']));
+ $currentSource->putArg('import_time_format', sanitize_text_field($_POST['import_time_format']));
}
}
// Carry over the setting whether to publish imported reports immediately
$publishReports = filter_input(INPUT_POST, 'import_publish_reports', FILTER_SANITIZE_STRING);
- $this->currentSource->putArg(
+ $currentSource->putArg(
'import_publish_reports',
Utilities::sanitizeCheckbox($publishReports)
);
@@ -126,14 +131,14 @@ protected function echoPageContent()
switch ($action) {
case AbstractSource::STEP_ANALYSIS:
- echo "Analysiere...";
+ $this->echoAnalysis($currentSource, $currentStep, $nextStep);
break;
case AbstractSource::STEP_CHOOSEFILE:
- if (!$this->currentSource instanceof FileSource) {
+ if (!$currentSource instanceof FileSource) {
$this->printError('The selected source does not import from a file');
return;
}
- $this->echoFileChooser($this->currentSource, $nextStep);
+ $this->echoFileChooser($currentSource, $nextStep);
break;
case AbstractSource::STEP_IMPORT:
echo "Import...";
@@ -143,6 +148,65 @@ protected function echoPageContent()
}
}
+ /**
+ * @param AbstractSource $source
+ * @param Step $currentStep
+ * @param Step $nextStep
+ */
+ private function echoAnalysis(AbstractSource $source, Step $currentStep, Step $nextStep)
+ {
+ try {
+ $source->checkPreconditions();
+ } catch (ImportCheckException $e) {
+ $this->printError(sprintf('Voraussetzung nicht erfüllt: %s', $e->getMessage()));
+ return;
+ }
+
+ $fields = $source->getFields();
+ if (empty($fields)) {
+ $this->printError('Es wurden keine Felder gefunden');
+ return;
+ }
+ $numberOfFields = count($fields);
+ $this->printSuccess(sprintf(
+ _n('Found %1$d field: %2$s', 'Found %1$d fields: %2$s', $numberOfFields, 'einsatzverwaltung'),
+ $numberOfFields,
+ esc_html(implode($fields, ', '))
+ ));
+
+ // Check for mandatory fields
+ $mandatoryFieldsOk = true;
+ foreach (array_keys($source->getAutoMatchFields()) as $autoMatchField) {
+ if (!in_array($autoMatchField, $fields)) {
+ $this->printError(
+ sprintf('Das automatisch zu importierende Feld %s konnte nicht gefunden werden!', $autoMatchField)
+ );
+ $mandatoryFieldsOk = false;
+ }
+ }
+ if (!$mandatoryFieldsOk) {
+ return;
+ }
+
+ // Count the entries
+ try {
+ $entries = $source->getEntries(null);
+ } catch (ImportException $e) {
+ $this->printError(sprintf('Fehler beim Abfragen der Einsätze: %s', $e->getMessage()));
+ return;
+ }
+
+ if (empty($entries)) {
+ $this->printWarning('Es wurden keine Einsätze gefunden.');
+ return;
+ }
+ $this->printSuccess(sprintf("Es wurden %s Einsätze gefunden", count($entries)));
+
+ // Felder matchen
+ echo "
Felder zuordnen
";
+ $this->renderMatchForm($source, $currentStep, $nextStep);
+ }
+
/**
* @param FileSource $source
* @param Step $nextStep
@@ -172,7 +236,7 @@ private function echoFileChooser(FileSource $source, Step $nextStep)
}
echo '';
}
@@ -199,4 +263,93 @@ private function loadSources()
$csv = new Csv();
$this->sources[$csv->getIdentifier()] = $csv;
}
+
+ /**
+ * Gibt das Formular für die Zuordnung zwischen zu importieren Feldern und denen von Einsatzverwaltung aus
+ *
+ * @param AbstractSource $source
+ * @param Step $currentStep
+ * @param Step $nextStep
+ * @param array $mapping
+ */
+ private function renderMatchForm(AbstractSource $source, Step $currentStep, Step $nextStep, array $mapping = [])
+ {
+ $fields = $source->getFields();
+
+ // If the incident numbers are managed automatically, don't offer to import them
+ $unmatchableFields = $source->getUnmatchableFields();
+ if (ReportNumberController::isAutoIncidentNumbers()) {
+ $unmatchableFields[] = 'einsatz_incidentNumber';
+ }
+
+ echo '';
+ }
+
+ /**
+ * Generates a select tag for selecting the available properties of reports
+ *
+ * @param string $name Name of the select tag
+ * @param string $selected Value of the selected option, defaults to '-' for 'do not import'
+ * @param array $fieldsToSkip Array of own field names that should be skipped during output
+ */
+ private function renderOwnFieldsDropdown(string $name, string $selected = '-', array $fieldsToSkip = [])
+ {
+ $fields = IncidentReport::getFields();
+
+ // Remove fields that should not be presented as an option
+ foreach ($fieldsToSkip as $ownField) {
+ unset($fields[$ownField]);
+ }
+
+ // Sortieren und ausgeben
+ uasort($fields, function ($field1, $field2) {
+ return strcmp($field1['label'], $field2['label']);
+ });
+ $string = sprintf('';
+
+ echo $string;
+ }
}
diff --git a/src/Import/Sources/Csv.php b/src/Import/Sources/Csv.php
index 52997507..91591fb2 100644
--- a/src/Import/Sources/Csv.php
+++ b/src/Import/Sources/Csv.php
@@ -48,7 +48,7 @@ public function checkPreconditions()
$this->delimiter = $delimiter;
}
- $attachmentId = $this->args['csv_file_id'];
+ $attachmentId = $this->args['file_id'];
if (empty($attachmentId)) {
throw new ImportCheckException('Keine Datei ausgewählt');
}
diff --git a/src/Import/Sources/WpEinsatz.php b/src/Import/Sources/WpEinsatz.php
index c96b7a58..73f2900a 100644
--- a/src/Import/Sources/WpEinsatz.php
+++ b/src/Import/Sources/WpEinsatz.php
@@ -76,7 +76,7 @@ public function getEntries($fields = null)
{
global $wpdb;
$queryFields = (null === $fields ? '*' : implode(array_merge(array('ID'), $fields), ','));
- $query = sprintf('SELECT %s FROM \'%s\' ORDER BY Datum', $queryFields, $this->tablename);
+ $query = sprintf('SELECT %s FROM `%s` ORDER BY `Datum`', $queryFields, $this->tablename);
$entries = $wpdb->get_results($query, ARRAY_A);
if ($entries === null) {
@@ -109,7 +109,7 @@ public function getFields()
global $wpdb;
$fields = array();
- foreach ($wpdb->get_col("DESCRIBE '$this->tablename'", 0) as $columnName) {
+ foreach ($wpdb->get_col("DESCRIBE `$this->tablename`", 0) as $columnName) {
// Unwichtiges ignorieren
if ($columnName == 'ID' || $columnName == 'Nr_Jahr' || $columnName == 'Nr_Monat') {
continue;
diff --git a/src/Import/Tool.php b/src/Import/Tool.php
index 86905c32..89d2ca71 100644
--- a/src/Import/Tool.php
+++ b/src/Import/Tool.php
@@ -29,70 +29,6 @@ class Tool
*/
private $utilities;
- /**
- * Generiert den Inhalt der Werkzeugseite
- */
- public function renderToolPage()
- {
- $this->helper = new Helper($this->utilities, $this->data);
-
- // TODO gemeinsame Prüfungen auslagern
- if ('analysis' == $aktion) {
- $this->analysisPage();
- } elseif ('import' == $aktion) {
- $this->importPage();
- }
- }
-
- private function analysisPage()
- {
- if (!$this->currentSource->checkPreconditions()) {
- return;
- }
-
- $felder = $this->currentSource->getFields();
- if (empty($felder)) {
- $this->utilities->printError('Es wurden keine Felder gefunden');
- return;
- }
- $this->utilities->printSuccess('Es wurden ' . count($felder) . ' Feld(er) gefunden: ' . implode($felder, ', '));
-
- // Auf Pflichtfelder prüfen
- $mandatoryFieldsOk = true;
- foreach (array_keys($this->currentSource->getAutoMatchFields()) as $autoMatchField) {
- if (!in_array($autoMatchField, $felder)) {
- $this->utilities->printError(
- sprintf('Das automatisch zu importierende Feld %s konnte nicht gefunden werden!', $autoMatchField)
- );
- $mandatoryFieldsOk = false;
- }
- }
- if (!$mandatoryFieldsOk) {
- return;
- }
-
- // Einsätze zählen
- $entries = $this->currentSource->getEntries(null);
- if (empty($entries)) {
- $this->utilities->printWarning('Es wurden keine Einsätze gefunden.');
- return;
- }
- $this->utilities->printSuccess(sprintf("Es wurden %s Einsätze gefunden", count($entries)));
-
- if ('evw_wpe' == $this->currentSource->getIdentifier()) {
- $this->printDataNotice();
- }
-
- // Felder matchen
- echo "
Felder zuordnen
";
-
- $this->helper->renderMatchForm($this->currentSource, array(
- 'nonce_action' => $this->getNonceAction($this->currentSource, $this->nextAction['slug']),
- 'action_value' => $this->currentSource->getActionAttribute($this->nextAction['slug']),
- 'next_action' => $this->nextAction
- ));
- }
-
private function importPage()
{
if (!$this->currentSource->checkPreconditions()) {
From 0a36deb4fc5ba35a0a6b6bad9be3a05f2df9c3e0 Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Tue, 1 Dec 2020 19:12:13 +0100
Subject: [PATCH 15/25] Clean up a bit
---
src/Import/Sources/Csv.php | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/Import/Sources/Csv.php b/src/Import/Sources/Csv.php
index 91591fb2..b0699d3e 100644
--- a/src/Import/Sources/Csv.php
+++ b/src/Import/Sources/Csv.php
@@ -13,12 +13,12 @@
*/
class Csv extends FileSource
{
- private $dateFormats = array('d.m.Y', 'd.m.y', 'Y-m-d', 'm/d/Y', 'm/d/y');
- private $timeFormats = array('H:i', 'G:i', 'H:i:s', 'G:i:s');
private $csvFilePath;
+ private $dateFormats = array('d.m.Y', 'd.m.y', 'Y-m-d', 'm/d/Y', 'm/d/y');
private $delimiter = ';';
private $enclosure = '"';
private $fileHasHeadlines = false;
+ private $timeFormats = array('H:i', 'G:i', 'H:i:s', 'G:i:s');
/**
* Csv constructor.
@@ -119,8 +119,7 @@ public function echoExtraFormFields(string $currentAction, Step $nextStep)
public function getDateFormat()
{
if (!array_key_exists('import_date_format', $this->args)) {
- $fallbackDateFormat = $this->dateFormats[0];
- return $fallbackDateFormat;
+ return $this->dateFormats[0];
}
return $this->args['import_date_format'];
@@ -183,8 +182,7 @@ public function getFields()
public function getTimeFormat()
{
if (!array_key_exists('import_time_format', $this->args)) {
- $fallbackTimeFormat = $this->timeFormats[0];
- return $fallbackTimeFormat;
+ return $this->timeFormats[0];
}
return $this->args['import_time_format'];
From 66e39f03a543cd5aab9602074dca6f3e2f8256f6 Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Wed, 2 Dec 2020 13:12:48 +0100
Subject: [PATCH 16/25] Make the CSV source use the new CsvReader class
---
src/Import/Page.php | 25 ++++--
src/Import/Sources/AbstractSource.php | 8 +-
src/Import/Sources/Csv.php | 117 +++++++++-----------------
src/Import/Sources/WpEinsatz.php | 4 +-
4 files changed, 67 insertions(+), 87 deletions(-)
diff --git a/src/Import/Page.php b/src/Import/Page.php
index ed4cceab..2bd09d7c 100644
--- a/src/Import/Page.php
+++ b/src/Import/Page.php
@@ -162,7 +162,13 @@ private function echoAnalysis(AbstractSource $source, Step $currentStep, Step $n
return;
}
- $fields = $source->getFields();
+ try {
+ $fields = $source->getFields();
+ } catch (ImportException $e) {
+ $this->printError('Fehler beim Abrufen der Felder');
+ return;
+ }
+
if (empty($fields)) {
$this->printError('Es wurden keine Felder gefunden');
return;
@@ -190,7 +196,7 @@ private function echoAnalysis(AbstractSource $source, Step $currentStep, Step $n
// Count the entries
try {
- $entries = $source->getEntries(null);
+ $entries = $source->getEntries();
} catch (ImportException $e) {
$this->printError(sprintf('Fehler beim Abfragen der Einsätze: %s', $e->getMessage()));
return;
@@ -274,7 +280,12 @@ private function loadSources()
*/
private function renderMatchForm(AbstractSource $source, Step $currentStep, Step $nextStep, array $mapping = [])
{
- $fields = $source->getFields();
+ try {
+ $fields = $source->getFields();
+ } catch (ImportException $e) {
+ $this->printError('Fehler beim Abrufen der Felder');
+ return;
+ }
// If the incident numbers are managed automatically, don't offer to import them
$unmatchableFields = $source->getUnmatchableFields();
@@ -300,7 +311,11 @@ private function renderMatchForm(AbstractSource $source, Step $currentStep, Step
$selected = $mapping[$field];
}
- $this->renderOwnFieldsDropdown($source->getInputName($field), $selected, $unmatchableFields);
+ try {
+ $this->renderOwnFieldsDropdown($source->getInputName($field), $selected, $unmatchableFields);
+ } catch (ImportException $e) {
+ echo 'ERROR';
+ }
}
echo '';
}
@@ -328,7 +343,7 @@ private function renderOwnFieldsDropdown(string $name, string $selected = '-', a
unset($fields[$ownField]);
}
- // Sortieren und ausgeben
+ // Sort fields by name
uasort($fields, function ($field1, $field2) {
return strcmp($field1['label'], $field2['label']);
});
diff --git a/src/Import/Sources/AbstractSource.php b/src/Import/Sources/AbstractSource.php
index 5da2a8c2..91e8070c 100644
--- a/src/Import/Sources/AbstractSource.php
+++ b/src/Import/Sources/AbstractSource.php
@@ -142,16 +142,17 @@ abstract public function getDateFormat();
/**
* Gibt die Einsatzberichte der Importquelle zurück
*
- * @param array $fields Felder der Importquelle, die abgefragt werden sollen. Ist dieser Parameter null, werden alle
- * Felder abgefragt.
+ * @param string[] $requestedFields Names of the fields that should be queried from the source. Defaults to empty
+ * array, which requests all fields.
*
* @return array
* @throws ImportException
*/
- abstract public function getEntries($fields);
+ abstract public function getEntries(array $requestedFields = []);
/**
* @return array
+ * @throws ImportException
*/
abstract public function getFields();
@@ -185,6 +186,7 @@ public function getIdentifier()
* @param string $field Bezeichner des Felds
*
* @return string Eindeutiger Name bestehend aus Bezeichnern der Importquelle und des Felds
+ * @throws ImportException
*/
public function getInputName(string $field)
{
diff --git a/src/Import/Sources/Csv.php b/src/Import/Sources/Csv.php
index b0699d3e..16209a27 100644
--- a/src/Import/Sources/Csv.php
+++ b/src/Import/Sources/Csv.php
@@ -1,12 +1,18 @@
readFile(0);
- } catch (Exception $e) {
+ $csvReader = new CsvReader($csvFilePath, $this->delimiter, $this->enclosure);
+ $csvReader->getLines(1);
+ } catch (FileReadException $e) {
throw new ImportCheckException($e->getMessage());
}
}
@@ -126,16 +133,25 @@ public function getDateFormat()
}
/**
- * Gibt die Einsatzberichte der Importquelle zurück
- *
- * @param array $fields Felder der Importquelle, die abgefragt werden sollen. Ist dieser Parameter null, werden alle
- * Felder abgefragt.
- *
- * @return array|bool
+ * @inheritDoc
*/
- public function getEntries($fields)
+ public function getEntries(array $requestedFields = [])
{
- $lines = $this->readFile(null, $fields);
+ $columns = [];
+ if (!empty($requestedFields)) {
+ $fields = $this->getFields();
+ foreach ($requestedFields as $requestedField) {
+ $columns[] = array_search($requestedField, $fields);
+ }
+ error_log('Requested fields: '.print_r($requestedFields, true).', columns: '.print_r($columns, true));
+ }
+
+ $csvReader = new CsvReader($this->csvFilePath, $this->delimiter, $this->enclosure);
+ try {
+ $lines = $csvReader->getLines(0, $columns);
+ } catch (FileReadException $e) {
+ throw new ImportException($e->getMessage());
+ }
if (empty($lines)) {
return false;
@@ -149,7 +165,7 @@ public function getEntries($fields)
}
/**
- * @return array
+ * @inheritDoc
*/
public function getFields()
{
@@ -157,23 +173,28 @@ public function getFields()
return $this->cachedFields;
}
- $fields = $this->readFile(1);
+ $csvReader = new CsvReader($this->csvFilePath, $this->delimiter, $this->enclosure);
+ try {
+ $lines = $csvReader->getLines(1);
+ $fields = $lines[0];
+ } catch (FileReadException $e) {
+ throw new ImportException($e->getMessage());
+ }
if (empty($fields)) {
return array();
}
- // Gebe nummerierte Spalten zurück, wenn es keine Überschriften gibt
+ // If the first line does not contain the column names, return names like Coulumn 1, Column 2, ...
if (!$this->fileHasHeadlines) {
return array_map(function ($number) {
- return sprintf('Spalte %d', $number);
- }, range(1, count($fields[0])));
+ return sprintf(__('Column %d', 'einsatzverwaltung'), $number);
+ }, range(1, count($fields)));
}
- $this->cachedFields = $fields[0];
+ $this->cachedFields = $fields;
- // Gebe die Überschriften der Spalten zurück
- return $fields[0];
+ return $fields;
}
/**
@@ -187,62 +208,4 @@ public function getTimeFormat()
return $this->args['import_time_format'];
}
-
- /**
- * @param int|null $numLines Maximale Anzahl zu lesender Zeilen, oder null um alle Zeilen einzulesen
- * @param array $requestedFields
- *
- * @return array|bool
- * @throws Exception
- */
- private function readFile($numLines = null, $requestedFields = array())
- {
- $fieldMap = array();
- if (!empty($requestedFields)) {
- $fields = $this->getFields();
- foreach ($requestedFields as $requestedField) {
- $fieldMap[$requestedField] = array_search($requestedField, $fields);
- }
- }
-
- $handle = fopen($this->csvFilePath, 'r');
- if (empty($handle)) {
- throw new Exception('Konnte Datei nicht öffnen');
- }
-
- if ($numLines === 0) {
- fclose($handle);
- return array();
- }
-
- $lines = array();
- while (null === $numLines || count($lines) < $numLines) {
- $line = fgetcsv($handle, 0, $this->delimiter, $this->enclosure);
-
- // Problem beim Lesen oder Ende der Datei
- if (empty($line)) {
- break;
- }
-
- // Leere Zeile
- if (is_array($line) && $line[0] == null) {
- continue;
- }
-
- if (empty($requestedFields)) {
- $lines[] = $line;
- continue;
- }
-
- $filteredLine = array();
- foreach ($fieldMap as $fieldName => $index) {
- // Fehlende Felder in zu kurzen Zeilen werden als leer gewertet
- $filteredLine[$fieldName] = array_key_exists($index, $line) ? $line[$index] : '';
- }
- $lines[] = $filteredLine;
- }
-
- fclose($handle);
- return $lines;
- }
}
diff --git a/src/Import/Sources/WpEinsatz.php b/src/Import/Sources/WpEinsatz.php
index 73f2900a..362e159a 100644
--- a/src/Import/Sources/WpEinsatz.php
+++ b/src/Import/Sources/WpEinsatz.php
@@ -72,10 +72,10 @@ public function getDateFormat()
/**
* @inheritDoc
*/
- public function getEntries($fields = null)
+ public function getEntries(array $requestedFields = [])
{
global $wpdb;
- $queryFields = (null === $fields ? '*' : implode(array_merge(array('ID'), $fields), ','));
+ $queryFields = (empty($requestedFields) ? '*' : implode(array_merge(array('ID'), $requestedFields), ','));
$query = sprintf('SELECT %s FROM `%s` ORDER BY `Datum`', $queryFields, $this->tablename);
$entries = $wpdb->get_results($query, ARRAY_A);
From ba889ed634256997286b68e96b0afb6388c3fa1e Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Wed, 2 Dec 2020 13:15:54 +0100
Subject: [PATCH 17/25] Fix the tests
---
tests/unit/Import/Sources/CsvTest.php | 4 ++--
tests/unit/Import/Sources/WpEinsatzTest.php | 12 ++++++------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/tests/unit/Import/Sources/CsvTest.php b/tests/unit/Import/Sources/CsvTest.php
index ad2d963e..acdde3bd 100644
--- a/tests/unit/Import/Sources/CsvTest.php
+++ b/tests/unit/Import/Sources/CsvTest.php
@@ -1,7 +1,7 @@
expects()->get_var(Mockery::type('string'))->andReturn('wpunit_einsaetze');
- $wpdb->expects()->get_col("DESCRIBE 'wpunit_einsaetze'", 0)->andReturn(['Datum', 'Örtlichkeit', 'Einsatz#']);
+ $wpdb->expects()->get_col("DESCRIBE `wpunit_einsaetze`", 0)->andReturn(['Datum', 'Örtlichkeit', 'Einsatz#']);
$this->expectException(ImportCheckException::class);
$source = new WpEinsatz();
@@ -72,7 +72,7 @@ public function testCheckShouldPassIfConditionsAreMet()
// Pretend that the table exists and return some good column names
$wpdb->expects()->get_var("SHOW TABLES LIKE 'wpunit_einsaetze'")->andReturn('wpunit_einsaetze');
- $wpdb->expects()->get_col("DESCRIBE 'wpunit_einsaetze'", 0)->andReturn(['Datum', 'Ort', 'Art', 'Einsatztext']);
+ $wpdb->expects()->get_col("DESCRIBE `wpunit_einsaetze`", 0)->andReturn(['Datum', 'Ort', 'Art', 'Einsatztext']);
$source = new WpEinsatz();
try {
@@ -88,7 +88,7 @@ public function testCanGetFieldNames()
global $wpdb;
// Return some column names
- $wpdb->expects()->get_col("DESCRIBE 'wpunit_einsaetze'", 0)->andReturn(['ID', 'Nr_Jahr', 'Nr_Monat', 'Datum', 'Ort']);
+ $wpdb->expects()->get_col("DESCRIBE `wpunit_einsaetze`", 0)->andReturn(['ID', 'Nr_Jahr', 'Nr_Monat', 'Datum', 'Ort']);
$source = new WpEinsatz();
$this->assertEqualSets(['Datum', 'Ort'], $source->getFields());
@@ -100,7 +100,7 @@ public function testFieldsGetCached()
global $wpdb;
// Return some column names
- $wpdb->expects()->get_col("DESCRIBE 'wpunit_einsaetze'", 0)->andReturn(['ID', 'Nr_Jahr', 'Nr_Monat', 'Datum', 'Ort']);
+ $wpdb->expects()->get_col("DESCRIBE `wpunit_einsaetze`", 0)->andReturn(['ID', 'Nr_Jahr', 'Nr_Monat', 'Datum', 'Ort']);
$source = new WpEinsatz();
$this->assertEqualSets(['Datum', 'Ort'], $source->getFields());
@@ -120,7 +120,7 @@ public function testGetsEntriesForAllFields()
['ID' => 2, 'colA' => 'value4', 'colB' => 'value5', 'colC' => 'value6'],
['ID' => 3, 'colA' => 'value7', 'colB' => 'value8', 'colC' => 'value9']
];
- $wpdb->expects()->get_results("SELECT * FROM 'wpunit_einsaetze' ORDER BY Datum", ARRAY_A)->andReturn($entries);
+ $wpdb->expects()->get_results("SELECT * FROM `wpunit_einsaetze` ORDER BY `Datum`", ARRAY_A)->andReturn($entries);
$source = new WpEinsatz();
try {
@@ -142,7 +142,7 @@ public function testGetsEntriesForCertainFields()
['ID' => 3, 'colA' => 'value5', 'colC' => 'value6']
];
$wpdb->expects()
- ->get_results("SELECT ID,colA,colC FROM 'wpunit_einsaetze' ORDER BY Datum", ARRAY_A)
+ ->get_results("SELECT ID,colA,colC FROM `wpunit_einsaetze` ORDER BY `Datum`", ARRAY_A)
->andReturn($entries);
$source = new WpEinsatz();
From f31f51e8a231db59ff3a2f085b936ea5b86f3a9e Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Wed, 2 Dec 2020 15:20:01 +0100
Subject: [PATCH 18/25] Generate and check the mapping
---
src/Import/Helper.php | 49 +-------------
src/Import/MappingHelper.php | 97 +++++++++++++++++++++++++++
src/Import/Page.php | 43 ++++++++++--
src/Import/Sources/AbstractSource.php | 33 +--------
src/Import/Sources/Csv.php | 2 +-
src/Import/Tool.php | 78 ---------------------
6 files changed, 140 insertions(+), 162 deletions(-)
create mode 100644 src/Import/MappingHelper.php
delete mode 100644 src/Import/Tool.php
diff --git a/src/Import/Helper.php b/src/Import/Helper.php
index 9f16e5c3..086d7b76 100644
--- a/src/Import/Helper.php
+++ b/src/Import/Helper.php
@@ -6,7 +6,6 @@
use abrain\Einsatzverwaltung\Exceptions\ImportPreparationException;
use abrain\Einsatzverwaltung\Import\Sources\AbstractSource;
use abrain\Einsatzverwaltung\Model\IncidentReport;
-use abrain\Einsatzverwaltung\ReportNumberController;
use abrain\Einsatzverwaltung\Utilities;
use DateTime;
@@ -264,6 +263,7 @@ public function sanitizeBooleanValues($value)
* @param array $mapping
* @param array $preparedInsertArgs
* @param array $yearsAffected
+ * @throws ImportException
* @throws ImportPreparationException
*/
public function prepareImport($source, $mapping, &$preparedInsertArgs, &$yearsAffected)
@@ -334,51 +334,4 @@ public function runImport($preparedInsertArgs, $source, $yearsAffected, $importS
}
}
}
-
- /**
- * Prüft, ob das Mapping stimmig ist und gibt Warnungen oder Fehlermeldungen aus
- *
- * @param array $mapping Das zu prüfende Mapping
- * @param AbstractSource $source
- *
- * @return bool True bei bestandener Prüfung, false bei Unstimmigkeiten
- */
- public function validateMapping($mapping, $source)
- {
- $valid = true;
-
- // Pflichtfelder prüfen
- if (!in_array('post_date', $mapping)) {
- $this->utilities->printError('Pflichtfeld Alarmzeit wurde nicht zugeordnet');
- $valid = false;
- }
-
- $unmatchableFields = $source->getUnmatchableFields();
- $autoMatchFields = $source->getAutoMatchFields();
- if (ReportNumberController::isAutoIncidentNumbers()) {
- $unmatchableFields[] = 'einsatz_incidentNumber';
- }
- foreach ($unmatchableFields as $unmatchableField) {
- if (in_array($unmatchableField, $mapping) && !in_array($unmatchableField, $autoMatchFields)) {
- $this->utilities->printError(sprintf(
- 'Feld %s kann nicht für ein zu importierendes Feld als Ziel angegeben werden',
- esc_html($unmatchableField)
- ));
- $valid = false;
- }
- }
-
- // Mehrfache Zuweisungen prüfen
- foreach (array_count_values($mapping) as $ownField => $count) {
- if ($count > 1) {
- $this->utilities->printError(sprintf(
- 'Feld %s kann nicht für mehr als ein zu importierendes Feld als Ziel angegeben werden',
- IncidentReport::getFieldLabel($ownField)
- ));
- $valid = false;
- }
- }
-
- return $valid;
- }
}
diff --git a/src/Import/MappingHelper.php b/src/Import/MappingHelper.php
new file mode 100644
index 00000000..33ad84f6
--- /dev/null
+++ b/src/Import/MappingHelper.php
@@ -0,0 +1,97 @@
+getFields() as $sourceField) {
+ $ownField = filter_input(INPUT_POST, $source->getInputName($sourceField), FILTER_SANITIZE_STRING);
+
+ // Skip source fields that are not mapped to a field
+ if (empty($ownField) || !is_string($ownField) || $ownField === '-') {
+ continue;
+ }
+
+ if (!array_key_exists($ownField, $ownFields)) {
+ throw new ImportCheckException(sprintf(__('Unknown field: %s', 'einsatzverwaltung'), $ownField));
+ }
+
+ $mapping[$sourceField] = $ownField;
+ }
+
+ // The source may give a mandatory mapping for certain fields
+ foreach ($source->getAutoMatchFields() as $sourceFieldAuto => $ownFieldAuto) {
+ $mapping[$sourceFieldAuto] = $ownFieldAuto;
+ }
+
+ return $mapping;
+ }
+
+ /**
+ * Prüft, ob das Mapping stimmig ist und gibt Warnungen oder Fehlermeldungen aus
+ *
+ * @param array $mapping Das zu prüfende Mapping
+ * @param AbstractSource $source
+ *
+ * @throws ImportCheckException
+ */
+ public function validateMapping(array $mapping, AbstractSource $source)
+ {
+ // Pflichtfelder prüfen
+ if (!in_array('post_date', $mapping)) {
+ throw new ImportCheckException('Pflichtfeld Alarmzeit wurde nicht zugeordnet');
+ }
+
+ $unmatchableFields = $source->getUnmatchableFields();
+ $autoMatchFields = $source->getAutoMatchFields();
+ if (ReportNumberController::isAutoIncidentNumbers()) {
+ $unmatchableFields[] = 'einsatz_incidentNumber';
+ }
+ foreach ($unmatchableFields as $unmatchableField) {
+ if (in_array($unmatchableField, $mapping) && !in_array($unmatchableField, $autoMatchFields)) {
+ throw new ImportCheckException(sprintf(
+ 'Feld %s kann nicht für ein zu importierendes Feld als Ziel angegeben werden',
+ esc_html($unmatchableField)
+ ));
+ }
+ }
+
+ // Mehrfache Zuweisungen prüfen
+ foreach (array_count_values($mapping) as $ownField => $count) {
+ if ($count > 1) {
+ throw new ImportCheckException(sprintf(
+ 'Feld %s kann nicht für mehr als ein zu importierendes Feld als Ziel angegeben werden',
+ IncidentReport::getFieldLabel($ownField)
+ ));
+ }
+ }
+ }
+}
diff --git a/src/Import/Page.php b/src/Import/Page.php
index 2bd09d7c..64c99886 100644
--- a/src/Import/Page.php
+++ b/src/Import/Page.php
@@ -141,7 +141,7 @@ protected function echoPageContent()
$this->echoFileChooser($currentSource, $nextStep);
break;
case AbstractSource::STEP_IMPORT:
- echo "Import...";
+ $this->echoImport($currentSource, $currentStep);
break;
default:
$this->printError(sprintf('Action %s is unknown', esc_html($action)));
@@ -164,7 +164,7 @@ private function echoAnalysis(AbstractSource $source, Step $currentStep, Step $n
try {
$fields = $source->getFields();
- } catch (ImportException $e) {
+ } catch (ImportCheckException $e) {
$this->printError('Fehler beim Abrufen der Felder');
return;
}
@@ -261,6 +261,41 @@ private function echoFileChooser(FileSource $source, Step $nextStep)
echo '';
}
+ /**
+ * @param AbstractSource $source
+ * @param Step $currentStep
+ */
+ private function echoImport(AbstractSource $source, Step $currentStep)
+ {
+ try {
+ $source->checkPreconditions();
+ } catch (ImportCheckException $e) {
+ $this->printError(sprintf('Voraussetzung nicht erfüllt: %s', $e->getMessage()));
+ return;
+ }
+
+ // Get the mapping of the source fields to our internal fields
+ $mappingHelper = new MappingHelper();
+ try {
+ $mapping = $mappingHelper->getMapping($source, IncidentReport::getFields());
+ $mappingHelper->validateMapping($mapping, $source);
+ } catch (ImportCheckException $e) {
+ $this->printError(sprintf("Fehler bei der Zuordnung: %s", $e->getMessage()));
+
+ // Repeat the mapping
+ $this->renderMatchForm($source, $currentStep, $currentStep, empty($mapping) ? [] : $mapping);
+ return;
+ }
+
+ // Start the import
+ echo '
Die Daten werden eingelesen, das kann einen Moment dauern.
';
+ // TODO do the import
+
+ $this->printSuccess('Der Import ist abgeschlossen');
+ $url = admin_url('edit.php?post_type=einsatz');
+ printf('Zu den Einsatzberichten', $url);
+ }
+
private function loadSources()
{
$wpEinsatz = new WpEinsatz();
@@ -282,7 +317,7 @@ private function renderMatchForm(AbstractSource $source, Step $currentStep, Step
{
try {
$fields = $source->getFields();
- } catch (ImportException $e) {
+ } catch (ImportCheckException $e) {
$this->printError('Fehler beim Abrufen der Felder');
return;
}
@@ -313,7 +348,7 @@ private function renderMatchForm(AbstractSource $source, Step $currentStep, Step
try {
$this->renderOwnFieldsDropdown($source->getInputName($field), $selected, $unmatchableFields);
- } catch (ImportException $e) {
+ } catch (ImportCheckException $e) {
echo 'ERROR';
}
}
diff --git a/src/Import/Sources/AbstractSource.php b/src/Import/Sources/AbstractSource.php
index 91e8070c..71011eaa 100644
--- a/src/Import/Sources/AbstractSource.php
+++ b/src/Import/Sources/AbstractSource.php
@@ -152,7 +152,7 @@ abstract public function getEntries(array $requestedFields = []);
/**
* @return array
- * @throws ImportException
+ * @throws ImportCheckException
*/
abstract public function getFields();
@@ -186,7 +186,7 @@ public function getIdentifier()
* @param string $field Bezeichner des Felds
*
* @return string Eindeutiger Name bestehend aus Bezeichnern der Importquelle und des Felds
- * @throws ImportException
+ * @throws ImportCheckException
*/
public function getInputName(string $field)
{
@@ -194,35 +194,6 @@ public function getInputName(string $field)
return $this->getIdentifier() . '-field' . $fieldId;
}
- /**
- * @param array $sourceFields Felder der Importquelle
- * @param array $ownFields Felder der Einsatzverwaltung
- *
- * @return array
- * @throws ImportException
- */
- public function getMapping($sourceFields, $ownFields)
- {
- $mapping = array();
- foreach ($sourceFields as $sourceField) {
- $index = $this->getInputName($sourceField);
- if (array_key_exists($index, $_POST)) {
- $ownField = $_POST[$index];
- if (!empty($ownField) && is_string($ownField) && $ownField != '-') {
- if (array_key_exists($ownField, $ownFields)) {
- $mapping[$sourceField] = $ownField;
- } else {
- throw new ImportException(sprintf(__('Unknown field: %s', 'einsatzverwaltung'), $ownField));
- }
- }
- }
- }
- foreach ($this->autoMatchFields as $sourceFieldAuto => $ownFieldAuto) {
- $mapping[$sourceFieldAuto] = $ownFieldAuto;
- }
- return $mapping;
- }
-
/**
* Gibt den Namen der Importquelle zurück
*
diff --git a/src/Import/Sources/Csv.php b/src/Import/Sources/Csv.php
index 16209a27..b5b11c2c 100644
--- a/src/Import/Sources/Csv.php
+++ b/src/Import/Sources/Csv.php
@@ -178,7 +178,7 @@ public function getFields()
$lines = $csvReader->getLines(1);
$fields = $lines[0];
} catch (FileReadException $e) {
- throw new ImportException($e->getMessage());
+ throw new ImportCheckException($e->getMessage());
}
if (empty($fields)) {
diff --git a/src/Import/Tool.php b/src/Import/Tool.php
deleted file mode 100644
index 89d2ca71..00000000
--- a/src/Import/Tool.php
+++ /dev/null
@@ -1,78 +0,0 @@
-currentSource->checkPreconditions()) {
- return;
- }
-
- $sourceFields = $this->currentSource->getFields();
- if (empty($sourceFields)) {
- $this->utilities->printError('Es wurden keine Felder gefunden');
- return;
- }
-
- // Mapping einlesen
- $mapping = $this->currentSource->getMapping($sourceFields, IncidentReport::getFields());
-
- // Prüfen, ob mehrere Felder das gleiche Zielfeld haben
- if (!$this->helper->validateMapping($mapping, $this->currentSource)) {
- // Und gleich nochmal...
- $this->nextAction = $this->currentAction;
-
- $this->helper->renderMatchForm($this->currentSource, array(
- 'mapping' => $mapping,
- 'nonce_action' => $this->getNonceAction($this->currentSource, $this->nextAction['slug']),
- 'action_value' => $this->currentSource->getActionAttribute($this->nextAction['slug']),
- 'next_action' => $this->nextAction
- ));
- return;
- }
-
- // Import starten
- echo '
Die Daten werden eingelesen, das kann einen Moment dauern.
';
- $importStatus = new ImportStatus($this->utilities, 0);
- try {
- $this->helper->import($this->currentSource, $mapping, $importStatus);
- } catch (ImportException $e) {
- $importStatus->abort('Import abgebrochen, Ursache: ' . $e->getMessage());
- return;
- } catch (ImportPreparationException $e) {
- $importStatus->abort('Importvorbereitung abgebrochen, Ursache: ' . $e->getMessage());
- return;
- }
-
- $this->utilities->printSuccess('Der Import ist abgeschlossen');
- $url = admin_url('edit.php?post_type=einsatz');
- printf('Zu den Einsatzberichten', $url);
- }
-}
From 04ed3f3f4b9500cae11dd84e506f9d4021265c4e Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Fri, 29 Jan 2021 18:33:45 +0100
Subject: [PATCH 19/25] Fix deprecated use of the implode function
---
src/Import/Page.php | 2 +-
src/Import/Sources/WpEinsatz.php | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Import/Page.php b/src/Import/Page.php
index 64c99886..de8af213 100644
--- a/src/Import/Page.php
+++ b/src/Import/Page.php
@@ -177,7 +177,7 @@ private function echoAnalysis(AbstractSource $source, Step $currentStep, Step $n
$this->printSuccess(sprintf(
_n('Found %1$d field: %2$s', 'Found %1$d fields: %2$s', $numberOfFields, 'einsatzverwaltung'),
$numberOfFields,
- esc_html(implode($fields, ', '))
+ esc_html(implode(', ', $fields))
));
// Check for mandatory fields
diff --git a/src/Import/Sources/WpEinsatz.php b/src/Import/Sources/WpEinsatz.php
index 362e159a..ff3051e6 100644
--- a/src/Import/Sources/WpEinsatz.php
+++ b/src/Import/Sources/WpEinsatz.php
@@ -75,7 +75,7 @@ public function getDateFormat()
public function getEntries(array $requestedFields = [])
{
global $wpdb;
- $queryFields = (empty($requestedFields) ? '*' : implode(array_merge(array('ID'), $requestedFields), ','));
+ $queryFields = (empty($requestedFields) ? '*' : implode(',', array_merge(array('ID'), $requestedFields)));
$query = sprintf('SELECT %s FROM `%s` ORDER BY `Datum`', $queryFields, $this->tablename);
$entries = $wpdb->get_results($query, ARRAY_A);
From 12287fb562320ed1d0f87cdbbcab7523e19f4c08 Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Sat, 6 Feb 2021 13:10:54 +0100
Subject: [PATCH 20/25] Make a tiny change, to trigger a new build of the PR
and check the results on Code Climate
---
src/Import/MappingHelper.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Import/MappingHelper.php b/src/Import/MappingHelper.php
index 33ad84f6..c4193c36 100644
--- a/src/Import/MappingHelper.php
+++ b/src/Import/MappingHelper.php
@@ -28,7 +28,7 @@ class MappingHelper
* @return array
* @throws ImportCheckException
*/
- public function getMapping(AbstractSource $source, array $ownFields)
+ public function getMapping(AbstractSource $source, array $ownFields): array
{
$mapping = [];
From fd3844957e5e218805e69d2ab7250d394931ec1b Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Sat, 6 Feb 2021 15:34:00 +0100
Subject: [PATCH 21/25] No need for bash
---
bin/check-branch-name.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bin/check-branch-name.sh b/bin/check-branch-name.sh
index 2a8a7504..2d05ec79 100755
--- a/bin/check-branch-name.sh
+++ b/bin/check-branch-name.sh
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
if [ "$DRONE_BUILD_EVENT" == "pull_request" ]; then
case "$DRONE_SOURCE_BRANCH" in
From 255c8e0f679528cdc88af0487c64129770911895 Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Sat, 6 Feb 2021 16:15:16 +0100
Subject: [PATCH 22/25] Send the test report in the same script, or the env
variable is no longer set
---
.drone.yml | 3 +--
bin/{determine-cc-branch.sh => report-code-coverage.sh} | 5 +++++
2 files changed, 6 insertions(+), 2 deletions(-)
rename bin/{determine-cc-branch.sh => report-code-coverage.sh} (71%)
diff --git a/.drone.yml b/.drone.yml
index 8db056ba..8ca00ef3 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -31,8 +31,7 @@ steps:
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
- XDEBUG_MODE=coverage ./vendor/bin/phpunit -c phpunit.xml
- - ./bin/determine-cc-branch.sh
- - ./cc-test-reporter after-build --debug --coverage-input-type clover --exit-code $?
+ - ./bin/report-code-coverage.sh
- name: slack
image: plugins/slack
settings:
diff --git a/bin/determine-cc-branch.sh b/bin/report-code-coverage.sh
similarity index 71%
rename from bin/determine-cc-branch.sh
rename to bin/report-code-coverage.sh
index 54c2cc23..3461ef6a 100755
--- a/bin/determine-cc-branch.sh
+++ b/bin/report-code-coverage.sh
@@ -1,5 +1,8 @@
#!/usr/bin/env bash
+# The root directory of the project is one up
+cd "$(dirname "$0")/.."
+
# Set the environment variable GIT_BRANCH for the Code Climate test-reporter, so it does not report the coverage for the
# target branch of pull requests but for the source branch
if [ "$DRONE_BUILD_EVENT" == "pull_request" ]; then
@@ -7,3 +10,5 @@ if [ "$DRONE_BUILD_EVENT" == "pull_request" ]; then
else
export GIT_BRANCH="$DRONE_COMMIT_BRANCH"
fi
+
+./cc-test-reporter after-build --debug --coverage-input-type clover
\ No newline at end of file
From 0f0fc2a165ccfc34712cf73c7d79ae9ebad4beb2 Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Sat, 24 Aug 2024 21:57:49 +0200
Subject: [PATCH 23/25] Fix composer.json
---
composer.json | 6 ------
1 file changed, 6 deletions(-)
diff --git a/composer.json b/composer.json
index 445ec4e8..4fe08d43 100644
--- a/composer.json
+++ b/composer.json
@@ -19,12 +19,6 @@
"source": "https://github.com/abrain/einsatzverwaltung",
"docs": "https://einsatzverwaltung.org/dokumentation/"
},
- "scripts": {
- "test": [
- "Composer\\Config::disableProcessTimeout",
- "phpunit"
- ]
- },
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
From 4a3a7363ffdd4387e8a9da884dcdb672f25fc84a Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Sat, 24 Aug 2024 22:06:55 +0200
Subject: [PATCH 24/25] Add missing translators comments
---
src/Import/CsvReader.php | 1 +
src/Import/MappingHelper.php | 1 +
src/Import/Page.php | 2 ++
src/Import/Sources/Csv.php | 1 +
src/Import/Sources/WpEinsatz.php | 1 +
5 files changed, 6 insertions(+)
diff --git a/src/Import/CsvReader.php b/src/Import/CsvReader.php
index e5194f98..a913c4bc 100644
--- a/src/Import/CsvReader.php
+++ b/src/Import/CsvReader.php
@@ -57,6 +57,7 @@ public function getLines(int $numLines, array $columns = [], int $offset = 0): a
{
$handle = fopen($this->filePath, 'r');
if ($handle === false) {
+ // translators: 1: file path
$message = sprintf(__('Could not open file %s', 'einsatzverwaltung'), $this->filePath);
throw new FileReadException($message);
}
diff --git a/src/Import/MappingHelper.php b/src/Import/MappingHelper.php
index c4193c36..3cd2d73a 100644
--- a/src/Import/MappingHelper.php
+++ b/src/Import/MappingHelper.php
@@ -41,6 +41,7 @@ public function getMapping(AbstractSource $source, array $ownFields): array
}
if (!array_key_exists($ownField, $ownFields)) {
+ // translators: 1: field name
throw new ImportCheckException(sprintf(__('Unknown field: %s', 'einsatzverwaltung'), $ownField));
}
diff --git a/src/Import/Page.php b/src/Import/Page.php
index de8af213..a0f1bf1e 100644
--- a/src/Import/Page.php
+++ b/src/Import/Page.php
@@ -175,6 +175,7 @@ private function echoAnalysis(AbstractSource $source, Step $currentStep, Step $n
}
$numberOfFields = count($fields);
$this->printSuccess(sprintf(
+ // translators: 1: number of fields, 2: comma-separated list of field names
_n('Found %1$d field: %2$s', 'Found %1$d fields: %2$s', $numberOfFields, 'einsatzverwaltung'),
$numberOfFields,
esc_html(implode(', ', $fields))
@@ -237,6 +238,7 @@ private function echoFileChooser(FileSource $source, Step $nextStep)
));
if (empty($attachments)) {
+ // translators: 1: MIME type
$this->printInfo(sprintf(__('No files of type %s found.', 'einsatzverwaltung'), $mimeType));
return;
}
diff --git a/src/Import/Sources/Csv.php b/src/Import/Sources/Csv.php
index b5b11c2c..ac799f33 100644
--- a/src/Import/Sources/Csv.php
+++ b/src/Import/Sources/Csv.php
@@ -188,6 +188,7 @@ public function getFields()
// If the first line does not contain the column names, return names like Coulumn 1, Column 2, ...
if (!$this->fileHasHeadlines) {
return array_map(function ($number) {
+ // translators: 1: column number
return sprintf(__('Column %d', 'einsatzverwaltung'), $number);
}, range(1, count($fields)));
}
diff --git a/src/Import/Sources/WpEinsatz.php b/src/Import/Sources/WpEinsatz.php
index ff3051e6..20343709 100644
--- a/src/Import/Sources/WpEinsatz.php
+++ b/src/Import/Sources/WpEinsatz.php
@@ -55,6 +55,7 @@ public function checkPreconditions()
}
if (!empty($this->problematicFields)) {
throw new ImportCheckException(sprintf(
+ // translators: 1: comma-separated list of field names
__('One or more fields have a special character in their name. This can become a problem during the import. Please rename the following fields in the settings of wp-einsatz: %s', 'einsatzverwaltung'),
esc_html(join(', ', $this->problematicFields))
));
From e61630e03279c0523a5bc608f0e5bc311a407b4f Mon Sep 17 00:00:00 2001
From: Andreas Brain
Date: Sun, 25 Aug 2024 15:51:26 +0200
Subject: [PATCH 25/25] Improve CSV reader error handling and tests
---
patchwork.json | 6 +++++
src/Import/CsvReader.php | 20 +++++++++------
tests/unit/Import/CsvReaderTest.php | 39 +++++++++++++++++++++++++++++
3 files changed, 57 insertions(+), 8 deletions(-)
create mode 100644 patchwork.json
diff --git a/patchwork.json b/patchwork.json
new file mode 100644
index 00000000..79951553
--- /dev/null
+++ b/patchwork.json
@@ -0,0 +1,6 @@
+{
+ "redefinable-internals": [
+ "feof",
+ "fgetcsv"
+ ]
+}
\ No newline at end of file
diff --git a/src/Import/CsvReader.php b/src/Import/CsvReader.php
index a913c4bc..1af28a15 100644
--- a/src/Import/CsvReader.php
+++ b/src/Import/CsvReader.php
@@ -88,20 +88,16 @@ private function readLines($handle, int $numLines, array $columns): array
$lines = array();
while ($numLines === 0 || $linesRead < $numLines) {
$line = fgetcsv($handle, 0, $this->delimiter, $this->enclosure);
- $linesRead++;
- // End of file reached?
- if ($line === false && feof($handle)) {
+ // Error while reading, most likely EOF
+ if ($line === false) {
break;
}
- // Problem while reading the file
- if (empty($line)) {
- throw new FileReadException();
- }
+ $linesRead++;
// Empty line in the file, skip this
- if (is_array($line) && $line[0] == null) {
+ if ($line == [null]) {
continue;
}
@@ -120,6 +116,14 @@ private function readLines($handle, int $numLines, array $columns): array
$lines[] = $filteredLine;
}
+ if (($numLines === 0 || $linesRead < $numLines) && feof($handle) === false) {
+ throw new FileReadException(sprintf(
+ // translators: 1: number of lines
+ _n('Reading was aborted after %d line', 'Reading was aborted after %d lines', $linesRead, 'einsatzverwaltung'),
+ $linesRead
+ ));
+ }
+
return $lines;
}
}
diff --git a/tests/unit/Import/CsvReaderTest.php b/tests/unit/Import/CsvReaderTest.php
index 4ecc4f9e..a9d50a4b 100644
--- a/tests/unit/Import/CsvReaderTest.php
+++ b/tests/unit/Import/CsvReaderTest.php
@@ -3,6 +3,7 @@
use abrain\Einsatzverwaltung\Exceptions\FileReadException;
use abrain\Einsatzverwaltung\UnitTestCase;
+use function Brain\Monkey\Functions\when;
/**
* Class CsvReaderTest
@@ -68,4 +69,42 @@ public function testFillsNotExistingColumns()
['9f0NPAB0HU', '']
], $lines);
}
+
+ public function testThrowsWhenReadingTooFewLines()
+ {
+ $this->expectException(FileReadException::class);
+ $this->expectExceptionMessage('2 lines');
+ $csvReader = new CsvReader(__DIR__ . '/reports.csv', ';', '"');
+
+ $counter = 0;
+ when('fgetcsv')->alias(function () use (&$counter) {
+ if ($counter++ > 1) {
+ return false;
+ }
+ return ['lorem', 'ipsum'];
+ });
+ when('feof')->justReturn(false);
+
+ // Suppress the warning, otherwise PHPUnit would convert it to an exception
+ @$csvReader->getLines(3);
+ }
+
+ public function testThrowsWhenStoppingBeforeEndOfFile()
+ {
+ $this->expectException(FileReadException::class);
+ $this->expectExceptionMessage('1 line');
+ $csvReader = new CsvReader(__DIR__ . '/reports.csv', ';', '"');
+
+ $counter = 0;
+ when('fgetcsv')->alias(function () use (&$counter) {
+ if ($counter++ > 0) {
+ return false;
+ }
+ return ['lorem', 'ipsum'];
+ });
+ when('feof')->justReturn(false);
+
+ // Suppress the warning, otherwise PHPUnit would convert it to an exception
+ @$csvReader->getLines(0);
+ }
}