diff --git a/CRM/FormProcessor/BAO/FormProcessor.php b/CRM/FormProcessor/BAO/FormProcessor.php
index 99f65d07d0d28611aa65d8127f4723b63806088b..50f22a9c13e755158e7baca135bd9a43644958e6 100644
--- a/CRM/FormProcessor/BAO/FormProcessor.php
+++ b/CRM/FormProcessor/BAO/FormProcessor.php
@@ -27,7 +27,9 @@
     $formProcessor->find();
     while ($formProcessor->fetch()) {
       $row = array();
-      self::storeValues($formProcessor, $row);
+      self::storeValues($formProcessor, $row); 
+			$row['inputs'] = array_values(CRM_FormProcessor_BAO_FormProcessorInput::getValues(array('form_processor_id' => $formProcessor->id)));
+			
       $result[$row['id']] = $row;
     }
     return $result;
@@ -95,6 +97,9 @@
 
     CRM_Utils_Hook::pre('delete', 'FormProcessor', $id, CRM_Core_DAO::$_nullArray);
 
+		// First delete all inputs
+		CRM_FormProcessor_BAO_FormProcessorInput::deleteWithFormProcessorId($id);
+
     $rule = new CRM_FormProcessor_BAO_FormProcessor();
     $rule->id = $id;
     $rule->delete();
