From ae59375a056e44b358dd0f383bcb73156ef444f1 Mon Sep 17 00:00:00 2001
From: Jaap Jansma <jaap@edeveloper.nl>
Date: Thu, 31 May 2018 15:27:34 +0200
Subject: [PATCH] added option group input type

---
 Civi/FormProcessor/Type/AbstractType.php      | 54 +++++++++------
 Civi/FormProcessor/Type/Factory.php           | 19 ++----
 Civi/FormProcessor/Type/OptionGroupType.php   | 68 +++++++++++++++++++
 ang/form_processor.ang.php                    |  2 +-
 ang/form_processor/FormProcessorEditCtrl.js   |  3 +-
 .../DefaultDataInputTable.html                |  2 +-
 .../FormProcessorEditCtrl/InputTable.html     |  2 +-
 .../config/crmFormProcessorSpecification.html |  1 +
 .../crmFormProcessorTypeConfiguration.html    | 35 +++++++++-
 .../crmFormProcessorTypeConfiguration.js      |  1 +
 .../inputs/InputDialogCtrl.html               |  2 +-
 ang/form_processor/inputs/InputDialogCtrl.js  |  4 +-
 .../inputs/crmFormProcessorInputs.js          |  4 +-
 13 files changed, 151 insertions(+), 46 deletions(-)
 create mode 100644 Civi/FormProcessor/Type/OptionGroupType.php

diff --git a/Civi/FormProcessor/Type/AbstractType.php b/Civi/FormProcessor/Type/AbstractType.php
index 4f06005..bc55a21 100644
--- a/Civi/FormProcessor/Type/AbstractType.php
+++ b/Civi/FormProcessor/Type/AbstractType.php
@@ -36,16 +36,6 @@
 	public function __construct($name, $label) {
 		$this->name = $name;
 		$this->label = $label;
-		
-		$this->configuration = new ConfigurationBag();
-		$this->defaultConfiguration = new ConfigurationBag();
-
-		foreach($this->getConfigurationSpecification() as $spec) {
-			if ($spec->getDefaultValue()) {
-				$this->configuration->set($spec->getName(), $spec->getDefaultValue());
-				$this->defaultConfiguration->set($spec->getName(), $spec->getDefaultValue());
-			}
-		}
 	}
 	
 	/**
@@ -113,6 +103,21 @@
 	public function getDefaultConfiguration() {
 		return $this->defaultConfiguration;
 	}
+  
+  /**
+   * Sets the default values of this action
+   */
+  public function setDefaults() {
+    $this->configuration = new ConfigurationBag();
+    $this->defaultConfiguration = new ConfigurationBag();
+
+    foreach($this->getConfigurationSpecification() as $spec) {
+      if ($spec->getDefaultValue() !== null) {
+        $this->configuration->setParameter($spec->getName(), $spec->getDefaultValue());
+        $this->defaultConfiguration->setParameter($spec->getName(), $spec->getDefaultValue());
+      }
+    }
+  }
 	
 	/**
 	 * @param ConfigurationBag $configuration
@@ -123,17 +128,26 @@
 	}
 	
 	public function toArray() {
-		return array(
-			'name' => $this->name,
-			'label' => $this->label,
-			'configuration_spec' => $this->getConfigurationSpecification()->toArray(),
-			'configuration' => $this->getConfiguration()->toArray(),
-			'default_configuration' => $this->getDefaultConfiguration()->toArray(),
-		);
+		$return['name'] = $this->name;
+	  $return['label'] = $this->label;
+	  $return['configuration_spec'] = $this->getConfigurationSpecification()->toArray();
+    $return['default_configuration'] = null;
+    if ($this->getDefaultConfiguration()) {
+      $return['default_configuration'] = $this->getDefaultConfiguration()->toArray();
+    }
+    return $return;
 	}
 	
-	public function jsonSerialize() {
-		return $this->toArray();
-	}
+	/**
+   * Returns the data structure to serialize it as a json
+   */
+  public function jsonSerialize() {
+    $return = $this->toArray();
+    // An empty array goes wrong with the default confifuration.
+    if (empty($return['default_configuration'])) {
+      $return['default_configuration'] = new \stdClass();;
+    }
+    return $return;
+  }
 	
  }
