diff --git a/Civi/DataProcessor/Factory.php b/Civi/DataProcessor/Factory.php index a69b8cee3d153c0d785d596c53fea6c3d86b5485..784e3ef4eeec9d66c99ffdbd08cb7c16233a4a2b 100644 --- a/Civi/DataProcessor/Factory.php +++ b/Civi/DataProcessor/Factory.php @@ -124,6 +124,7 @@ class Factory { $this->addOutput('participant_search', 'CRM_DataprocessorSearch_ParticipantSearch', E::ts('Participant Search')); $this->addOutput('export_csv', 'CRM_DataprocessorOutputExport_CSV', E::ts('CSV Export')); $this->addFilter('simple_sql_filter', 'Civi\DataProcessor\FilterHandler\SimpleSqlFilter', E::ts('Field filter')); + $this->addFilter('contact_in_group_filter', 'Civi\DataProcessor\FilterHandler\ContactInGroupFilter', E::ts('Contact in Group filter')); $this->addFilter('case_role_filter', 'Civi\DataProcessor\FilterHandler\CaseRoleFilter', E::ts('Case Role filter')); $this->addjoinType('simple_join', 'Civi\DataProcessor\DataFlow\MultipleDataFlows\SimpleJoin', E::ts('Select fields to join on')); $this->addjoinType('simple_non_required_join', 'Civi\DataProcessor\DataFlow\MultipleDataFlows\SimpleNonRequiredJoin', E::ts('Select fields to join on (not required)')); @@ -132,6 +133,7 @@ class Factory { $this->addOutputHandler('file_field', 'Civi\DataProcessor\FieldOutputHandler\FileFieldOutputHandler', E::ts('File download link')); $this->addOutputHandler('option_label', 'Civi\DataProcessor\FieldOutputHandler\OptionFieldOutputHandler', E::ts('Option label')); $this->addOutputHandler('case_roles', 'Civi\DataProcessor\FieldOutputHandler\CaseRolesFieldOutputHandler', E::ts('Case Roles')); + $this->addOutputHandler('groups_of_contact', 'Civi\DataProcessor\FieldOutputHandler\GroupsOfContactFieldOutputHandler', E::ts('Display the groups of a contact')); } diff --git a/Civi/DataProcessor/FieldOutputHandler/GroupsOfContactFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/GroupsOfContactFieldOutputHandler.php new file mode 100644 index 0000000000000000000000000000000000000000..34d63ea6615b3a665298dab5da1467a65f142e04 --- /dev/null +++ b/Civi/DataProcessor/FieldOutputHandler/GroupsOfContactFieldOutputHandler.php @@ -0,0 +1,191 @@ +<?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 GroupsOfContactFieldOutputHandler extends AbstractFieldOutputHandler { + + /** + * @var \Civi\DataProcessor\Source\SourceInterface + */ + protected $dataSource; + + /** + * @var SourceInterface + */ + protected $contactIdSource; + + /** + * @var FieldSpecification + */ + protected $contactIdField; + + /** + * @var FieldSpecification + */ + protected $outputFieldSpecification; + + /** + * @var int|false + */ + protected $parent_group_id; + + /** + * @return \Civi\DataProcessor\DataSpecification\FieldSpecification + */ + public function getOutputFieldSpecification() { + return $this->outputFieldSpecification; + } + + /** + * Returns the data type of this field + * + * @return String + */ + protected function getType() { + return 'String'; + } + + /** + * 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($alias, 'String', $title, null, $alias); + $this->contactIdSource = $this->dataProcessor->getDataSourceByName($configuration['datasource']); + $this->contactIdField = $this->contactIdSource->getAvailableFields()->getFieldSpecificationByName($configuration['field']); + $this->contactIdSource->ensureFieldInSource($this->contactIdField); + + $this->outputFieldSpecification = new FieldSpecification($this->contactIdField->name, 'String', $title, null, $alias); + + if (isset($configuration['parent_group']) && $configuration['parent_group']) { + $this->parent_group_id = civicrm_api3('Group', 'getvalue', array('return' => 'id', 'name' => $configuration['parent_group'])); + } + } + + /** + * Returns the formatted value + * + * @param $rawRecord + * @param $formattedRecord + * + * @return \Civi\DataProcessor\FieldOutputHandler\FieldOutput + */ + public function formatField($rawRecord, $formattedRecord) { + $contactId = $rawRecord[$this->contactIdField->alias]; + $sql = "SELECT g.title, g.id + FROM civicrm_group g + INNER JOIN civicrm_group_contact gc ON gc.group_id = g.id + WHERE gc.status = 'Added' AND gc.contact_id = %1"; + if ($this->parent_group_id) { + $childGroupIds = \CRM_Contact_BAO_GroupNesting::getDescendentGroupIds([$this->parent_group_id], FALSE); + $sql .= " AND gc.group_id IN (".implode(", ", $childGroupIds).")"; + } + $sql .= " ORDER BY g.title"; + $sqlParams[1] = array($contactId, 'Integer'); + $dao = \CRM_Core_DAO::executeQuery($sql, $sqlParams); + $rawValues = array(); + $formattedValues = array(); + while($dao->fetch()) { + $rawValues[] = array( + 'group_id' => $dao->id, + 'group' => $dao->title, + ); + $formattedValues[] = $dao->title; + } + $output = new FieldOutput($rawValues); + $output->formattedValue = implode(", ", $formattedValues); + return $output; + } + + /** + * 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']); + $groupsApi = civicrm_api3('Group', 'get', array('is_active' => 1, 'options' => array('limit' => 0))); + $groups = array(); + foreach($groupsApi['values'] as $group) { + $groups[$group['name']] = $group['title']; + } + + $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', 'parent_group', E::ts('Show only subgroup(s) of'), $groups, false, array( + 'style' => 'min-width:250px', + 'class' => 'crm-select2 huge', + 'placeholder' => E::ts('- Show all groups -'), + 'multiple' => false, + )); + + if (isset($field['configuration'])) { + $configuration = $field['configuration']; + $defaults = array(); + if (isset($configuration['field']) && isset($configuration['datasource'])) { + $defaults['contact_id_field'] = $configuration['datasource'] . '::' . $configuration['field']; + } + if (isset($configuration['parent_group'])) { + $defaults['parent_group'] = $configuration['parent_group']; + } + $form->setDefaults($defaults); + } + } + + /** + * 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/GroupsOfContactFieldOutputHandler.tpl"; + } + + + /** + * Process the submitted values and create a configuration array + * + * @param $submittedValues + * @return array + */ + public function processConfiguration($submittedValues) { + list($datasource, $field) = explode('::', $submittedValues['contact_id_field'], 2); + $configuration['field'] = $field; + $configuration['datasource'] = $datasource; + $configuration['parent_group'] = isset($submittedValues['parent_group']) ? $submittedValues['parent_group'] : false; + return $configuration; + } + + + + +} \ No newline at end of file diff --git a/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php b/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php new file mode 100644 index 0000000000000000000000000000000000000000..04f3e63095c43667773e47bf1a440e4f3bf0f6ee --- /dev/null +++ b/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php @@ -0,0 +1,226 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +namespace Civi\DataProcessor\FilterHandler; + +use Civi\DataProcessor\DataFlow\SqlDataFlow; +use Civi\DataProcessor\DataFlow\SqlTableDataFlow; +use Civi\DataProcessor\DataSpecification\CustomFieldSpecification; +use Civi\DataProcessor\DataSpecification\FieldSpecification; +use Civi\DataProcessor\Source\SourceInterface; +use CRM_Dataprocessor_ExtensionUtil as E; + +class ContactInGroupFilter extends AbstractFilterHandler { + + /** + * @var \Civi\DataProcessor\DataSpecification\FieldSpecification + */ + protected $fieldSpecification; + + /** + * @var SourceInterface + */ + protected $dataSource; + + /** + * @var array + */ + protected $parent_group_id = false; + + public function __construct() { + parent::__construct(); + } + + /** + * Initialize the processor + * + * @param String $alias + * @param String $title + * @param bool $is_required + * @param array $configuration + */ + public function initialize($alias, $title, $is_required, $configuration) { + if ($this->fieldSpecification) { + return; // Already initialized. + } + if (!isset($configuration['datasource']) || !isset($configuration['field'])) { + return; // Invalid configuration + } + + $this->is_required = $is_required; + + $this->dataSource = $this->data_processor->getDataSourceByName($configuration['datasource']); + if ($this->dataSource) { + $this->fieldSpecification = clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']); + $this->fieldSpecification->alias = $alias; + $this->fieldSpecification->title = $title; + } + + if (isset($configuration['parent_group']) && $configuration['parent_group']) { + $this->parent_group_id = civicrm_api3('Group', 'getvalue', array('return' => 'id', 'name' => $configuration['parent_group'])); + } + } + + /** + * @return \Civi\DataProcessor\DataSpecification\FieldSpecification + */ + public function getFieldSpecification() { + return $this->fieldSpecification; + } + + /** + * @param array $filter + * The filter settings + * @return mixed + */ + public function setFilter($filter) { + $dataFlow = $this->dataSource->ensureField($this->fieldSpecification->name); + $group_ids = $filter['value']; + if (!is_array($group_ids)) { + $group_ids = array($group_ids); + } + $groupTableAlias = 'civicrm_group_contact_'.$this->fieldSpecification->alias; + $groupFilters = array( + new SqlDataFlow\SimpleWhereClause($groupTableAlias, 'status', '=', 'Added'), + new SqlDataFlow\SimpleWhereClause($groupTableAlias, 'group_id', 'IN', $group_ids), + ); + + if ($dataFlow && $dataFlow instanceof SqlDataFlow) { + $whereClause = new SqlDataFlow\InTableWhereClause( + 'contact_id', + 'civicrm_group_contact', + $groupTableAlias, + $groupFilters, + $dataFlow->getName(), + $this->fieldSpecification->name, + $filter['op'] + ); + + $dataFlow->addWhereClause($whereClause); + } + } + + /** + * Returns true when this filter has additional configuration + * + * @return bool + */ + public function hasConfiguration() { + return true; + } + + /** + * When this filter type has additional configuration you can add + * the fields on the form with this function. + * + * @param \CRM_Core_Form $form + * @param array $filter + */ + public function buildConfigurationForm(\CRM_Core_Form $form, $filter=array()) { + $fieldSelect = \CRM_Dataprocessor_Utils_DataSourceFields::getAvailableFilterFieldsInDataSources($filter['data_processor_id']); + $groupsApi = civicrm_api3('Group', 'get', array('is_active' => 1, 'options' => array('limit' => 0))); + $groups = array(); + foreach($groupsApi['values'] as $group) { + $groups[$group['name']] = $group['title']; + } + + $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', 'parent_group', E::ts('Show only subgroup(s) of'), $groups, false, array( + 'style' => 'min-width:250px', + 'class' => 'crm-select2 huge', + 'placeholder' => E::ts('- Show all groups -'), + 'multiple' => false, + )); + + if (isset($filter['configuration'])) { + $configuration = $filter['configuration']; + $defaults = array(); + if (isset($configuration['field']) && isset($configuration['datasource'])) { + $defaults['contact_id_field'] = $configuration['datasource'] . '::' . $configuration['field']; + } + if (isset($configuration['parent_group'])) { + $defaults['parent_group'] = $configuration['parent_group']; + } + $form->setDefaults($defaults); + } + } + + /** + * When this filter type has configuration specify the template file name + * for the configuration form. + * + * @return false|string + */ + public function getConfigurationTemplateFileName() { + return "CRM/Dataprocessor/Form/Filter/Configuration/ContactInGroupFilter.tpl"; + } + + + /** + * Process the submitted values and create a configuration array + * + * @param $submittedValues + * @return array + */ + public function processConfiguration($submittedValues) { + list($datasource, $field) = explode('::', $submittedValues['contact_id_field'], 2); + $configuration['field'] = $field; + $configuration['datasource'] = $datasource; + $configuration['parent_group'] = isset($submittedValues['parent_group']) ? $submittedValues['parent_group'] : false; + return $configuration; + } + + /** + * Add the elements to the filter form. + * + * @param \CRM_Core_Form $form + * @return array + * Return variables belonging to this filter. + */ + public function addToFilterForm(\CRM_Core_Form $form) { + $fieldSpec = $this->getFieldSpecification(); + $operations = $this->getOperatorOptions($fieldSpec); + + $title = $fieldSpec->title; + if ($this->isRequired()) { + $title .= ' <span class="crm-marker">*</span>'; + } + + $api_params['is_active'] = 1; + if ($this->parent_group_id) { + $childGroupIds = \CRM_Contact_BAO_GroupNesting::getDescendentGroupIds([$this->parent_group_id], FALSE); + $api_params['id']['IN'] = $childGroupIds; + } + + $form->addElement('select', "{$fieldSpec->alias}_op", E::ts('Operator:'), $operations); + $form->addEntityRef( "{$fieldSpec->alias}_value", NULL, array( + 'placeholder' => E::ts('Select a group'), + 'entity' => 'Group', + 'api' => array('params' => $api_params), + 'create' => false, + 'multiple' => true, + )); + + $filter['type'] = $fieldSpec->type; + $filter['title'] = $title; + + return $filter; + } + + protected function getOperatorOptions(\Civi\DataProcessor\DataSpecification\FieldSpecification $fieldSpec) { + return array( + 'IN' => E::ts('Is one of'), + 'NOT IN' => E::ts('Is not one of'), + ); + } + + +} \ No newline at end of file diff --git a/templates/CRM/Dataprocessor/Form/Field/Configuration/GroupsOfContactFieldOutputHandler.tpl b/templates/CRM/Dataprocessor/Form/Field/Configuration/GroupsOfContactFieldOutputHandler.tpl new file mode 100644 index 0000000000000000000000000000000000000000..b35503fcac911e14a56dafbdf70a93970e381edd --- /dev/null +++ b/templates/CRM/Dataprocessor/Form/Field/Configuration/GroupsOfContactFieldOutputHandler.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.parent_group.label}</div> + <div class="content">{$form.parent_group.html}</div> + <div class="clear"></div> + </div> +{/crmScope} \ No newline at end of file diff --git a/templates/CRM/Dataprocessor/Form/Filter/Configuration/ContactInGroupFilter.tpl b/templates/CRM/Dataprocessor/Form/Filter/Configuration/ContactInGroupFilter.tpl new file mode 100644 index 0000000000000000000000000000000000000000..f71c32a0c6cc3e5dfdf0577465e694d25a824d96 --- /dev/null +++ b/templates/CRM/Dataprocessor/Form/Filter/Configuration/ContactInGroupFilter.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.parent_group.label}</div> + <div class="content">{$form.parent_group.html}</div> + <div class="clear"></div> +</div> +{/crmScope} \ No newline at end of file