diff --git a/CRM/FormProcessor/BAO/FormProcessorInput.php b/CRM/FormProcessor/BAO/FormProcessorInput.php
new file mode 100644
index 0000000000000000000000000000000000000000..a91312a18ceb017255349425dea70b3883c3b154
--- /dev/null
+++ b/CRM/FormProcessor/BAO/FormProcessorInput.php
@@ -0,0 +1,137 @@
+<?php
+
+use CRM_FormProcessor_ExtensionUtil as E;
+
+/**
+ * @author Jaap Jansma (CiviCooP) <jaap.jansma@civicoop.org>
+ * @license http://www.gnu.org/licenses/agpl-3.0.html
+ */
+class CRM_FormProcessor_BAO_FormProcessorInput extends CRM_FormProcessor_DAO_FormProcessorInput {
+  
+	 /**
+   * Function to get values
+   * 
+   * @return array $result found rows with data
+   * @access public
+   * @static
+   */
+  public static function getValues($params) {
+    $result = array();
+    $input = new CRM_FormProcessor_BAO_FormProcessorInput();
+    if (!empty($params)) {
+      $fields = self::fields();
+      foreach ($params as $key => $value) {
+        if (isset($fields[$key])) {
+          $input->$key = $value;
+        }
+      }
+    }
+    $input->find();
+    while ($input->fetch()) {
+      $row = array();
+      self::storeValues($input, $row);
+      if (!empty($row['form_processor_id'])) {
+      	$type = \Civi::service('form_processor_type_factory')->getTypeByName($row['type']);
+				if ($type) {
+					$row['type'] = $type->toArray();
+				}
+        $result[$row['id']] = $row;
+      } else {
+        //invalid input because no there is no form processor
+        CRM_FormProcessor_BAO_FormProcessorInput::deleteWithId($row['id']);
+      }
+    }
+    return $result;
+  }
+
+  /**
+   * Function to add or update form processor input
+   * 
+   * @param array $params 
+   * @return array $result
+   * @access public
+   * @throws Exception when params is empty
+   * @static
+   */
+  public static function add($params) {
+    $result = array();
+    if (empty($params)) {
+      throw new Exception('Params can not be empty when adding or updating a form processor input');
+    }
+		
+		if (!empty($params['id'])) {
+      CRM_Utils_Hook::pre('edit', 'FormProcessorInput', $params['id'], $params);
+    }
+    else {
+      CRM_Utils_Hook::pre('create', 'FormProcessorInput', NULL, $params);
+    }
+		
+    $input = new CRM_FormProcessor_BAO_FormProcessorInput();
+    $fields = self::fields();
+    foreach ($params as $key => $value) {
+      if (isset($fields[$key])) {
+        $input->$key = $value;
+      }
+    }
+    $input->save();
+    self::storeValues($input, $result);
+		
+		if (!empty($params['id'])) {
+      CRM_Utils_Hook::post('edit', 'FormProcessorInput', $input->id, $input);
+    }
+    else {
+      CRM_Utils_Hook::post('create', 'FormProcessorInput', $input->id, $input);
+    }
+		
+    return $result;
+  }
+
+  /**
+   * Function to delete a form processor input with id
+   * 
+   * @param int $id
+   * @throws Exception when $id is empty
+   * @access public
+   * @static
+   */
+  public static function deleteWithId($id) {
+    if (empty($id)) {
+      throw new Exception('id can not be empty when attempting to delete a form processor input');
+    }
+		
+		CRM_Utils_Hook::pre('delete', 'FormProcessorInput', $id, CRM_Core_DAO::$_nullArray);
+		
+    $input = new CRM_FormProcessor_BAO_FormProcessorInput();
+    $input->id = $id;
+    if ($input->find(true)) {
+      $input->delete();
+    }
+		
+		CRM_Utils_Hook::post('delete', 'FormProcessorInput', $id, CRM_Core_DAO::$_nullArray);
+		
+    return;
+  }
+
+	/**
+   * Function to delete all inputs with a form processor id
+   *
+   * @param int $formProcessorId
+   * @access public
+   * @static
+   */
+  public static function deleteWithFormProcessorId($formProcessorId) {
+    $input = new CRM_FormProcessor_BAO_FormProcessorInput();
+    $input->form_processor_id = $formProcessorId;
+    $input->find(FALSE);
+    while ($input->fetch()) {
+      CRM_Utils_Hook::pre('delete', 'FormProcessorInput', $input->id, CRM_Core_DAO::$_nullArray);
+		
+	    $input->id = $id;
+      $input->delete();
+			
+			CRM_Utils_Hook::post('delete', 'FormProcessorInput', $input->id, CRM_Core_DAO::$_nullArray);
+    }
+  }
+	
+	
+}
\ No newline at end of file
diff --git a/CRM/FormProcessor/DAO/FormProcessorInput.php b/CRM/FormProcessor/DAO/FormProcessorInput.php
new file mode 100644
index 0000000000000000000000000000000000000000..88573225f7aff39451df924eb5bb15b5cbf33e65
--- /dev/null
+++ b/CRM/FormProcessor/DAO/FormProcessorInput.php
@@ -0,0 +1,120 @@
+<?php
+
+use CRM_FormProcessor_ExtensionUtil as E;
+
+/**
+ * @author Jaap Jansma (CiviCooP) <jaap.jansma@civicoop.org>
+ * @license http://www.gnu.org/licenses/agpl-3.0.html
+ */
+class CRM_FormProcessor_DAO_FormProcessorInput extends CRM_Core_DAO {
+  /**
+   * static instance to hold the field values
+   *
+   * @var array
+   * @static
+   */
+  static $_fields = null;
+  static $_export = null;
+  /**
+   * empty definition for virtual function
+   */
+  static function getTableName() {
+    return 'civicrm_form_processor_input';
+  }
+  /**
+   * returns all the column names of this table
+   *
+   * @access public
+   * @return array
+   */
+  public static function &fields() {
+    if (!(self::$_fields)) {
+      self::$_fields = array(
+        'id' => array(
+          'name' => 'id',
+          'title' => E::ts('ID'),
+          'type' => CRM_Utils_Type::T_INT,
+          'required' => true
+        ),
+        'form_processor_id' => array(
+          'name' => 'form_processor_id',
+          'title' => E::ts('Form Processor ID'),
+          'type' => CRM_Utils_Type::T_INT,
+          'required' => true,
+          'FKApiName' => 'FormProcessor',
+        ),
+        'name' => array(
+          'name' => 'name',
+          'title' => E::ts('Name'),
+          'type' => CRM_Utils_Type::T_STRING,
+          'maxlength' => 80,
+          'required' => true,
+        ) ,
+        'type' => array(
+          'name' => 'type',
+          'title' => E::ts('Type'),
+          'type' => CRM_Utils_Type::T_STRING,
+          'maxlength' => 80,
+          'required' => true,
+        ),
+        'is_required' => array(
+          'name' => 'is_required',
+          'title' => E::ts('Is required'),
+          'type' => CRM_Utils_Type::T_INT,
+        ),
+        'default_value' => array(
+          'name' => 'default_value',
+          'title' => E::ts('Default Value'),
+          'type' => CRM_Utils_Type::T_STRING,
+          'maxlength' => 256,
+          'required' => true,
+        ),
+      );
+    }
+    return self::$_fields;
+  }
+  /**
+   * Returns an array containing, for each field, the array key used for that
+   * field in self::$_fields.
+   *
+   * @access public
+   * @return array
+   */
+  public static function &fieldKeys() {
+    if (!(self::$_fieldKeys)) {
+      self::$_fieldKeys = array(
+        'id' => 'id', 
+        'form_processor_id' => 'form_processor_id',
+        'name' => 'name',
+        'type' => 'type',
+        'is_required' => 'is_required',
+        'default_value' => 'default_value',
+      );
+    }
+    return self::$_fieldKeys;
+  }
+  /**
+   * returns the list of fields that can be exported
+   *
+   * @access public
+   * return array
+   * @static
+   */
+  static function &export($prefix = false)
+  {
+    if (!(self::$_export)) {
+      self::$_export = array();
+      $fields = self::fields();
+      foreach($fields as $name => $field) {
+        if (CRM_Utils_Array::value('export', $field)) {
+          if ($prefix) {
+            self::$_export['activity'] = & $fields[$name];
+          } else {
+            self::$_export[$name] = & $fields[$name];
+          }
+        }
+      }
+    }
+    return self::$_export;
+  }
+}
\ No newline at end of file
diff --git a/Civi/FormProcessor/API/Provider.php b/Civi/FormProcessor/API/Provider.php
index 77f6f7693d567577d8e0d47e1e9cf34cb17619fe..ca9a210e8abb08b0f2cc943af08762441f7f9473 100644
--- a/Civi/FormProcessor/API/Provider.php
+++ b/Civi/FormProcessor/API/Provider.php
@@ -8,7 +8,11 @@
  
  use \Civi\API\Provider\ProviderInterface as API_ProviderInterface;
 