diff --git a/Civi/FormProcessor/Type/Factory.php b/Civi/FormProcessor/Type/Factory.php
index 801942f..cc96080 100644
--- a/Civi/FormProcessor/Type/Factory.php
+++ b/Civi/FormProcessor/Type/Factory.php
@@ -10,6 +10,7 @@
  use \Civi\FormProcessor\Type\AbstractType;
  use \Civi\FormProcessor\Type\GenericType;
  use \Civi\FormProcessor\Type\CountryType;
+ use \Civi\FormProcessor\Type\OptionGroupType;
  
  use CRM_FormProcessor_ExtensionUtil as E;
  
@@ -27,6 +28,7 @@
 		$this->addType(new GenericType('Text', E::ts('Long text')));
 		$this->addType(new DateType(E::ts('Date')));
 		$this->addType(new GenericType('Boolean', E::ts('Yes/No')));
+    $this->addType(new OptionGroupType('OptionGroup', E::ts('Option Group')));
     $this->addType(new CountryType('Country', E::ts('Country')));
 	}
 	
@@ -48,7 +50,9 @@
 	 */
 	public function getTypeByName($name) {
 		if (isset($this->types[$name])) {
-			return $this->types[$name];
+		  $type = clone $this->types[$name];
+      $type->setDefaults();
+      return $type;
 		}
 		return null;
 	}
@@ -60,17 +64,4 @@
 		return $this->types;
 	}
 	
-	/**
-	 * Returns an array with the name and label
-	 * 
-	 * @return array<string>
-	 */
-	public function getTypeLabels() {
-		$return = array();
-		foreach($this->types as $type) {
-			$return[] = $type->toArray();
-		}
-		return $return;
-	}
-	
  }
