From 42fc2a68965a2835a5d9112f9dbbbefe4490bb13 Mon Sep 17 00:00:00 2001
From: Simon Hermann <simon.hermann@civiservice.de>
Date: Mon, 26 Jun 2023 11:24:05 +0200
Subject: [PATCH] Added a second output handler, which compares the input
 string with up to 5 values. If any of these values matches the input, the
 result is mapped to a the given replacement string.

---
 Civi/DataProcessor/Factory.php                |   3 +-
 .../ConditionallyMapStrings.php               | 156 ++++++++++++++++++
 .../FieldOutputHandler/SanitizeString.php     |   5 +-
 .../ConditionallyMapStringsOutputHandler.tpl  |  45 +++++
 4 files changed, 205 insertions(+), 4 deletions(-)
 create mode 100644 Civi/DataProcessor/FieldOutputHandler/ConditionallyMapStrings.php
 create mode 100644 templates/CRM/Dataprocessor/Form/Field/Configuration/ConditionallyMapStringsOutputHandler.tpl

diff --git a/Civi/DataProcessor/Factory.php b/Civi/DataProcessor/Factory.php
index 611642d2..0a58d8c1 100644
--- a/Civi/DataProcessor/Factory.php
+++ b/Civi/DataProcessor/Factory.php
@@ -223,7 +223,8 @@ class Factory {
     $this->addOutputHandler('aggregation_function', new Definition('Civi\DataProcessor\FieldOutputHandler\AggregateFunctionFieldOutputHandler'), E::ts('Aggregation function'));
     $this->addOutputHandler('image', new Definition('Civi\DataProcessor\FieldOutputHandler\ImageFieldOutputHandler'), E::ts('Image'));
     $this->addOutputHandler('array', new Definition('Civi\DataProcessor\FieldOutputHandler\ArrayFieldOutputHandler'), E::ts('Multivalue Field as Array'));
-    $this->addOutputHandler('sanitize_string', new Definition('Civi\DataProcessor\FieldOutputHandler\SanitizeString'), E::ts('Escape special characters in the string and replace newlines by \\n.'));
+    $this->addOutputHandler('sanitize_string', new Definition('Civi\DataProcessor\FieldOutputHandler\SanitizeString'), E::ts('Replaces special characters by their HTML code and newlines by \\n.'));
+    $this->addOutputHandler('conditionally_map_strings', new Definition('Civi\DataProcessor\FieldOutputHandler\ConditionallyMapStrings'), E::ts('Compare the input with up to 5 strings and map the output accordingly.'));
     $this->addFileFormat('csv', new Definition('Civi\DataProcessor\FileFormat\CSVFileFormat'), E::ts('CSV'));
   }
 
