Commit c031ad67 authored by jaapjansma's avatar jaapjansma
Browse files

merge

parents 5a735d87 a25820d9
# Master version (in development)
# Version 1.2.0 (not yet released)
* Made CSV Export download available for anonymous users.
* Change Group Filter so that it also works with smart groups
* Fixed bug with date filter
# Version 1.1.0
* Respect selected permissions for outputs
* Allow to specify "Is Empty" for various filters.
* Allow to limit ContactFilter to only show contacts from specific groups.
* it is now also possible to output a data processor on CiviCRMs dahsboard.
* Output a data processor as a dashboard.
* Output a data processor as a tab on the contact summary screen.
* Output a data processor as a contribution search.
* Output a data processor as a membership search.
* Added field outputs for simple calculations (substract and total).
* Fixed bug with date filter
* Added escaped output to search screens.
* Replaced the value separator in the raw field with a comma.
* Added filter to search text in multiple fields.
* Added filter for searching contacts with a certain tag.
* Added filter for searching contacts with a certain type.
* Added filter for contact has membership.
* Added filter to respect the ACL. So that a user only sees the contacts he is allowed to see.
* Removed the title attribute from the outputs as those don't make sense.
* Refactored aggregation functionality and added aggregation function field.
* Fixed issue with updating navigation after editing an output.
* Added option to expand criteria forms on search forms.
* Added a Date field.
* Added function to clone a data processor.
* Added Case ID field on the activity source.
* Added field to display relationships.
* Added is not empty as a filter operator.
* Added hidden fields option to search outputs, dashboard output and contact summary tab output.
* Added formatted number output field handler
* Added SQL Table Data Source
* Export from a search only exports the selected rows.
# Version 1.0.7
* Changed Event Participants Field Output Handler to return a string.
* Build a cache clear when a data processor configuration is changed.
# Version 1.0.6
......@@ -23,7 +56,6 @@
# Version 1.0.3
* Fixed issue with date filters.
>>>>>>> origin/performance
# Version 1.0.2
......
......@@ -34,18 +34,6 @@ class CRM_Contact_DataProcessorContactSearch implements UIOutputInterface {
$field = $outputFieldHandler->getOutputFieldSpecification();
$fields[$field->alias] = $field->title;
}
$form->add('text', 'title', E::ts('Title'),NULL, true);
// form elements for adding Dashlet
// $output['dashlet'] 1-> Yes 2->No
if(isset($output['dashlet']) && $output['dashlet']==1){
$form->add('text', 'dashlet_title', E::ts('Dashlet Title'), NULL, true);
$form->add('text', 'dashlet_name', E::ts('Dashlet Name (system name)'), NULL, true);
$form->add('select', 'dashlet_active', E::ts('Is Dashlet Active ?'), array(1=>'Yes', 0=> 'No'), true);
}
$form->add('select','permission', E::ts('Permission'), \CRM_Core_Permission::basicPermissions(), true, array(
'style' => 'min-width:250px',
'class' => 'crm-select2 huge',
......@@ -58,8 +46,17 @@ class CRM_Contact_DataProcessorContactSearch implements UIOutputInterface {
));
$form->add('select', 'hide_id_field', E::ts('Show Contact ID field'), array(0=>'Contact ID is Visible', 1=> 'Contact ID is hidden'));
$form->add('select', 'hidden_fields', E::ts('Hidden fields'), $fields, false, array(
'style' => 'min-width:250px',
'class' => 'crm-select2 huge',
'multiple' => true,
'placeholder' => E::ts('- select -'),
));
$form->add('wysiwyg', 'help_text', E::ts('Help text for this search'), array('rows' => 6, 'cols' => 80));
$form->add('checkbox', 'expanded_search', E::ts('Expand criteria form initially'));
// navigation field
$navigationOptions = $navigation->getNavigationOptions();
if (isset($output['configuration']['navigation_id'])) {
......@@ -70,15 +67,10 @@ class CRM_Contact_DataProcessorContactSearch implements UIOutputInterface {
$defaults = array();
if ($output) {
if (isset($output['permission'])) {
$defaults['permission'] = $output['permission'];
}
if (isset($output['dashlet_name'])) {
$defaults['dashlet_name'] = $output['dashlet_name'];
$defaults['dashlet_title'] = $output['dashlet_title'];
$defaults['dashlet_active'] = $output['dashlet_active'];
}
if (isset($output['configuration']) && is_array($output['configuration'])) {
if (isset($output['configuration']['contact_id_field'])) {
$defaults['contact_id_field'] = $output['configuration']['contact_id_field'];
......@@ -86,24 +78,24 @@ class CRM_Contact_DataProcessorContactSearch implements UIOutputInterface {
if (isset($output['configuration']['navigation_id'])) {
$defaults['navigation_parent_path'] = $navigation->getNavigationParentPathById($output['configuration']['navigation_id']);
}
if (isset($output['configuration']['title'])) {
$defaults['title'] = $output['configuration']['title'];
}
if (isset($output['configuration']['hide_id_field'])) {
$defaults['hide_id_field'] = $output['configuration']['hide_id_field'];
}
if (isset($output['configuration']['hidden_fields'])) {
$defaults['hidden_fields'] = $output['configuration']['hidden_fields'];
}
if (isset($output['configuration']['help_text'])) {
$defaults['help_text'] = $output['configuration']['help_text'];
}
if (isset($output['configuration']['expanded_search'])) {
$defaults['expanded_search'] = $output['configuration']['expanded_search'];
}
}
}
if (!isset($defaults['permission'])) {
$defaults['permission'] = 'access CiviCRM';
}
if (empty($defaults['title'])) {
$defaults['title'] = civicrm_api3('DataProcessor', 'getvalue', array('id' => $output['data_processor_id'], 'return' => 'title'));
}
$form->setDefaults($defaults);
}
......@@ -127,31 +119,23 @@ class CRM_Contact_DataProcessorContactSearch implements UIOutputInterface {
*/
public function processConfiguration($submittedValues, &$output) {
$output['permission'] = $submittedValues['permission'];
$configuration['title'] = $submittedValues['title'];
$configuration['contact_id_field'] = $submittedValues['contact_id_field'];
$configuration['hidden_fields'] = $submittedValues['hidden_fields'];
$configuration['navigation_parent_path'] = $submittedValues['navigation_parent_path'];
$configuration['hide_id_field'] = $submittedValues['hide_id_field'];
$configuration['help_text'] = $submittedValues['help_text'];
$configuration['expanded_search'] = isset($submittedValues['expanded_search']) ? $submittedValues['expanded_search'] : false;
return $configuration;
}
/**
* Process the submitted values and create a configuration array
* This function is called prior to removing an output
*
* @param $submittedValues
* @param array $output
* @return array
* @return void
*/
public function processDashletConfiguration($submittedValues) {
$configuration['domain_id'] = 1;
$configuration['name'] = $submittedValues['dashlet_name'];
$configuration['label'] = $submittedValues['dashlet_title'];
$configuration['permission'] = $submittedValues['permission'];
$configuration['is_active'] = $submittedValues['dashlet_active'];
$configuration['cache_minutes'] = 60;
return $configuration;
public function deleteOutput($output) {
// Do nothing
}
/**
......@@ -173,7 +157,7 @@ class CRM_Contact_DataProcessorContactSearch implements UIOutputInterface {
* @return string
*/
public function getTitleForUiLink($output, $dataProcessor) {
return isset($output['configuration']['title']) ? $output['configuration']['title'] : $dataProcessor['title'];
return $dataProcessor['title'];
}
/**
......@@ -208,4 +192,4 @@ class CRM_Contact_DataProcessorContactSearch implements UIOutputInterface {
$output['permission']
));
}
}
\ No newline at end of file
}
<?php
/**
* @author Jaap Jansma <jaap.jansma@civicoop.org>
* @license AGPL-3.0
*/
use CRM_Dataprocessor_ExtensionUtil as E;
use Civi\DataProcessor\Output\UIOutputInterface;
class CRM_Contact_DataProcessorContactSummaryTab implements UIOutputInterface {
/**
* Implements hook_civicrm_tabset().
*
* Adds the data processor out to the contact summary tabs.
*
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_tabset/
*
* @param $tabsetName
* @param $tabs
* @param $context
*/
public static function hookTabset($tabsetName, &$tabs, $context) {
if ($tabsetName != 'civicrm/contact/view') {
return;
}
$factory = dataprocessor_get_factory();
// Check whether the factory exists. Usually just after
// installation the factory does not exists but then no
// outputs exists either. So we can safely return this function.
if (!$factory) {
return;
}
$maxWeight = 0;
foreach($tabs as $tab) {
if (isset($tab['weight']) && $tab['weight'] > $maxWeight) {
$maxWeight = $tab['weight'];
}
}
$maxWeight++;
$sql = "SELECT o.*, d.name as data_processor_name
FROM civicrm_data_processor d
INNER JOIN civicrm_data_processor_output o ON d.id = o.data_processor_id
WHERE d.is_active = 1 AND o.type = 'contact_summary_tab'";
$dao = CRM_Core_DAO::executeQuery($sql);
while($dao->fetch()) {
$outputClass = $factory->getOutputByName($dao->type);
if (!$outputClass instanceof \Civi\DataProcessor\Output\UIOutputInterface) {
continue;
}
$output = civicrm_api3('DataProcessorOutput', 'getsingle', ['id' => $dao->id]);
$dataprocessor = civicrm_api3('DataProcessor', 'getsingle', ['id' => $dao->data_processor_id]);
if (!$outputClass->checkUIPermission($output, $dataprocessor)) {
continue;
}
$tab = [
'id' => 'dataprocessor_' . $dataprocessor['name'],
'title' => $outputClass->getTitleForUiLink($output, $dataprocessor),
'icon' => $outputClass->getIconForUiLink($output, $dataprocessor),
'url' => CRM_Utils_System::url('civicrm/dataprocessor/page/contactsummary', array('contact_id' => $context['contact_id'], 'data_processor' => $dataprocessor['name'], 'reset' => 1, 'force' => 1)),
'class' => '',
];
if (isset($output['configuration']['weight']) && strlen($output['configuration']['weight']) && is_numeric($output['configuration']['weight'])) {
$tab['weight'] = $output['configuration']['weight'];
if ($tab['weight'] > $maxWeight) {
$maxWeight = $tab['weight'];
}
}
else {
$tab['weight'] = $maxWeight;
$maxWeight++;
}
$tabs[$tab['id']] = $tab;
}
}
/**
* Returns true when this output has additional configuration
*
* @return bool
*/
public function hasConfiguration() {
return true;
}
/**
* When this output type has additional configuration you can add
* the fields on the form with this function.
*
* @param \CRM_Core_Form $form
* @param array $output
*/
public function buildConfigurationForm(\CRM_Core_Form $form, $output = []) {
$fieldSelect = \CRM_Dataprocessor_Utils_DataSourceFields::getAvailableFilterFieldsInDataSources($output['data_processor_id']);
$dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $output['data_processor_id']));
$dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
$fields = array();
foreach($dataProcessorClass->getDataFlow()->getOutputFieldHandlers() as $outputFieldHandler) {
$field = $outputFieldHandler->getOutputFieldSpecification();
$fields[$field->alias] = $field->title;
}
$form->add('select','permission', E::ts('Permission'), \CRM_Core_Permission::basicPermissions(), true, array(
'style' => 'min-width:250px',
'class' => 'crm-select2 huge',
'placeholder' => E::ts('- select -'),
));
$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', 'hidden_fields', E::ts('Hidden fields'), $fields, false, array(
'style' => 'min-width:250px',
'class' => 'crm-select2 huge',
'multiple' => true,
'placeholder' => E::ts('- select -'),
));
$form->add('wysiwyg', 'help_text', E::ts('Help text for this tab'), array('rows' => 6, 'cols' => 80));
$form->add('wysiwyg', 'no_result_text', E::ts('No Result Text'), array('rows' => 2, 'class' => 'huge'), false);
$form->add('text', 'weight', E::ts('Weight'));
$form->add('text', 'default_limit', E::ts('Default Limit'));
$defaults = array();
if ($output) {
if (isset($output['permission'])) {
$defaults['permission'] = $output['permission'];
}
if (isset($output['configuration']) && is_array($output['configuration'])) {
if (isset($output['configuration']['help_text'])) {
$defaults['help_text'] = $output['configuration']['help_text'];
}
if (isset($output['configuration']['no_result_text'])) {
$defaults['no_result_text'] = $output['configuration']['no_result_text'];
}
if (isset($output['configuration']['contact_id_field'])) {
$defaults['contact_id_field'] = $output['configuration']['contact_id_field'];
}
if (isset($output['configuration']['hidden_fields'])) {
$defaults['hidden_fields'] = $output['configuration']['hidden_fields'];
}
if (isset($output['configuration']['default_limit'])) {
$defaults['default_limit'] = $output['configuration']['default_limit'];
}
if (isset($output['configuration']['weight'])) {
$defaults['weight'] = $output['configuration']['weight'];
}
}
}
if (!isset($defaults['permission'])) {
$defaults['permission'] = 'access CiviCRM';
}
if (!isset($defaults['no_result_text'])) {
$defaults['no_result_text'] = E::ts('No records');
}
if (!isset($defaults['default_limit'])) {
$defaults['default_limit'] = 25;
}
$form->setDefaults($defaults);
}
/**
* When this output type has configuration specify the template file name
* for the configuration form.
*
* @return false|string
*/
public function getConfigurationTemplateFileName() {
return "CRM/Contact/Form/OutputConfiguration/DataProcessorContactSummaryTab.tpl";
}
/**
* Process the submitted values and create a configuration array
*
* @param $submittedValues
* @param array $output
*
* @return array $output
* @throws \Exception
*/
public function processConfiguration($submittedValues, &$output) {
$output['permission'] = $submittedValues['permission'];
$configuration['contact_id_field'] = $submittedValues['contact_id_field'];
$configuration['hidden_fields'] = $submittedValues['hidden_fields'];
$configuration['help_text'] = $submittedValues['help_text'];
$configuration['no_result_text'] = $submittedValues['no_result_text'];
$configuration['weight'] = $submittedValues['weight'];
$configuration['default_limit'] = $submittedValues['default_limit'];
return $configuration;
}
/**
* This function is called prior to removing an output
*
* @param array $output
* @return void
* @throws \Exception
*/
public function deleteOutput($output) {
// Do nothing
}
/**
* Returns the url for the page/form this output will show to the user
*
* @param array $output
* @param array $dataProcessor
* @return string
*/
public function getUrlToUi($output, $dataProcessor) {
return "civicrm/dataprocessor_contact_summary/{$dataProcessor['name']}";
}
/**
* Returns the url for the page/form this output will show to the user
*
* @param array $output
* @param array $dataProcessor
* @return string
*/
public function getTitleForUiLink($output, $dataProcessor) {
return $dataProcessor['title'];
}
/**
* Returns the url for the page/form this output will show to the user
*
* @param array $output
* @param array $dataProcessor
* @return string|false
*/
public function getIconForUiLink($output, $dataProcessor) {
return false;
}
/**
* Returns the callback for the UI.
*
* @return string
*/
public function getCallbackForUi() {
return 'CRM_Contact_Form_DataProcessorContactSummaryTab';
}
/**
* Checks whether the current user has access to this output
*
* @param array $output
* @param array $dataProcessor
* @return bool
*/
public function checkUIPermission($output, $dataProcessor) {
return CRM_Core_Permission::check(array(
$output['permission']
));
}
}
<?php
/**
* @author Jaap Jansma <jaap.jansma@civicoop.org>
* @license AGPL-3.0
*/
use Civi\DataProcessor\DataFlow\SqlDataFlow;
use Civi\DataProcessor\DataFlow\SqlDataFlow\SimpleWhereClause;
use Civi\DataProcessor\Exception\DataSourceNotFoundException;
use Civi\DataProcessor\Exception\FieldNotFoundException;
use Civi\DataProcessor\ProcessorType\AbstractProcessorType;
use CRM_Dataprocessor_ExtensionUtil as E;
class CRM_Contact_Form_DataProcessorContactSummaryTab extends CRM_DataprocessorSearch_Form_AbstractSearch {
public function buildQuickform() {
parent::buildQuickform();
$this->add('hidden', 'data_processor');
$this->setDefaults(array('data_processor' => $this->getDataProcessorName()));
$this->assign('no_result_text', $this->dataProcessorOutput['configuration']['no_result_text']);
}
/**
* Returns the default row limit.
*
* @return int
*/
protected function getDefaultLimit() {
$defaultLimit = 25;
if (!empty($this->dataProcessorOutput['configuration']['default_limit'])) {
$defaultLimit = $this->dataProcessorOutput['configuration']['default_limit'];
}
return $defaultLimit;
}
/**
* Returns the name of the ID field in the dataset.
*
* @return string
*/
protected function getIdFieldName() {
return false;
}
/**
* @return false|string
*/
protected function getEntityTable() {
return false;
}
/**
* Returns the url for view of the record action
*
* @param $row
*
* @return false|string
*/
protected function link($row) {
return false;
}
/**
* Returns the link text for view of the record action
*
* @param $row
*
* @return false|string
*/
protected function linkText($row) {
return false;
}
/**
* Return the data processor ID
*
* @return String
*/
protected function getDataProcessorName() {
$dataProcessorName = str_replace('civicrm/dataprocessor_contact_summary/', '', CRM_Utils_System::getUrlPath());
return $dataProcessorName;
}
/**
* Returns the name of the output for this search
*
* @return string
*/
protected function getOutputName() {
return 'contact_summary_tab';
}
/**
* Checks whether the output has a valid configuration
*
* @return bool
*/
protected function isConfigurationValid() {
return TRUE;
}
/**
* Add buttons for other outputs of this data processor
*/
protected function addExportOutputs() {
// Don't add exports
}
/**
* Alter the data processor.
*
* Use this function in child classes to add for example additional filters.
*
* E.g. The contact summary tab uses this to add additional filtering on the contact id of
* the displayed contact.
*
* @param \Civi\DataProcessor\ProcessorType\AbstractProcessorType $dataProcessorClass
*/
protected function alterDataProcessor(AbstractProcessorType $dataProcessorClass) {
$cid = CRM_Utils_Request::retrieve('contact_id', 'Integer', $this, true);
list($datasource_name, $field_name) = explode('::', $this->dataProcessorOutput['configuration']['contact_id_field'], 2);
$dataSource = $dataProcessorClass->getDataSourceByName($datasource_name);
if (!$dataSource) {
throw new DataSourceNotFoundException(E::ts("Requires data source '%1' which could not be found. Did you rename or deleted the data source?", array(1=>$datasource_name)));
}
$fieldSpecification = $dataSource->getAvailableFilterFields()->getFieldSpecificationByAlias($field_name);
if (!$fieldSpecification) {
$fieldSpecification = $dataSource->getAvailableFilterFields()->getFieldSpecificationByName($field_name);
}
if (!$fieldSpecification) {
throw new FieldNotFoundException(E::ts("Requires a field with the name '%1' in the data source '%2'. Did you change the data source type?", array(
1 => $field_name,
2 => $datasource_name
)));
}
$fieldSpecification = clone $fieldSpecification;
$fieldSpecification->alias = 'contact_summary_tab_contact_id';
$dataFlow = $dataSource->ensureField($fieldSpecification);
if ($dataFlow && $dataFlow instanceof SqlDataFlow) {
$whereClause = new SimpleWhereClause($dataFlow->getName(), $fieldSpecification->name, '=', $cid, $fieldSpecification->type);
$dataFlow->addWhereClause($whereClause);