diff --git a/Civi/FormProcessor/Type/OptionGroupType.php b/Civi/FormProcessor/Type/OptionGroupType.php
new file mode 100644
index 0000000..21ed39f
--- /dev/null
+++ b/Civi/FormProcessor/Type/OptionGroupType.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @author Jaap Jansma (CiviCooP) <jaap.jansma@civicoop.org>
+ * @license http://www.gnu.org/licenses/agpl-3.0.html
+ */
+
+namespace Civi\FormProcessor\Type;
+
+use \Civi\FormProcessor\Config\Specification;
+use \Civi\FormProcessor\Config\SpecificationBag;
+use \Civi\FormProcessor\Type\AbstractType;
+use \Civi\FormProcessor\Type\OptionListInterface;
+
+use \CRM_FormProcessor_ExtensionUtil as E;
+
+class OptionGroupType extends AbstractType implements OptionListInterface {
+
+  /**
+   * Get the configuration specification
+   *
+   * @return SpecificationBag
+   */
+  public function getConfigurationSpecification() {
+    $optionsGroups = array();
+    $optionGroupsApi = civicrm_api3('OptionGroup', 'get', array('is_active' => 1, 'options' => array('limit' => 0)));
+    foreach($optionGroupsApi['values'] as $optionGroup) {
+      $optionsGroups[$optionGroup['id']] = $optionGroup['title'];
+    }
+    return new SpecificationBag(array(
+      new Specification('option_group_id', 'Integer', E::ts('Option group'), true, null, null, $optionsGroups, false),
+    ));
+  }
+
+  public function validateValue($value) {
+    if (\CRM_Utils_Type::validate($value, 'String', false) === NULL) {
+      return false;
+    }
+
+    $countries = $this->getOptions();
+    if (!isset($countries[$value])) {
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * Returns the type number from CRM_Utils_Type
+   */
+  public function getCrmType() {
+    return \CRM_Utils_Type::T_STRING;
+  }
+
+  public function getOptions() {
+    $option_group_id = $this->configuration->get('option_group_id');
+    if (!$option_group_id) {
+      return array();
+    }
+    $optionsApi = civicrm_api3('OptionValue', 'get', array('option_group_id' => $option_group_id, 'is_active' => 1, 'options' => array('limit' => 0)));
+    $options = array();
+    foreach($optionsApi['values'] as $optionValue) {
+      $options[$optionValue['value']] = $optionValue['label'];
+    }
+    return $options;
+  }
+
+}
diff --git a/ang/form_processor.ang.php b/ang/form_processor.ang.php
index f68b7c5..04981db 100644
--- a/ang/form_processor.ang.php
+++ b/ang/form_processor.ang.php
@@ -34,7 +34,7 @@ return array (
   ),
   'settings' => 
   array (
-  	'inputTypes' => \Civi::service('form_processor_type_factory')->getTypeLabels(),
+  	'inputTypes' => \Civi::service('form_processor_type_factory')->getTypes(),
   	'validators' => \Civi::service('form_processor_validation_factory')->getValidators(),
   	'outputHandlers' => \Civi::service('form_processor_output_handler_factory')->getHandlersAsArray(),
   	'defaultOutputHandler' => \Civi::service('form_processor_output_handler_factory')->getDefaultHandler()->toArray(),
diff --git a/ang/form_processor/FormProcessorEditCtrl.js b/ang/form_processor/FormProcessorEditCtrl.js
index 7ffd51b..7d47497 100644
--- a/ang/form_processor/FormProcessorEditCtrl.js
+++ b/ang/form_processor/FormProcessorEditCtrl.js
@@ -137,8 +137,7 @@
       		delete input.id;
       	}
       	var type = angular.copy(input.type);
-      	input.form_processor_instance_id = formProcessor.id;
-      	input.configuration = input.type.configuration; 
+      	input.form_processor_instance_id = formProcessor.id; 
       	input.type = input.type.name;
       	crmApi(api, 'create', input, true).then(function (input_result){
     			input.id = input_result['id'];
diff --git a/ang/form_processor/FormProcessorEditCtrl/DefaultDataInputTable.html b/ang/form_processor/FormProcessorEditCtrl/DefaultDataInputTable.html
index a774062..82b8a3c 100644
--- a/ang/form_processor/FormProcessorEditCtrl/DefaultDataInputTable.html
+++ b/ang/form_processor/FormProcessorEditCtrl/DefaultDataInputTable.html
@@ -2,6 +2,6 @@
 <crm-form-processor-inputs
 	form-processor="formProcessor"
 	inputs="formProcessor.default_data_inputs"
-	deletedActions="deletedDefaultDataInputs"
+	deleted-inputs="deletedDefaultDataInputs"
 	entity="FormProcessorDefaultDataInput">
 </crm-form-processor-inputs>
\ No newline at end of file
diff --git a/ang/form_processor/FormProcessorEditCtrl/InputTable.html b/ang/form_processor/FormProcessorEditCtrl/InputTable.html
index cbe0cae..71b35d0 100644
--- a/ang/form_processor/FormProcessorEditCtrl/InputTable.html
+++ b/ang/form_processor/FormProcessorEditCtrl/InputTable.html
@@ -2,6 +2,6 @@
 <crm-form-processor-inputs
 	form-processor="formProcessor"
 	inputs="formProcessor.inputs"
-	deletedActions="deletedInputs"
+	deleted-inputs="deletedInputs"
 	entity="FormProcessorInput">
 </crm-form-processor-inputs>
\ No newline at end of file
diff --git a/ang/form_processor/config/crmFormProcessorSpecification.html b/ang/form_processor/config/crmFormProcessorSpecification.html
index 5defdc2..8c9304b 100644
--- a/ang/form_processor/config/crmFormProcessorSpecification.html
+++ b/ang/form_processor/config/crmFormProcessorSpecification.html
@@ -13,6 +13,7 @@
  </div>
  <div ng-if="!noLabel" class="clear"></div>
 </div>
+
 <div ng-if="specification.type == 'fields'">
 	<div ng-if="!noLabel" class="label">
   	<label crm-ui-for="specification.name" crm-depth="1" crm-ui-force-required="specification.required">{{specification.title}}</label>
diff --git a/ang/form_processor/crmFormProcessorTypeConfiguration.html b/ang/form_processor/crmFormProcessorTypeConfiguration.html
index c5512a5..a1e306d 100644
--- a/ang/form_processor/crmFormProcessorTypeConfiguration.html
+++ b/ang/form_processor/crmFormProcessorTypeConfiguration.html
@@ -1,15 +1,44 @@
 
 <ng-repeat ng-repeat="spec in type.configuration_spec.parameter_specifications">
-<div crm-ui-field="{name: 'spec.name', title: spec.title, required: spec.required}">
+
+  <div ng-if="spec.options"
+    crm-ui-field="{name: spec.name, title: spec.title, required: spec.required}">
+    <select
+          style="width: 90%;"
+          crm-ui-select
+          crm-ui-id="{{spec.name}}"
+          ui-options="{allowClear: true}"
+          name="{{spec.name}}"
+          ng-model="configuration[spec.name]"
+          ng-required="spec.required"
+      >
+        <option ng-repeat="(key,value) in spec.options" value="{{key}}">{{value}}</option>
+      </select>
+  </div>
+  <div ng-if="spec.fk_entity"
+    crm-ui-field="{name: spec.name, title: spec.title, required: spec.required}">
+      <input
+        crm-entityref="{entity: spec.fk_entity, select: {allowClear: true, placeholder: spec.title}}"
+        name="{{spec.name}}"
+        crm-ui-id="{{spec.name}}"
+        ng-model="configuration[spec.name]"
+        ng-required="spec.required"
+      />
+  </div>
+
+<div ng-if="!spec.options && !spec.fk_entity"
+    crm-ui-field="{name: spec.name, title: spec.title, required: spec.required}">
       <input
         type="text"
         name="{{spec.name}}"
-        ng-model="type.configuration[spec.name]"
+        crm-ui-id="{{spec.name}}"
+        ng-model="configuration[spec.name]"
         class="big crm-form-text"
         ng-required="spec.required"
         />
 			
-			<div class="description" ng-if="spec.description" ng-bind-html="spec.description"></div>
+</div>
+<div class="description" ng-if="spec.description" ng-bind-html="spec.description"></div>
 
 </ng-repeat>
 
diff --git a/ang/form_processor/crmFormProcessorTypeConfiguration.js b/ang/form_processor/crmFormProcessorTypeConfiguration.js
index 8bec3b1..e2b757f 100644
--- a/ang/form_processor/crmFormProcessorTypeConfiguration.js
+++ b/ang/form_processor/crmFormProcessorTypeConfiguration.js
@@ -5,6 +5,7 @@
 	    restrict: 'E',
 	    scope: {
 	      type: '=type',
+	      configuration: '=configuration',
 	    },
 	    templateUrl: '~/form_processor/crmFormProcessorTypeConfiguration.html',
 	  };
diff --git a/ang/form_processor/inputs/InputDialogCtrl.html b/ang/form_processor/inputs/InputDialogCtrl.html
index 6da944e..1083a5b 100644
--- a/ang/form_processor/inputs/InputDialogCtrl.html
+++ b/ang/form_processor/inputs/InputDialogCtrl.html
@@ -38,7 +38,7 @@
 	    </div>
 	    
 	    <crm-form-processor-type-configuration 
-	    	type=input.type
+	    	type=input.type configuration=input.configuration
     	></crm-form-processor-type-configuration>
     	
     	<div class="crm-block">
diff --git a/ang/form_processor/inputs/InputDialogCtrl.js b/ang/form_processor/inputs/InputDialogCtrl.js
index fbf8206..d69936f 100644
--- a/ang/form_processor/inputs/InputDialogCtrl.js
+++ b/ang/form_processor/inputs/InputDialogCtrl.js
@@ -11,13 +11,13 @@
   		$scope.input.deletedValidators = [];
   	}
     
-    $scope.$watch('model.input.name', function(newName, oldName) {
+    $scope.$watch('input.name', function(newName, oldName) {
     	$scope.invalidName = newName && !newName.match(/^[a-z0-9_]+$/) ? true : false;
     	$scope.nameExists = false;
     	if (newName && !$scope.invalidName) {
     		// Check whether the name already exists
     		angular.forEach($scope.model.inputs, function(input, key) {
-    			if (input != $scope.model.input && input.name == newName) {
+    			if (input != $scope.input && input.name == newName) {
     				$scope.nameExists = true;
     			}
     		});
diff --git a/ang/form_processor/inputs/crmFormProcessorInputs.js b/ang/form_processor/inputs/crmFormProcessorInputs.js
index 7849be8..569c85b 100644
--- a/ang/form_processor/inputs/crmFormProcessorInputs.js
+++ b/ang/form_processor/inputs/crmFormProcessorInputs.js
@@ -48,7 +48,8 @@
 		      	'name': '',
 		  			'is_required': 0,
 		  			'default_value': '',
-		  			'validators': []
+		  			'validators': [],
+		  			'configuration': angular.copy(inputType.default_configuration),
 		    	};
 		      $scope.editInput(input);
 		    };
@@ -64,6 +65,7 @@
 		      index = $scope.inputs.indexOf(input);
 		      var model = {
 		      	formProcessor: $scope.formProcessor,
+		      	inputs: $scope.inputs,
 		      	input: input,
 		      	entity: $scope.entity
 		      };
-- 
GitLab