diff --git a/Civi/DataProcessor/FieldOutputHandler/ConditionallyMapStrings.php b/Civi/DataProcessor/FieldOutputHandler/ConditionallyMapStrings.php
new file mode 100644
index 00000000..3831e5da
--- /dev/null
+++ b/Civi/DataProcessor/FieldOutputHandler/ConditionallyMapStrings.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace Civi\DataProcessor\FieldOutputHandler;
+
+use \Civi\DataProcessor\DataSpecification\FieldSpecification;
+use \Civi\DataProcessor\FieldOutputHandler\AbstractSimpleFieldOutputHandler;
+use \Civi\DataProcessor\FieldOutputHandler\FieldOutput;
+use CRM_Dataprocessor_ExtensionUtil as E;
+
+class ConditionallyMapStrings extends AbstractSimpleFieldOutputHandler {
+
+  private $conditions;
+  private $replacements;
+  private $default;
+
+  private $dataFields = 6;
+
+  /**
+   * When this handler has additional configuration you can add
+   * the fields on the form with this function.
+   *
+   * @param \CRM_Core_Form $form
+   * @param array $field
+   */
+  public function buildConfigurationForm(\CRM_Core_Form $form, $field = array()) {
+    parent::buildConfigurationForm($form, $field);
+    for ($i = 1; $i < $this->dataFields; ++$i) {
+      $form->add('text', "condition_{$i}", E::ts("{$i}. input comparison"), FALSE);
+      $form->add('text', "replacement_{$i}", E::ts("{$i}. input replacement"), FALSE);
+    }
+    $form->add('text', "default", E::ts("Default fallback, if no condition matches."), FALSE);
+
+
+    // set the default values
+    if (isset($field['configuration'])) {
+      $configuration = $field['configuration'];
+      for ($i = 1; $i < $this->dataFields; ++$i) {
+        if (isset($configuration["condition_{$i}"])) {
+          $this->defaults["condition_{$i}"] = $configuration["condition_{$i}"];
+        }
+        if (isset($configuration["replacement_{$i}"])) {
+          $this->defaults["replacement_{$i}"] = $configuration["replacement_{$i}"];
+        }
+      }
+      if (isset($configuration['default'])) {
+        $this->defaults['default'] = $configuration['default'];
+      }
+
+      $form->setDefaults($this->defaults);
+    }
+  }
+
+  /**
+   * Process the submitted values and create a configuration array
+   *
+   * @param $submittedValues
+   * @return array
+   */
+  public function processConfiguration($submittedValues) {
+
+    $configuration = parent::processConfiguration($submittedValues);
+    // register the fields to the QuickForm
+    for ($i = 1; $i < $this->dataFields; ++$i) {
+      if (isset($submittedValues["condition_{$i}"])) {
+        $configuration["condition_{$i}"] = $submittedValues["condition_{$i}"];
+      }
+      if (isset($submittedValues["replacement_{$i}"])) {
+        $configuration["replacement_{$i}"] = $submittedValues["replacement_{$i}"];
+      }
+    }
+    if (isset($submittedValues['default'])) {
+      $configuration['default'] = $submittedValues['default'];
+    }
+
+    return $configuration;
+  }
+
+  /**
+   * @return string
+   */
+  protected function getFieldTitle() {
+    return E::ts('Input field');
+  }
+
+  public function getConfigurationTemplateFileName() {
+    return "CRM/Dataprocessor/Form/Field/Configuration//ConditionallyMapStringsOutputHandler.tpl";
+  }
+
+  /**
+   * Initialize the processor
+   *
+   * @param String $alias
+   * @param String $title
+   * @param array $configuration
+   * @param \Civi\DataProcessor\ProcessorType\AbstractProcessorType $processorType
+   */
+  public function initialize($alias, $title, $configuration) {
+    parent::initialize($alias, $title, $configuration);
+    $this->outputFieldSpec = new FieldSpecification($alias, 'String', $title, null, $alias);
+    $this->initializeConfiguration($configuration);
+  }
+
+  /**
+   * @param array $configuration
+   *
+   * @return void
+   */
+  protected function initializeConfiguration($configuration) {
+    for ($i = 1; $i < $this->dataFields; ++$i) {
+      $replacement = "replacement_{$i}";
+      $condition = "condition_{$i}";
+      if (isset($configuration[$condition])) {
+        $this->conditions[$condition] = $configuration[$condition];
+      }
+      if (isset($configuration[$replacement])) {
+        $this->replacements[$replacement] = $configuration[$replacement];
+      }
+    }
+    if (isset($configuration['default'])) {
+      $this->default = $configuration['default'];
+    }
+  }
+
+  /**
+   * Returns the formatted value
+   *
+   * @param $rawRecord
+   * @param $formattedRecord
+   *
+   * @return \Civi\DataProcessor\FieldOutputHandler\FieldOutput
+   */
+  public function formatField($rawRecord, $formattedRecord) {
+
+    $inputField = $rawRecord[$this->inputFieldSpec->alias];
+
+    // set the default value
+    if (!empty($this->default)) {
+      $result = $this->default;
+    }
+    else {
+      $result = $inputField;
+    }
+
+    // check all conditions and replace with the replacement string, if a match is found
+    for ($i = 1; $i < $this->dataFields; ++$i) {
+      $replacement = "replacement_{$i}";
+      $condition = "condition_{$i}";
+      if (!empty($this->conditions[$condition]) && $this->conditions[$condition] === $inputField) {
+        $result = $this->replacements[$replacement];
+        break 1;
+      }
+    }
+
+    return new FieldOutput($result);
+  }
+}
\ No newline at end of file
diff --git a/Civi/DataProcessor/FieldOutputHandler/SanitizeString.php b/Civi/DataProcessor/FieldOutputHandler/SanitizeString.php
index 84b59d7c..48c864e1 100644
--- a/Civi/DataProcessor/FieldOutputHandler/SanitizeString.php
+++ b/Civi/DataProcessor/FieldOutputHandler/SanitizeString.php
@@ -6,7 +6,7 @@ use \Civi\DataProcessor\FieldOutputHandler\AbstractSimpleFieldOutputHandler;
 use \Civi\DataProcessor\FieldOutputHandler\FieldOutput;
 use CRM_Dataprocessor_ExtensionUtil as E;
 
