diff --git a/CRM/Dataprocessor/Form/Field.php b/CRM/Dataprocessor/Form/Field.php index 268b9a98d29013d1caaa64628a35a1bcccca7953..f3c075244963d006e8adc93020bd5a28d3a5e4a0 100644 --- a/CRM/Dataprocessor/Form/Field.php +++ b/CRM/Dataprocessor/Form/Field.php @@ -22,12 +22,30 @@ class CRM_Dataprocessor_Form_Field extends CRM_Core_Form { private $field; + private $snippet; + + /** + * @var \Civi\DataProcessor\FieldOutputHandler\AbstractFieldOutputHandler + */ + private $outputHandlerClass; + + /** + * @var \Civi\DataProcessor\FieldOutputHandler\AbstractFieldOutputHandler[] + */ + private $outputHandlers; + /** * Function to perform processing before displaying form (overrides parent function) * * @access public */ function preProcess() { + $this->snippet = CRM_Utils_Request::retrieve('snippet', 'String'); + if ($this->snippet) { + $this->assign('suppressForm', TRUE); + $this->controller->_generateQFKey = FALSE; + } + $this->dataProcessorId = CRM_Utils_Request::retrieve('data_processor_id', 'Integer'); $this->assign('data_processor_id', $this->dataProcessorId); if ($this->dataProcessorId) { @@ -38,11 +56,23 @@ class CRM_Dataprocessor_Form_Field extends CRM_Core_Form { $this->id = CRM_Utils_Request::retrieve('id', 'Integer'); $this->assign('id', $this->id); + if ($this->id) { $this->field = civicrm_api3('DataProcessorField', 'getsingle', array('id' => $this->id)); $this->assign('field', $this->field); } + $this->outputHandlers = $this->dataProcessorClass->getAvailableOutputHandlers(); + + $type = CRM_Utils_Request::retrieve('type', 'String'); + if (!$type && $this->field) { + $type = $this->field['type']; + } + if ($type) { + $this->outputHandlerClass = $this->outputHandlers[$type]; + $this->assign('has_configuration', $this->outputHandlerClass->hasConfiguration()); + } + $title = E::ts('Data Processor Field'); CRM_Utils_System::setTitle($title); } @@ -54,10 +84,10 @@ class CRM_Dataprocessor_Form_Field extends CRM_Core_Form { $this->add('text', 'name', E::ts('Name'), array('size' => CRM_Utils_Type::HUGE), FALSE); $this->add('text', 'title', E::ts('Title'), array('size' => CRM_Utils_Type::HUGE), TRUE); - $outputHandlers = $this->dataProcessorClass->getAvailableOutputHandlers(); - foreach($outputHandlers as $outputHandler) { + foreach($this->outputHandlers as $outputHandler) { $outputHandlersSelect[$outputHandler->getName()] = $outputHandler->getTitle(); } + asort($outputHandlersSelect); $this->add('select', 'type', E::ts('Select Field'), $outputHandlersSelect, true, array( 'style' => 'min-width:250px', @@ -65,6 +95,11 @@ class CRM_Dataprocessor_Form_Field extends CRM_Core_Form { 'placeholder' => E::ts('- select -'), )); + if ($this->outputHandlerClass && $this->outputHandlerClass->hasConfiguration()) { + $this->outputHandlerClass->buildConfigurationForm($this, $this->field); + $this->assign('configuration_template', $this->outputHandlerClass->getConfigurationTemplateFileName()); + } + $this->addButtons(array( array('type' => 'next', 'name' => E::ts('Save'), 'isDefault' => TRUE,), array('type' => 'cancel', 'name' => E::ts('Cancel')))); @@ -125,6 +160,10 @@ class CRM_Dataprocessor_Form_Field extends CRM_Core_Form { $params['id'] = $this->id; } + if ($this->outputHandlerClass && $this->outputHandlerClass->hasConfiguration()) { + $params['configuration'] = $this->outputHandlerClass->processConfiguration($values); + } + civicrm_api3('DataProcessorField', 'create', $params); CRM_Utils_System::redirect($redirectUrl); diff --git a/Civi/DataProcessor/Event/OutputHandlerEvent.php b/Civi/DataProcessor/Event/OutputHandlerEvent.php index d81f1e33e4c4f5549241201c33acb274b797c3bd..c66e68655dce01f66d9e0e3a0e650519d2e0c780 100644 --- a/Civi/DataProcessor/Event/OutputHandlerEvent.php +++ b/Civi/DataProcessor/Event/OutputHandlerEvent.php @@ -6,8 +6,7 @@ namespace Civi\DataProcessor\Event; -use Civi\DataProcessor\DataSpecification\FieldSpecification; -use Civi\DataProcessor\Source\SourceInterface; +use Civi\DataProcessor\ProcessorType\AbstractProcessorType; use Symfony\Component\EventDispatcher\Event; class OutputHandlerEvent extends Event { @@ -15,23 +14,17 @@ class OutputHandlerEvent extends Event { const NAME = 'dataprocessor.outputhandler'; /** - * @var FieldSpecification + * @var \Civi\DataProcessor\ProcessorType\AbstractProcessorType */ - public $fieldSpecification; - - /** - * @var SourceInterface - */ - public $dataSource; + public $dataProcessor; /** * @var array */ public $handlers = array(); - public function __construct(FieldSpecification $field, SourceInterface $source) { - $this->fieldSpecification = $field; - $this->dataSource = $source; + public function __construct(AbstractProcessorType $dataProcessor) { + $this->dataProcessor = $dataProcessor; } } \ No newline at end of file diff --git a/Civi/DataProcessor/Event/SourceOutputHandlerEvent.php b/Civi/DataProcessor/Event/SourceOutputHandlerEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..39fb4410b55d907f2b14710f9ebf271bff4e8338 --- /dev/null +++ b/Civi/DataProcessor/Event/SourceOutputHandlerEvent.php @@ -0,0 +1,37 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +namespace Civi\DataProcessor\Event; + +use Civi\DataProcessor\DataSpecification\FieldSpecification; +use Civi\DataProcessor\Source\SourceInterface; +use Symfony\Component\EventDispatcher\Event; + +class SourceOutputHandlerEvent extends Event { + + const NAME = 'dataprocessor.source_outputhandler'; + + /** + * @var FieldSpecification + */ + public $fieldSpecification; + + /** + * @var SourceInterface + */ + public $dataSource; + + /** + * @var array + */ + public $handlers = array(); + + public function __construct(FieldSpecification $field, SourceInterface $source) { + $this->fieldSpecification = $field; + $this->dataSource = $source; + } + +} \ No newline at end of file diff --git a/Civi/DataProcessor/Factory.php b/Civi/DataProcessor/Factory.php index 0e90ba0628691aca6f72d51b74ab6159ada1691b..5237c836f0c02151ed78f8b98afe52c7530970b6 100644 --- a/Civi/DataProcessor/Factory.php +++ b/Civi/DataProcessor/Factory.php @@ -10,6 +10,8 @@ use Civi\DataProcessor\DataFlow\Sort\SortCompareFactory; use Civi\DataProcessor\DataSpecification\FieldSpecification; use Civi\DataProcessor\Event\FilterHandlerEvent; use Civi\DataProcessor\Event\OutputHandlerEvent; +use Civi\DataProcessor\Event\SourceOutputHandlerEvent; +use Civi\DataProcessor\FieldOutputHandler\ContactLinkFieldOutputHandler; use Civi\DataProcessor\FieldOutputHandler\FileFieldOutputHandler; use Civi\DataProcessor\FieldOutputHandler\OptionFieldOutputHandler; use Civi\DataProcessor\FieldOutputHandler\RawFieldOutputHandler; @@ -277,7 +279,7 @@ class Factory { } public function getOutputHandlers(FieldSpecification $field, SourceInterface $source) { - $event = new OutputHandlerEvent($field, $source); + $event = new SourceOutputHandlerEvent($field, $source); $rawOutputhandler = new RawFieldOutputHandler($field, $source); $event->handlers[$rawOutputhandler->getName()] = $rawOutputhandler; if ($field->getOptions()) { @@ -288,7 +290,7 @@ class Factory { $fileOutputHandler = new FileFieldOutputHandler($field, $source); $event->handlers[$fileOutputHandler->getName()] = $fileOutputHandler; } - $this->dispatcher->dispatch(OutputHandlerEvent::NAME, $event); + $this->dispatcher->dispatch(SourceOutputHandlerEvent::NAME, $event); return $event->handlers; } diff --git a/Civi/DataProcessor/FieldOutputHandler/AbstractFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/AbstractFieldOutputHandler.php index 37442dcbe6023a57623528157de5d169ab139d4f..9bb0bbea86f5998995e5de5baa1ef3935d3fd283 100644 --- a/Civi/DataProcessor/FieldOutputHandler/AbstractFieldOutputHandler.php +++ b/Civi/DataProcessor/FieldOutputHandler/AbstractFieldOutputHandler.php @@ -7,6 +7,7 @@ namespace Civi\DataProcessor\FieldOutputHandler; use Civi\DataProcessor\DataSpecification\FieldSpecification; +use Civi\DataProcessor\ProcessorType\AbstractProcessorType; abstract class AbstractFieldOutputHandler { @@ -87,5 +88,51 @@ abstract class AbstractFieldOutputHandler { return $this->outputFieldSpecification; } + /** + * Returns true when this handler has additional configuration. + * + * @return bool + */ + public function hasConfiguration() { + return false; + } + + /** + * 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()) { + // Example add a checkbox to the form. + // $form->add('checkbox', 'show_label', E::ts('Show label')); + } + + /** + * When this handler has configuration specify the template file name + * for the configuration form. + * + * @return false|string + */ + public function getConfigurationTemplateFileName() { + // Example return "CRM/FormFieldLibrary/Form/FieldConfiguration/TextField.tpl"; + return false; + } + + + /** + * Process the submitted values and create a configuration array + * + * @param $submittedValues + * @return array + */ + public function processConfiguration($submittedValues) { + // Add the show_label to the configuration array. + // $configuration['show_label'] = $submittedValues['show_label']; + // return $configuration; + return array(); + } + } \ No newline at end of file diff --git a/Civi/DataProcessor/FieldOutputHandler/ContactLinkFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/ContactLinkFieldOutputHandler.php new file mode 100644 index 0000000000000000000000000000000000000000..6b0ae5d993063d02a488ed79aa0edfc5383b89b2 --- /dev/null +++ b/Civi/DataProcessor/FieldOutputHandler/ContactLinkFieldOutputHandler.php @@ -0,0 +1,196 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +namespace Civi\DataProcessor\FieldOutputHandler; + +use Civi\DataProcessor\ProcessorType\AbstractProcessorType; +use CRM_Dataprocessor_ExtensionUtil as E; +use Civi\DataProcessor\Source\SourceInterface; +use Civi\DataProcessor\DataSpecification\FieldSpecification; +use Civi\DataProcessor\FieldOutputHandler\FieldOutput; + +class ContactLinkFieldOutputHandler extends AbstractFieldOutputHandler { + + /** + * @var \Civi\DataProcessor\Source\SourceInterface + */ + protected $dataSource; + + /** + * @var AbstractProcessorType + */ + protected $dataProcessor; + + /** + * @var SourceInterface + */ + protected $contactIdSource; + + /** + * @var FieldSpecification + */ + protected $contactIdField; + + /** + * @var SourceInterface + */ + protected $contactNameSource; + + /** + * @var FieldSpecification + */ + protected $contactNameField; + + + public function __construct(AbstractProcessorType $dataProcessor) { + $this->dataProcessor = $dataProcessor; + } + + /** + * Returns the name of the handler type. + * + * @return String + */ + public function getName() { + return 'contact_link'; + } + + /** + * Returns the data type of this field + * + * @return String + */ + protected function getType() { + return 'String'; + } + + /** + * Returns the title of this field + * + * @return String + */ + public function getTitle() { + return E::ts('Link to view contact'); + } + + /** + * Initialize the processor + * + * @param String $alias + * @param String $title + * @param array $configuration + * @param \Civi\DataProcessor\ProcessorType\AbstractProcessorType $processorType + */ + public function initialize($alias, $title, $configuration) { + $this->outputFieldSpecification = new FieldSpecification($this->getName(), 'String', $title, null, $alias); + $this->contactIdSource = $this->dataProcessor->getDataSourceByName($configuration['contact_id_datasource']); + $this->contactIdField = $this->contactIdSource->getAvailableFields()->getFieldSpecificationByName($configuration['contact_id_field']); + $this->contactIdSource->ensureFieldInSource($this->contactIdField); + + $this->contactNameSource = $this->dataProcessor->getDataSourceByName($configuration['contact_name_datasource']); + $this->contactNameField = $this->contactNameSource->getAvailableFields()->getFieldSpecificationByName($configuration['contact_name_field']); + $this->contactNameSource->ensureFieldInSource($this->contactNameField); + + //$this->dataSource->ensureFieldInSource($this->inputFieldSpec); + } + + /** + * Returns the formatted value + * + * @param $rawRecord + * @param $formattedRecord + * + * @return \Civi\DataProcessor\FieldOutputHandler\FieldOutput + */ + public function formatField($rawRecord, $formattedRecord) { + $contactId = $rawRecord[$this->contactIdField->alias]; + $contactname = $rawRecord[$this->contactNameField->alias]; + $url = \CRM_Utils_System::url('civicrm/contact/view', array( + 'reset' => 1, + 'cid' => $contactId, + )); + $link = '<a href="'.$url.'">'.$contactname.'</a>'; + $formattedValue = new FieldOutput($contactname); + $formattedValue->formattedValue = $link; + return $formattedValue; + } + + /** + * Returns true when this handler has additional configuration. + * + * @return bool + */ + public function hasConfiguration() { + return true; + } + + /** + * 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()) { + $fieldSelect = \CRM_Dataprocessor_Utils_DataSourceFields::getAvailableFieldsInDataSources($field['data_processor_id']); + + $form->add('select', 'contact_id_field', E::ts('Contact ID Field'), $fieldSelect, true, array( + 'style' => 'min-width:250px', + 'class' => 'crm-select2 huge', + 'placeholder' => E::ts('- select -'), + )); + $form->add('select', 'contact_name_field', E::ts('Contact Name Field'), $fieldSelect, true, array( + 'style' => 'min-width:250px', + 'class' => 'crm-select2 huge', + 'placeholder' => E::ts('- select -'), + )); + if (isset($field['configuration'])) { + $configuration = $field['configuration']; + $defaults = array(); + if (isset($configuration['contact_id_field']) && isset($configuration['contact_id_datasource'])) { + $defaults['contact_id_field'] = $configuration['contact_id_datasource'] . '::' . $configuration['contact_id_field']; + } + if (isset($configuration['contact_name_field']) && isset($configuration['contact_name_datasource'])) { + $defaults['contact_name_field'] = $configuration['contact_name_datasource'] . '::' . $configuration['contact_name_field']; + } + $form->setDefaults($defaults); + } + + // Example add a checkbox to the form. + // $form->add('checkbox', 'show_label', E::ts('Show label')); + } + + /** + * When this handler has configuration specify the template file name + * for the configuration form. + * + * @return false|string + */ + public function getConfigurationTemplateFileName() { + return "CRM/Dataprocessor/Form/Field/Configuration/ContactLinkFieldOutputHandler.tpl"; + } + + + /** + * Process the submitted values and create a configuration array + * + * @param $submittedValues + * @return array + */ + public function processConfiguration($submittedValues) { + list($contact_id_datasource, $contact_id_field) = explode('::', $submittedValues['contact_id_field'], 2); + $configuration['contact_id_field'] = $contact_id_field; + $configuration['contact_id_datasource'] = $contact_id_datasource; + list($contact_name_datasource, $contact_name_field) = explode('::', $submittedValues['contact_name_field'], 2); + $configuration['contact_name_field'] = $contact_name_field; + $configuration['contact_name_datasource'] = $contact_name_datasource; + return $configuration; + } + + + + +} \ No newline at end of file diff --git a/Civi/DataProcessor/ProcessorType/AbstractProcessorType.php b/Civi/DataProcessor/ProcessorType/AbstractProcessorType.php index ccae327a4cddfec9379d598078a3d20bd27643a6..9c3eba4b55e0d3f333930c657d1c2b2d325283e1 100644 --- a/Civi/DataProcessor/ProcessorType/AbstractProcessorType.php +++ b/Civi/DataProcessor/ProcessorType/AbstractProcessorType.php @@ -14,7 +14,9 @@ use Civi\DataProcessor\DataFlow\MultipleDataFlows\JoinInterface; use Civi\DataProcessor\DataFlow\MultipleDataFlows\JoinSpecification; use Civi\DataProcessor\DataFlow\SqlDataFlow; use Civi\DataProcessor\DataSpecification\FieldSpecification; +use Civi\DataProcessor\Event\OutputHandlerEvent; use Civi\DataProcessor\FieldOutputHandler\AbstractFieldOutputHandler; +use Civi\DataProcessor\FieldOutputHandler\ContactLinkFieldOutputHandler; use Civi\DataProcessor\FilterHandler\AbstractFilterHandler; use Civi\DataProcessor\Storage\StorageInterface; @@ -110,6 +112,7 @@ abstract class AbstractProcessorType { * @return \Civi\DataProcessor\FieldOutputHandler\AbstractFieldOutputHandler[] */ public function getAvailableOutputHandlers() { + $dispatcher = \Civi::dispatcher(); $factory = dataprocessor_get_factory(); $handlers = array(); foreach($this->dataSources as $dataSource) { @@ -118,6 +121,13 @@ abstract class AbstractProcessorType { $handlers = array_merge($handlers, $fieldHandlers); } } + + $handlers['contact_link'] = new ContactLinkFieldOutputHandler($this); + + $event = new OutputHandlerEvent($this); + $event->handlers = $handlers; + $dispatcher->dispatch(OutputHandlerEvent::NAME, $event); + return $handlers; } diff --git a/Civi/DataProcessor/Source/AbstractCivicrmEntitySource.php b/Civi/DataProcessor/Source/AbstractCivicrmEntitySource.php index 452e17b1a11bae988941e77f2ee721a93d36b46c..b6fc635c1ffbbd588c5f3379fe263953adbcd09e 100644 --- a/Civi/DataProcessor/Source/AbstractCivicrmEntitySource.php +++ b/Civi/DataProcessor/Source/AbstractCivicrmEntitySource.php @@ -15,6 +15,7 @@ use Civi\DataProcessor\DataFlow\MultipleDataFlows\SimpleJoin; use Civi\DataProcessor\DataSpecification\AggregationField; use Civi\DataProcessor\DataSpecification\CustomFieldSpecification; use Civi\DataProcessor\DataSpecification\DataSpecification; +use Civi\DataProcessor\DataSpecification\FieldExistsException; use Civi\DataProcessor\DataSpecification\FieldSpecification; use Civi\DataProcessor\DataSpecification\Utils as DataSpecificationUtils; use Civi\DataProcessor\ProcessorType\AbstractProcessorType; @@ -328,17 +329,25 @@ abstract class AbstractCivicrmEntitySource extends AbstractSource { * @throws \Exception */ public function ensureFieldInSource(FieldSpecification $fieldSpecification) { - if ($this->getAvailableFields()->doesFieldExist($fieldSpecification->name)) { - if ($fieldSpecification instanceof CustomFieldSpecification) { - $customGroupDataFlow = $this->ensureCustomGroup($fieldSpecification->customGroupTableName, $fieldSpecification->customGroupName); - if (!$customGroupDataFlow->getDataSpecification()->doesFieldExist($fieldSpecification->alias)) { - $customGroupDataFlow->getDataSpecification() + try { + if ($this->getAvailableFields() + ->doesFieldExist($fieldSpecification->name)) { + if ($fieldSpecification instanceof CustomFieldSpecification) { + $customGroupDataFlow = $this->ensureCustomGroup($fieldSpecification->customGroupTableName, $fieldSpecification->customGroupName); + if (!$customGroupDataFlow->getDataSpecification() + ->doesFieldExist($fieldSpecification->alias)) { + $customGroupDataFlow->getDataSpecification() + ->addFieldSpecification($fieldSpecification->alias, $fieldSpecification); + } + } + else { + $entityDataFlow = $this->ensureEntity(); + $entityDataFlow->getDataSpecification() ->addFieldSpecification($fieldSpecification->alias, $fieldSpecification); } - } else { - $entityDataFlow = $this->ensureEntity(); - $entityDataFlow->getDataSpecification()->addFieldSpecification($fieldSpecification->alias, $fieldSpecification); } + } catch (FieldExistsException $e) { + // Do nothing. } } diff --git a/templates/CRM/Dataprocessor/Form/Field.tpl b/templates/CRM/Dataprocessor/Form/Field.tpl index c46b6b8912c2a2d4244adb5c38356db18e4b18a8..903173976fb7537ee152c9b426409335a935bcd6 100644 --- a/templates/CRM/Dataprocessor/Form/Field.tpl +++ b/templates/CRM/Dataprocessor/Form/Field.tpl @@ -1,15 +1,15 @@ {crmScope extensionKey='dataprocessor'} -<div class="crm-submit-buttons"> - {include file="CRM/common/formButtons.tpl" location="top"} -</div> - {if $action eq 8} {* Are you sure to delete form *} <h3>{ts}Delete Field{/ts}</h3> <div class="crm-block crm-form-block crm-data-processor_label-block"> <div class="crm-section">{ts 1=$field->title}Are you sure to delete field '%1'?{/ts}</div> </div> -{else} + + <div class="crm-submit-buttons"> + {include file="CRM/common/formButtons.tpl" location="bottom"} + </div> +{elseif (!$snippet)} {* block for rule data *} <h3>{ts}Field{/ts}</h3> @@ -38,6 +38,16 @@ <div class="content">{$form.name.html}</div> <div class="clear"></div> </div> + + <div id="type_configuration"> + {if ($configuration_template)} + {include file=$configuration_template} + {/if} + </div> + </div> + + <div class="crm-submit-buttons"> + {include file="CRM/common/formButtons.tpl" location="bottom"} </div> <script type="text/javascript"> @@ -46,6 +56,14 @@ var id = {/literal}{if ($field)}{$field.id}{else}false{/if}{literal}; var data_processor_id = {/literal}{$data_processor_id}{literal}; + $('#type').on('change', function() { + var type = $('#type').val(); + if (type) { + var dataUrl = CRM.url('civicrm/dataprocessor/form/field', {type: type, 'data_processor_id': data_processor_id, 'id': id}); + CRM.loadPage(dataUrl, {'target': '#type_configuration'}); + } + }); + $('#title').on('blur', function() { var title = $('#title').val(); if ($('#nameSection').hasClass('hiddenElement') && !id) { @@ -61,9 +79,11 @@ }); {/literal} </script> +{else} + <div id="type_configuration"> + {if ($configuration_template)} + {include file=$configuration_template} + {/if} + </div> {/if} - -<div class="crm-submit-buttons"> - {include file="CRM/common/formButtons.tpl" location="bottom"} -</div> {/crmScope} \ No newline at end of file diff --git a/templates/CRM/Dataprocessor/Form/Field/Configuration/ContactLinkFieldOutputHandler.tpl b/templates/CRM/Dataprocessor/Form/Field/Configuration/ContactLinkFieldOutputHandler.tpl new file mode 100644 index 0000000000000000000000000000000000000000..cf0d0a996c2a9a4073ef63bc8a434ecde4ab9074 --- /dev/null +++ b/templates/CRM/Dataprocessor/Form/Field/Configuration/ContactLinkFieldOutputHandler.tpl @@ -0,0 +1,12 @@ +{crmScope extensionKey='dataprocessor'} + <div class="crm-section"> + <div class="label">{$form.contact_id_field.label}</div> + <div class="content">{$form.contact_id_field.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.contact_name_field.label}</div> + <div class="content">{$form.contact_name_field.html}</div> + <div class="clear"></div> + </div> +{/crmScope} \ No newline at end of file