- 
+/**
+ * This class connects the enabled form processors to 
+ * the the api interface of CiviCRM. That way the form processors 
+ * could be executed over the api.
+ */ 
  class Provider implements API_ProviderInterface {
  	
 	public function __construct() {
@@ -34,7 +38,7 @@
    */
   public function getEntityNames($version) {
   	return array(
-  		'FormProcessorExecuter'
+  		'FormProcessorExecutor'
 		);
   }
 
diff --git a/Civi/FormProcessor/Type/AbstractType.php b/Civi/FormProcessor/Type/AbstractType.php
new file mode 100644
index 0000000000000000000000000000000000000000..f1312b1f5525b5104abc2eadc447d7c11864045b
--- /dev/null
+++ b/Civi/FormProcessor/Type/AbstractType.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @author Jaap Jansma (CiviCooP) <jaap.jansma@civicoop.org>
+ * @license http://www.gnu.org/licenses/agpl-3.0.html
+ */
+ 
+ namespace Civi\FormProcessor\Type;
+ 
+ abstract class AbstractType {
+ 	
+	protected $name;
+	
+	protected $label;
+	
+	public function __construct($name, $label) {
+		$this->name = $name;
+		$this->label = $label;
+	}
+	
+	/**
+	 * Returns whether the provided value is a valid
+	 * 
+	 * @param mixed $value
+	 * @return bool
+	 */
+	abstract public function isValueValidForType($value);
+	
+	/**
+	 * Returns the name of the type.
+	 * 
+	 * @return string
+	 */
+	public function getName() {
+		return $this->name;
+	}
+	
+	/**
+	 * Returns the label of the type.
+	 * 
+	 * @return string
+	 */
+	public function getLabel() {
+		return $this->label;
+	}
+	
+	public function toArray() {
+		return array(
+			'name' => $this->name,
+			'label' => $this->label,
+		);
+	}
+	
+ }
diff --git a/Civi/FormProcessor/Type/Factory.php b/Civi/FormProcessor/Type/Factory.php
new file mode 100644
index 0000000000000000000000000000000000000000..1151d9e1270eb26354895acf098f685b4561c9a8
--- /dev/null
+++ b/Civi/FormProcessor/Type/Factory.php
@@ -0,0 +1,76 @@
+<?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\Type\AbstractType;
+ use \Civi\FormProcessor\Type\GenericType;
+ 
+ use CRM_FormProcessor_ExtensionUtil as E;
+ 
+ class Factory {
+ 	
+	/**
+	 * @var array<AbstractType>
+	 */
+	protected $types = array();
+	
+	public function __construct() {
+		$this->addType(new GenericType('Integer', E::ts('Numeric (no-decimal)')));
+		$this->addType(new GenericType('Float', E::ts('Numeric (with-decimal)')));
+		$this->addType(new GenericType('String', E::ts('Short text')));
+		$this->addType(new GenericType('Text', E::ts('Long text')));
+		$this->addType(new GenericType('Date', E::ts('Date')));
+		$this->addType(new GenericType('Time', E::ts('Time')));
+		$this->addType(new GenericType('Boolean', E::ts('Yes/No')));
+		$this->addType(new GenericType('Email', E::ts('E-mail')));
+	}
+	
+	/**
+	 * Add a type
+	 * 
+	 * @param AbstractType $type
+	 */
+	public function addType(AbstractType $type) {
+		$this->types[$type->getName()] = $type;
+		return $this;
+	}
+	
+	/**
+	 * Return the type
+	 * 
+	 * @param string $name
+	 * @return AbstractType|null
+	 */
+	public function getTypeByName($name) {
+		if (isset($this->types[$name])) {
+			return $this->types[$name];
+		}
+		return null;
+	}
+	
+	/**
+	 * @return array<AbstractType>
+	 */
+	public function getTypes() {
+		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/GenericType.php b/Civi/FormProcessor/Type/GenericType.php
new file mode 100644
index 0000000000000000000000000000000000000000..9c569ddf2bc0ff7f2788249d99dee3cdbb814e21
--- /dev/null
+++ b/Civi/FormProcessor/Type/GenericType.php
@@ -0,0 +1,21 @@
+<?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\Type\AbstractType;
+ 
+ class GenericType extends AbstractType {
+ 	
+	public function isValueValidForType($value) {
+		if (\CRM_Utils_Type::validate($value, $this->name, false) === NULL) {
+			return false;
+		}
+		return true;
+	}
+	
+ }
diff --git a/ang/form_processor.ang.php b/ang/form_processor.ang.php
index 735fa16be0bbff828e68c1e1e867d4d67a9fb040..a49e2aa66a5f902b576fc7e5559f79128f2a3954 100644
--- a/ang/form_processor.ang.php
+++ b/ang/form_processor.ang.php
@@ -20,5 +20,6 @@ return array (
   ),
   'settings' => 
   array (
+  	'inputTypes' => \Civi::service('form_processor_type_factory')->getTypeLabels(),
   ),
 );
diff --git a/ang/form_processor/AddInputDialogCtrl.html b/ang/form_processor/AddInputDialogCtrl.html
new file mode 100644
index 0000000000000000000000000000000000000000..24b22c08431a2234259c69014a569361e13f95b8
--- /dev/null
+++ b/ang/form_processor/AddInputDialogCtrl.html
@@ -0,0 +1,45 @@
+<div ng-controller="InputDialogCtrl" class="">
+  <div class="crm-block" ng-form="addInputForm" crm-ui-id-scope>
+    <div class="crm-group">
+    	
+    	<div crm-ui-field="{name: 'formProcessorDetailForm.type', title: ts('Type')}">
+    		<div>{{model.input.type.label}}</div>
+  		</div>
+    	
+    	<div crm-ui-field="{name: 'addInputForm.name', title: ts('Name')}">
+      <input
+        crm-ui-id="addInputForm.name"
+        type="text"
+        name="name"
+        ng-model="model.input.name"
+        class="big crm-form-text"
+        required
+        />
+        <div ng-show="invalidName">
+        	<em>{{ts('WARNING: The name of the formprocessor is invalid (Only lowercase characters and _ are allowed).')}}</em>
+      	</div>
+      	<div ng-show="nameExists">
+        	<em>{{ts('WARNING: The name of the formprocessor already exists.')}}</em>
+      	</div>
+	    </div>
+	
+			<div crm-ui-field="{title: ts('Is required?')}">
+	      <input name="is_required" type="checkbox" ng-model="model.input.is_required" ng-true-value="'1'" ng-false-value="'0'"/>
+	 		</div>
+	 		
+	 		<div crm-ui-field="{name: 'addInputForm.default_value', title: ts('Default value')}">
+	      <input
+	        crm-ui-id="addInputForm.default_value"
+	        type="text"
+	        name="title"
+	        ng-model="model.input.default_value"
+	        class="big crm-form-text"
+	        />
+	    </div>
+
+			<button crm-icon="fa-check" ng-click="saveClick()">{{ts('Add input')}}</button>
+			<button crm-icon="fa-times" ng-click="cancelClick()">{{ts('Cancel')}}</button>
+			
+    </div>
+  </div>
+</div>
diff --git a/ang/form_processor/EditInputDialogCtrl.html b/ang/form_processor/EditInputDialogCtrl.html
new file mode 100644
index 0000000000000000000000000000000000000000..47faf5872824d12064f4e6f34363899019c5f7f2
--- /dev/null
+++ b/ang/form_processor/EditInputDialogCtrl.html
@@ -0,0 +1,45 @@
+<div ng-controller="InputDialogCtrl" class="">
+  <div class="crm-block" ng-form="editInputForm" crm-ui-id-scope>
+    <div class="crm-group">
+    	
+    	<div crm-ui-field="{name: 'formProcessorDetailForm.type', title: ts('Type')}">
+    		<div>{{model.input.type.label}}</div>
+  		</div>
+    	
+    	<div crm-ui-field="{name: 'editInputForm.name', title: ts('Name')}">
+      <input
+        crm-ui-id="editInputForm.name"
+        type="text"
+        name="name"
+        ng-model="model.input.name"
+        class="big crm-form-text"
+        required
+        />
+        <div ng-show="invalidName">
+        	<em>{{ts('WARNING: The name of the formprocessor is invalid (Only lowercase characters and _ are allowed).')}}</em>
+      	</div>
+      	<div ng-show="nameExists">
+        	<em>{{ts('WARNING: The name of the formprocessor already exists.')}}</em>
+      	</div>
+	    </div>
+	
+			<div crm-ui-field="{title: ts('Is required?')}">
+	      <input name="is_required" type="checkbox" ng-model="model.input.is_required" ng-true-value="'1'" ng-false-value="'0'"/>
+	 		</div>
+	 		
+	 		<div crm-ui-field="{name: 'editInputForm.default_value', title: ts('Default value')}">
+	      <input
+	        crm-ui-id="editInputForm.default_value"
+	        type="text"
+	        name="title"
+	        ng-model="model.input.default_value"
+	        class="big crm-form-text"
+	        />
+	    </div>
+
+			<button crm-icon="fa-check" ng-click="saveClick()">{{ts('Update input')}}</button>
+			<button crm-icon="fa-times" ng-click="cancelClick()">{{ts('Cancel')}}</button>
+			
+    </div>
+  </div>
+</div>
diff --git a/ang/form_processor/FormProcessors.js b/ang/form_processor/FormProcessors.js
index 28f46dbeafb8f6f703c305b1a7acceb9097d6a98..b5bd75912fa0e22559e9a810709d19f613894860 100644
--- a/ang/form_processor/FormProcessors.js
+++ b/ang/form_processor/FormProcessors.js
@@ -13,7 +13,7 @@
           }
         }
       });