-class SanitizeStringDataprocessor extends AbstractSimpleFieldOutputHandler {
+class SanitizeString extends AbstractSimpleFieldOutputHandler {
 
   /**
    * @return string
@@ -29,10 +29,9 @@ class SanitizeStringDataprocessor extends AbstractSimpleFieldOutputHandler {
    * @return \Civi\DataProcessor\FieldOutputHandler\FieldOutput
    */
   public function formatField($rawRecord, $formattedRecord) {
-
     $inputField = $rawRecord[$this->inputFieldSpec->alias];
 
-    $result = nl2br(htmlspecialchars($inputField));
+    $result = htmlspecialchars(preg_replace("/\R/", '\n', $inputField));
 
     return new FieldOutput($result);
   }
diff --git a/templates/CRM/Dataprocessor/Form/Field/Configuration/ConditionallyMapStringsOutputHandler.tpl b/templates/CRM/Dataprocessor/Form/Field/Configuration/ConditionallyMapStringsOutputHandler.tpl
new file mode 100644
index 00000000..6b178983
--- /dev/null
+++ b/templates/CRM/Dataprocessor/Form/Field/Configuration/ConditionallyMapStringsOutputHandler.tpl
@@ -0,0 +1,45 @@
+{crmScope extensionKey='dataprocessor'}
+{include file="CRM/Dataprocessor/Form/Field/Configuration/SimpleFieldOutputHandler.tpl"}
+    <p class="help">{ts}Compare the input field with up to 5 values. Replace the input value with the replacement of the first value that matches.{/ts}</p>
+<div class="crm-section">
+  <div class="label">{$form.condition_1.label}</div>
+  <div class="content">{$form.condition_1.html}</div>
+  <div class="label">{$form.replacement_1.label}</div>
+  <div class="content">{$form.replacement_1.html}</div>
+  <div class="clear"></div>
+  </div>
+<div class="crm-section">
+  <div class="label">{$form.condition_2.label}</div>
+  <div class="content">{$form.condition_2.html}</div>
+  <div class="label">{$form.replacement_2.label}</div>
+  <div class="content">{$form.replacement_2.html}</div>
+  <div class="clear"></div>
+  </div>
+<div class="crm-section">
+  <div class="label">{$form.condition_3.label}</div>
+  <div class="content">{$form.condition_3.html}</div>
+  <div class="label">{$form.replacement_3.label}</div>
+  <div class="content">{$form.replacement_3.html}</div>
+  <div class="clear"></div>
+  </div>
+<div class="crm-section">
+  <div class="label">{$form.condition_4.label}</div>
+  <div class="content">{$form.condition_4.html}</div>
+  <div class="label">{$form.replacement_4.label}</div>
+  <div class="content">{$form.replacement_4.html}</div>
+  <div class="clear"></div>
+  </div>
+<div class="crm-section">
+  <div class="label">{$form.condition_5.label}</div>
+  <div class="content">{$form.condition_5.html}</div>
+  <div class="label">{$form.replacement_5.label}</div>
+  <div class="content">{$form.replacement_5.html}</div>
+  <div class="clear"></div>
+  </div>
+<div class="crm-section">
+  <div class="label">{$form.default.label}</div>
+  <div class="content">{$form.default.html}</div>
+  <div class="clear"></div>
+</div>
+    <p class="help">{ts}If no default value is provided, the original input is returned, if none of the comparison values matches.{/ts}</p>
+{/crmScope}
\ No newline at end of file
-- 
GitLab