Commit e8c3fd9d authored by jaapjansma's avatar jaapjansma
Browse files

Added contact summary as an output

parent 87729615
......@@ -3,7 +3,8 @@
* 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.
* Added field outputs for simple calculations (substract and total).
# Version 1.0.7
......@@ -34,4 +35,4 @@
# Version 1.0.1
Initial release.
\ No newline at end of file
Initial release.
<?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']);
$form->add('text', 'title', E::ts('Title'), true);
$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('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']['title'])) {
$defaults['title'] = $output['configuration']['title'];
}
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']['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 (empty($defaults['title'])) {
$defaults['title'] = civicrm_api3('DataProcessor', 'getvalue', array('id' => $output['data_processor_id'], 'return' => 'title'));
}
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['title'] = $submittedValues['title'];
$configuration['contact_id_field'] = $submittedValues['contact_id_field'];
$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 isset($output['configuration']['title']) ? $output['configuration']['title'] : $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)));
}
if (!$dataSource->getAvailableFilterFields()->getFieldSpecificationByName($field_name)) {
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 $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->alias = 'contact_summary_tab_contact_id';
$dataFlow = $dataSource->ensureField($fieldSpecification->name);
if ($dataFlow && $dataFlow instanceof SqlDataFlow) {
$whereClause = new SimpleWhereClause($dataFlow->getName(), $fieldSpecification->name, '=', $cid, $fieldSpecification->type);
$dataFlow->addWhereClause($whereClause);
}
}
}
<?php
/**
* @author Jaap Jansma <jaap.jansma@civicoop.org>
* @license AGPL-3.0
*/
class CRM_Contact_Page_DataProcessorContactSummaryTab extends CRM_Core_Page {
/**
* @var int
*/
private $outputId;
/**
* @var String
*/
private $dataProcessorName;
/**
* @var array
*/
private $dataProcessor;
/**
* @var Civi\DataProcessor\ProcessorType\AbstractProcessorType
*/
private $dataProcessorClass;
/**
* Pre Process the results
*
* @return void
*/
protected function preProcess() {
$this->dataProcessorName = CRM_Utils_Request::retrieve('data_processor', 'String', $this, true);
$contact_id = CRM_Utils_Request::retrieve('contact_id', 'Integer', $this, true);
$this->dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('name' => $this->dataProcessorName));
$this->dataProcessorClass = CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor);
$this->assign('dataProcessorName', $this->dataProcessorName);
$this->assign('contact_id', $contact_id);
$this->assign('url', CRM_Utils_System::url("civicrm/dataprocessor_contact_summary/{$this->dataProcessorName}", array('contact_id' => $contact_id, 'reset' => '1', 'snippet' => 'json')));
}
/**
* Dataprocessor Output as dashlet.
*
* @return void
*/
public function run() {
$this->preProcess();
return parent::run();
}
}
......@@ -32,7 +32,7 @@ class CRM_DataprocessorDashlet_Dashlet implements Civi\DataProcessor\Output\Outp
'placeholder' => E::ts('- select -'),
));
$form->add('text', 'default_limit', E::ts('Default Limit'));
$form->add('wysiwyg', 'help_text', E::ts('Help text for this search'), array('rows' => 6, 'cols' => 80));
$form->add('wysiwyg', 'help_text', E::ts('Help text for this dashlet'), array('rows' => 6, 'cols' => 80));
$defaults = array();
if ($output) {
......
......@@ -50,22 +50,7 @@ class CRM_DataprocessorDashlet_Page_Dashlet extends CRM_Core_Page {
public function run() {
$this->preProcess();
$this->addColumnHeaders();
return parent::run();
}
/**
* Add the headers for the columns
*
*/
protected function addColumnHeaders() {
$columnHeaders = array();
foreach($this->dataProcessorClass->getDataFlow()->getOutputFieldHandlers() as $outputFieldHandler) {
$field = $outputFieldHandler->getOutputFieldSpecification();
$columnHeaders[$field->alias] = $field->title;
}
$this->assign('columnHeaders', $columnHeaders);
}
}
......@@ -6,6 +6,7 @@
use Civi\DataProcessor\FieldOutputHandler\FieldOutput;
use Civi\DataProcessor\FieldOutputHandler\Markupable;
use Civi\DataProcessor\ProcessorType\AbstractProcessorType;
use CRM_Dataprocessor_ExtensionUtil as E;
abstract class CRM_DataprocessorSearch_Form_AbstractSearch extends CRM_Dataprocessor_Form_Output_AbstractUIOutputForm {
......@@ -245,6 +246,8 @@ abstract class CRM_DataprocessorSearch_Form_AbstractSearch extends CRM_Dataproce
$this->dataProcessorClass->getDataFlow()->addSort($sortField['name'], $sortDirection);
}
$this->alterDataProcessor($this->dataProcessorClass);
$pagerParams = $this->getPagerParams();
$pagerParams['total'] = $this->dataProcessorClass->getDataFlow()->recordCount();
$pagerParams['pageID'] = $pageId;
......@@ -280,7 +283,9 @@ abstract class CRM_DataprocessorSearch_Form_AbstractSearch extends CRM_Dataproce
$row['link_text'] = $this->linkText($row);
}
$this->addElement('checkbox', $row['checkbox'], NULL, NULL, ['class' => 'select-row']);
if (isset($row['checkbox'])) {
$this->addElement('checkbox', $row['checkbox'], NULL, NULL, ['class' => 'select-row']);
}
if ($row['id'] && $this->usePrevNextCache()) {
$prevnextData[] = array(
......@@ -461,4 +466,18 @@ abstract class CRM_DataprocessorSearch_Form_AbstractSearch extends CRM_Dataproce
return $this->dataProcessorOutput['configuration']['title'];
}
/**
* 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) {
}
}
......@@ -119,6 +119,7 @@ class Factory {
$this->addDataSource('membership_status', 'Civi\DataProcessor\Source\Member\MembershipStatusSource', E::ts('Membership Status'));
$this->addDataSource('csv', 'Civi\DataProcessor\Source\CSV', E::ts('CSV File'));
$this->addOutput('api', 'Civi\DataProcessor\Output\Api', E::ts('API'));
$this->addOutput('contact_summary_tab', 'CRM_Contact_DataProcessorContactSummaryTab', E::ts('Tab on contact summary'));
$this->addOutput('dashlet', 'CRM_DataprocessorDashlet_Dashlet', E::ts('Dashlet'));
$this->addOutput('search', 'CRM_DataprocessorSearch_Search', E::ts('Search'));
$this->addOutput('contact_search', 'CRM_Contact_DataProcessorContactSearch', E::ts('Contact Search'));
......
......@@ -115,6 +115,21 @@ function dataprocessor_search_action_designer_types(&$types) {
CRM_DataprocessorSearch_Task::searchActionDesignerTypes($types);
}
/**
* 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
*/
function dataprocessor_civicrm_tabset($tabsetName, &$tabs, $context) {
CRM_Contact_DataProcessorContactSummaryTab::hookTabset($tabsetName, $tabs, $context);
}
/**
* Implements hook_civicrm_config().
*
......
{crmScope extensionKey='dataprocessor'}
{capture assign="criteriaFormTitle"}{ts}Filter{/ts}{/capture}
{include file="CRM/DataprocessorSearch/Form/Search.tpl"}
{/crmScope}
{crmScope extensionKey='dataprocessor'}
<div id="dashlet_configuration">
<div