-      $routeProvider.when('/formProcessor/new', {
+      /*$routeProvider.when('/formProcessor/new', {
         templateUrl: '~/form_processor/edit.html',
         controller: 'FormProcessorCtrl',
         resolve: {
@@ -25,28 +25,33 @@
           	};
           }
         }
-      });
+      });*/
       $routeProvider.when('/formProcessor/:id', {
         templateUrl: '~/form_processor/edit.html',
         controller: 'FormProcessorCtrl',
         resolve: {
-        	formProcessor: function($route, crmApi) {
-          	return crmApi('FormProcessor', 'getsingle', {
-          		id: $route.current.params.id,
-          		options: {limit: 0}
-        		});
-          }
+        	apiCalls: function($route, crmApi) {
+        		var reqs = {};
+            if ($route.current.params.id !== 'new') {
+              reqs.formProcessor = ['FormProcessor', 'getsingle', {
+                id: $route.current.params.id
+              }];
+            }
+            return crmApi(reqs);
+         	}
         }
       });
     }
   );
   
-  angular.module('form_processor').controller('FormProcessorCtrl', function($scope, crmApi, formProcessor) {
+  angular.module('form_processor').controller('FormProcessorCtrl', function($scope, dialogService, crmApi, apiCalls) {
   	var ts = $scope.ts = CRM.ts(null);
 
-    $scope.formProcessor = formProcessor;	
+    $scope.formProcessor = apiCalls.formProcessor; 
     $scope.locks = {name: true};
     $scope.isNameValid = false;
+    $scope.inputTypes = CRM.form_processor.inputTypes;
+    $scope.deletedInputs = [];
     
     $scope.$watch('formProcessor.name', function(newFormProcessorName, oldFrmProcessorName) {
     	// Watch for changes in the name field
@@ -75,10 +80,69 @@
       result.then(function(data) {
         if (data.is_error === 0 || data.is_error == '0') {
           $scope.formProcessor.id = data.id;
+          
+          angular.forEach($scope.deletedInputs, function(input, key) {
+          	if (input.id) {          	
+          		crmApi('FormProcessorInput', 'delete', {'id': input.id}, true);
+          	}
+          });
+          
+          angular.forEach($scope.formProcessor.inputs, function(input, key) {
+          	input.form_processor_id = $scope.formProcessor.id; 
+          	input.type = input.type.name;
+          	crmApi('FormProcessorInput', 'create', input, true);
+          });
           window.location.href = '#/formProcessors';
         }
       });
     };
+    
+    $scope.removeInput = function removeInput(input) {
+    	var index = $scope.formProcessor.inputs.indexOf(input);
+    	if (index >= 0) {
+    		$scope.formProcessor.inputs.splice(index, 1);
+    		$scope.deletedInputs.push(input);
+    	}
+    };
+    
+     // Open a dialog for adding an input
+    $scope.addInput = function addInput(inputType) {    	    	
+      var options = CRM.utils.adjustDialogDefaults({
+        autoOpen: false,
+        width: '40%',
+        height: 'auto',
+        title: ts('Add input')
+      });
+      var model = {
+      	formProcessor: $scope.formProcessor,
+      	input: {
+      		type: inputType,
+        	'name': '',
+    			'is_required': 0,
+    			'default_value': '',
+      	}	
+      };
+      dialogService.open('InputDialog', '~/form_processor/AddInputDialogCtrl.html', model, options)
+      .then(
+			function(data) {
+				$scope.formProcessor.inputs.push(data.input);
+			});
+    };
+    
+    // Open a dialog for editting an input
+    $scope.editInput = function editInput(input) {    	    	
+      var options = CRM.utils.adjustDialogDefaults({
+        autoOpen: false,
+        width: '40%',
+        height: 'auto',
+        title: ts('Add input')
+      });
+      var model = {
+      	formProcessor: $scope.formProcessor,
+      	input: input
+      };
+      dialogService.open('InputDialog', '~/form_processor/EditInputDialogCtrl.html', model, options);
+    };
   });
   
   angular.module('form_processor').controller('FormProcessorListCtrl', function($scope, crmApi, formProcessors) {
diff --git a/ang/form_processor/InputDialogCtrl.js b/ang/form_processor/InputDialogCtrl.js
new file mode 100644
index 0000000000000000000000000000000000000000..b24ab2b4f070d650cd0199fe4e50158d4114d11f
--- /dev/null
+++ b/ang/form_processor/InputDialogCtrl.js
@@ -0,0 +1,32 @@
+(function(angular, $, _) {
+
+  angular.module('form_processor').controller('InputDialogCtrl', function InputDialogCtrl($scope, dialogService) {
+    $scope.ts = CRM.ts(null);
+    
+    $scope.invalidName = false;
+    $scope.nameExists = false;
+    
+    $scope.$watch('model.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.formProcessor.inputs, function(input, key) {
+    			if (input != $scope.model.input && input.name == newName) {
+    				$scope.nameExists = true;
+    			}
+    		});
+    	}
+    }, true);
+    
+    $scope.saveClick = function() {
+    	dialogService.close('InputDialog', $scope.model);
+    };
+		
+		$scope.cancelClick = function() {
+    	dialogService.cancel('InputDialog');
+    };
+    
+  });
+
+})(angular, CRM.$, CRM._);
diff --git a/ang/form_processor/edit.html b/ang/form_processor/edit.html
index df6ba1601aff059d482f93ceef1b6aa3b48dad21..9e59cfaa4746fff0a4eeae8ec6da3cdd21ffb65d 100644
--- a/ang/form_processor/edit.html
+++ b/ang/form_processor/edit.html
@@ -10,6 +10,8 @@ Required vars: formProcessor
 <div class="crm-block crm-form-block crmFormProcessor">
 
   <div ng-include="'~/form_processor/formProcessorDetails.html'"></div>
+  
+  <div ng-include="'~/form_processor/formProcessorInputTable.html'"></div>
 
   <div class="crm-submit-buttons">
     <button crm-icon="fa-check" ng-click="editFormProcessorForm.$setPristine(); save()" ng-disabled="editFormProcessorForm.$invalid || !isNameValid">
diff --git a/ang/form_processor/formProcessorInputTable.html b/ang/form_processor/formProcessorInputTable.html
new file mode 100644
index 0000000000000000000000000000000000000000..b7832478e0f916fece475d0251ccfd1099b20112
--- /dev/null
+++ b/ang/form_processor/formProcessorInputTable.html
@@ -0,0 +1,46 @@
+<!--
+Controller: FormProcessorCtrl
+Required vars: formProcessor
+-->
+<div class="crm-block" ng-form="formProcessorInputsForm" crm-ui-id-scope>
+	<h3>{{ts('Inputs')}}</h3>
+<table>
+  <thead>
+	  <tr>
+	    <th>{{ts('Name')}}</th>
+	    <th>{{ts('Type')}}</th>
+	    <th>{{ts('Is required')}}</th>
+	    <th>{{ts('Default value')}}</th>
+	    <th></th>
+	  </tr>
+  </thead>
+  <tbody>
+	  <tr ng-repeat="input in formProcessor.inputs | orderBy:'name'" ng-class-even="'crm-entity even-row even'" ng-class-odd="'crm-entity odd-row odd'">
+	    <td>{{input.name}}</td>
+	    <td>{{input.type.label}}</td>
+	    <td>{{input.is_required == 1 ? ts('Yes') : ts('No')}}</td>
+	    <td>{{input.default_value}}</td>
+	    <td>
+	      <a crm-icon="fa-edit" class="crm-hover-button" ng-click="editInput(input)" title="{{ts('Edit')}}">{{ts('Edit')}}</a>
+	      <a crm-icon="fa-trash" class="crm-hover-button" ng-click="removeInput(input)" title="{{ts('Remove')}}">{{ts('Remove')}}</a>
+	    </td>
+	  </tr>
+  </tbody>
+
+  <tfoot>
+	  <tr class="addRow">
+	    <td colspan="4">
+	    	<select
+        crm-ui-id="formProcessorInputsForm.type"
+        name="type"
+        ui-jq="select2"
+        ui-options="{dropdownAutoWidth : true, allowClear: true}"
+        ng-model="inputType"
+        ng-options="inputType.label for inputType in inputTypes">
+        <option value="">{{ts('- Select input type -')}}</option>
+      </select>
+      <button crm-icon="fa-check" ng-click="addInput(inputType)">{{ts('Add input')}}</button>	    	
+	    </td>
+	  </tr>
+  </tfoot>
+</table>
\ No newline at end of file
diff --git a/api/v3/FormProcessor/Create.php b/api/v3/FormProcessor/Create.php
index 0e0a465f153ba414b1448a9b39e76df577e84ba9..181fee3752d9972a8dfe36add83fcf3137d55574 100644
--- a/api/v3/FormProcessor/Create.php
+++ b/api/v3/FormProcessor/Create.php
@@ -3,7 +3,7 @@
 use CRM_FormProcessor_ExtensionUtil as E;
 
 /**
- * CiviRuleRule.Create API specification (optional)
+ * FormProcessor.Create API specification (optional)
  * This is used for documentation and validation.
  *
  * @param array $spec description of fields supported by this API call
@@ -40,7 +40,7 @@ function _civicrm_api3_form_processor_create_spec(&$spec) {
 }
 
 /**
- * CiviRuleRule.Create API
+ * FormProcessor.Create API
  *
  * @param array $params
  * @return array API result descriptor
@@ -66,7 +66,8 @@ function civicrm_api3_form_processor_create($params) {
     $params['created_date'] = date('Ymd');
     $params['created_user_id'] = $userId;
   }
-  $returnValues = CRM_FormProcessor_BAO_FormProcessor::add($params);
+  $returnValue = CRM_FormProcessor_BAO_FormProcessor::add($params);
+	$returnValues[$returnValue['id']] = $returnValue;
   return civicrm_api3_create_success($returnValues, $params, 'FormProcessor', 'Create');
 }
 
diff --git a/api/v3/FormProcessor/Delete.php b/api/v3/FormProcessor/Delete.php
index f509d487785c9a2f8bd19e98e93ef7e316c703f1..ac417589a3a134b537543b1755ef86450b988c12 100644
--- a/api/v3/FormProcessor/Delete.php
+++ b/api/v3/FormProcessor/Delete.php
@@ -3,7 +3,7 @@
 use CRM_FormProcessor_ExtensionUtil as E;
 
 /**
- * CiviRuleRule.Delete API specification (optional)
+ * FormProcessor.Delete API specification (optional)
  * This is used for documentation and validation.
  *
  * @param array $spec description of fields supported by this API call
@@ -19,7 +19,7 @@ function _civicrm_api3_form_processor_Delete_spec(&$spec) {
 }
 
 /**
- * CiviRuleRule.Delete API
+ * FormProcessor.Delete API
  *
  * @param array $params
  * @return array API result descriptor
diff --git a/api/v3/FormProcessor/Get.php b/api/v3/FormProcessor/Get.php
index ff1e2a0c74e9d1b641605ee2f7fe43aa706e9343..43bf53e8f8a83bd203f1d0b0372c92b5d268e7a8 100644
--- a/api/v3/FormProcessor/Get.php
+++ b/api/v3/FormProcessor/Get.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * CiviRuleRule.Get API
+ * FormProcessorInput.Get API
  *
  * @param array $params
  * @return array API result descriptor
@@ -14,7 +14,7 @@ function civicrm_api3_form_processor_get($params) {
 }
 
 /**
- * CiviRuleRule.Create API specification (optional)
+ * FormProcessorInput.Get API specification (optional)
  * This is used for documentation and validation.
  *
  * @param array $spec description of fields supported by this API call
diff --git a/api/v3/FormProcessor/Validatename.php b/api/v3/FormProcessor/Validatename.php
index 23df90982f7f55f9d34f9b525454506b5f572c73..f21098d3030f857145ba252f3fa460b31b57964b 100644
--- a/api/v3/FormProcessor/Validatename.php
+++ b/api/v3/FormProcessor/Validatename.php
@@ -3,7 +3,7 @@
 use CRM_FormProcessor_ExtensionUtil as E;
 
 /**
- * CiviRuleRule.Get API
+ * FormProcessor.Validatename API
  *
  * @param array $params
  * @return array API result descriptor
@@ -20,7 +20,7 @@ function civicrm_api3_form_processor_validatename($params) {
 }
 
 /**
- * CiviRuleRule.Create API specification (optional)
+ * FormProcessor.Validatename API specification (optional)
  * This is used for documentation and validation.
  *
  * @param array $spec description of fields supported by this API call
diff --git a/api/v3/FormProcessorInput/Create.php b/api/v3/FormProcessorInput/Create.php
new file mode 100644
index 0000000000000000000000000000000000000000..833fa22f0392c6916a63942316b45ae951b27298
--- /dev/null
+++ b/api/v3/FormProcessorInput/Create.php
@@ -0,0 +1,63 @@
+<?php
+
+use CRM_FormProcessor_ExtensionUtil as E;
+
+/**
+ * FormProcessorInput.Create API specification (optional)
+ * This is used for documentation and validation.
+ *
+ * @param array $spec description of fields supported by this API call
+ * @return void
+ * @see http://wiki.civicrm.org/confluence/display/CRM/API+Architecture+Standards
+ */
+function _civicrm_api3_form_processor_input_create_spec(&$spec) {
+  $spec['id'] = array(
+		'title' => E::ts('ID'),
+		'type' => CRM_Utils_Type::T_INT,
+		'api_required' => false
+	);
+	$spec['form_processor_id'] = array(
+		'title' => E::ts('Form Processor ID'),
+		'type' => CRM_Utils_Type::T_INT,
+		'api_required' => true,
+		'FKApiName' => 'FormProcessor',
+	);
+	$spec['name'] = array(
+		'title' => E::ts('Name'),
+		'type' => CRM_Utils_Type::T_STRING,
+		'api_required' => true
+	);
+	$spec['type'] = array(
+		'title' => E::ts('Type'),
+		'type' => CRM_Utils_Type::T_STRING,
+		'api_required' => true
+	);
+	$spec['is_required'] = array(
+		'title' => E::ts('Is required'),
+		'type' => CRM_Utils_Type::T_BOOLEAN,
+		'api_required' => false,
+		'api.default' => false,
+	);
+	$spec['default_value'] = array(
+		'title' => E::ts('Default Value'),
+		'type' => CRM_Utils_Type::T_STRING,
+		'api_required' => false
+	);
+}
+
+/**
+ * FormProcessorInput.Create API
+ *
+ * @param array $params
+ * @return array API result descriptor
+ * @see civicrm_api3_create_success
+ * @see civicrm_api3_create_error
+ *
+ *
+ */
+function civicrm_api3_form_processor_input_create($params) {
+  $returnValue = CRM_FormProcessor_BAO_FormProcessorInput::add($params);
+	$returnValues[$returnValue['id']] = $returnValue;
+  return civicrm_api3_create_success($returnValues, $params, 'FormProcessorInput', 'Create');
+}
+
diff --git a/api/v3/FormProcessorInput/Delete.php b/api/v3/FormProcessorInput/Delete.php
new file mode 100644
index 0000000000000000000000000000000000000000..86503957996227552c7f8070eca6bfbbf452a727
--- /dev/null
+++ b/api/v3/FormProcessorInput/Delete.php
@@ -0,0 +1,37 @@
+<?php
+
+use CRM_FormProcessor_ExtensionUtil as E;
+
+/**
+ * FormProcessorInput.Delete API specification (optional)
+ * This is used for documentation and validation.
+ *
+ * @param array $spec description of fields supported by this API call
+ * @return void
+ * @see http://wiki.civicrm.org/confluence/display/CRMDOC/API+Architecture+Standards
+ */
+function _civicrm_api3_form_processor_input_Delete_spec(&$spec) {
+  $spec['id'] = array(
+		'title' => E::ts('ID'),
+		'type' => CRM_Utils_Type::T_INT,
+		'api_required' => true
+	);
+}
+
+/**
+ * FormProcessorInput.Delete API
+ *
+ * @param array $params
+ * @return array API result descriptor
+ * @see civicrm_api3_create_success
+ * @see civicrm_api3_create_error
+ * @throws API_Exception
+ */
+function civicrm_api3_form_processor_input_Delete($params) {
+  if (!array_key_exists('id', $params) || empty($params['id'])) {
+    throw new API_Exception('Parameter id is mandatory and can not be empty in ' . __METHOD__, 0010);
+  } else {
+    return civicrm_api3_create_success(CRM_FormProcessor_BAO_FormProcessorInput::deleteWithId($params['id']), $params, 'FormProcessorInput', 'Delete');
+  }
+}
+
diff --git a/api/v3/FormProcessorInput/Get.php b/api/v3/FormProcessorInput/Get.php
new file mode 100644
index 0000000000000000000000000000000000000000..9a2cae6e59179895c3dd2905d5b6646335470867
--- /dev/null
+++ b/api/v3/FormProcessorInput/Get.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * FormProcessorInput.Get API
+ *
+ * @param array $params
+ * @return array API result descriptor
+ * @see civicrm_api3_create_success
+ * @see civicrm_api3_create_error
+ * @throws API_Exception
+ */
+function civicrm_api3_form_processor_input_get($params) {
+  $returnValues = CRM_FormProcessor_BAO_FormProcessorInput::getValues($params);
+  return civicrm_api3_create_success($returnValues, $params, 'FormProcessorInput', 'Get');
+}
+
+/**
+ * FormProcessorInput.Get API specification (optional)
+ * This is used for documentation and validation.
+ *
+ * @param array $spec description of fields supported by this API call
+ * @return void
+ * @see http://wiki.civicrm.org/confluence/display/CRM/API+Architecture+Standards
+ */
+function _civicrm_api3_form_processor_input_get_spec(&$spec) {
+	$fields = CRM_FormProcessor_BAO_FormProcessorInput::fields();
+	foreach($fields as $fieldname => $field) {
+		$spec[$fieldname] = $field;
+	}
+}
+
diff --git a/form_processor.php b/form_processor.php
index 8d0398a7eea4b83f1d44147befc62778138222bb..68425bf84151560995f71177bfefb7cf08205f49 100644
--- a/form_processor.php
+++ b/form_processor.php
@@ -3,12 +3,23 @@
 require_once 'form_processor.civix.php';
 use CRM_FormProcessor_ExtensionUtil as E;
 
-function form_processor_civicrm_container($container) {
+use \Symfony\Component\DependencyInjection\ContainerBuilder;
+use \Symfony\Component\DependencyInjection\Definition;
+
+/**
+ * Implements hook_civicrm_container()
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_container/
+ */
+function form_processor_civicrm_container(ContainerBuilder $container) {
+	// Register the TypeFactory
+	$container->setDefinition('form_processor_type_factory', new Definition('Civi\FormProcessor\Type\Factory'));
+	
+	
 	// Register our API Provider.
 	// The API provider is used to process incoming api calls and process them
 	// with the form processor logic.	
-	
-	$apiProviderDefinition = new \Symfony\Component\DependencyInjection\Definition('Civi\FormProcessor\API\Provider');
+	$apiProviderDefinition = new Definition('Civi\FormProcessor\API\Provider');
 	$apiKernelDefinition = $container->getDefinition('civi_api_kernel');
 	$apiKernelDefinition->addMethodCall('registerApiProvider', array($apiProviderDefinition)); 
 }
diff --git a/sql/create_civicrm_form_processor.sql b/sql/create_civicrm_form_processor.sql
index 0e171a8cf0a4dd077e88bfd50363caf3b8311d5d..aa39e963a6e2f8a9a46e7674687579473c4fcdf6 100644
--- a/sql/create_civicrm_form_processor.sql
+++ b/sql/create_civicrm_form_processor.sql
@@ -11,3 +11,14 @@ CREATE TABLE IF NOT EXISTS `civicrm_form_processor` (
   PRIMARY KEY (`id`),
   UNIQUE INDEX `name_UNIQUE` (`name` ASC)
 ) ENGINE = InnoDB;
+
+CREATE TABLE IF NOT EXISTS `civicrm_form_processor_input` (
+  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
+  `form_processor_id` INT UNSIGNED NOT NULL,
+  `name` VARCHAR(80) NOT NULL,
+  `type` VARCHAR(80) NOT NULL,
+  `is_required` TINYINT NULL DEFAULT 0,
+  `default_value` VARCHAR(256) NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE INDEX `name_id_UNIQUE` (`id`, `name` ASC)
+) ENGINE = InnoDB;
diff --git a/sql/uninstall.sql b/sql/uninstall.sql
index a715ab70b7a58875ad3fa93ba0a4ea3882d5a106..084e627b4aa60ae559c885db6e4bd4812501e588 100644
--- a/sql/uninstall.sql
+++ b/sql/uninstall.sql
@@ -1 +1,2 @@
+DROP TABLE `civicrm_form_processor_input`;
 DROP TABLE `civicrm_form_processor`;