Commit 0713541f authored by jaapjansma's avatar jaapjansma
Browse files

Output a data processor as a contribution search.

parent a2767905
......@@ -5,6 +5,7 @@
* Allow to limit ContactFilter to only show contacts from specific groups.
* 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.
* Added field outputs for simple calculations (substract and total).
* Added escaped output to search screens.
* Replaced the value separator in the raw field with a comma.
......
......@@ -55,26 +55,13 @@ abstract class CRM_Dataprocessor_Form_Output_AbstractUIOutputForm extends CRM_Co
$this->assign('has_exposed_filters', $this->hasExposedFilters());
}
/**
* Check whether the user has access to the output.
*
* @return bool
*/
protected function checkPermission() {
if (isset($this->dataProcessorOutput['permission']) && $this->dataProcessorOutput['permission']) {
if (!CRM_Core_Permission::check(array($this->dataProcessorOutput['permission']))) {
return false;
}
}
return true;
}
/**
* Retrieve the data processor and the output configuration
*
* @throws \Exception
*/
protected function loadDataProcessor() {
$factory = dataprocessor_get_factory();
if (!$this->dataProcessorId) {
$dataProcessorName = $this->getDataProcessorName();
$sql = "
......@@ -97,7 +84,12 @@ abstract class CRM_Dataprocessor_Form_Output_AbstractUIOutputForm extends CRM_Co
$this->dataProcessorOutput = civicrm_api3('DataProcessorOutput', 'getsingle', array('id' => $dao->output_id));
$this->assign('output', $this->dataProcessorOutput);
if (!$this->checkPermission()) {
$outputClass = $factory->getOutputByName($this->dataProcessorOutput['type']);
if (!$outputClass instanceof \Civi\DataProcessor\Output\UIOutputInterface) {
throw new \Exception('Invalid output');
}
if (!$outputClass->checkUIPermission($this->dataProcessorOutput, $this->dataProcessor)) {
CRM_Utils_System::permissionDenied();
CRM_Utils_System::civiExit();
} elseif (!$this->isConfigurationValid()) {
......
<?php
/**
* @author Jaap Jansma <jaap.jansma@civicoop.org>
* @license AGPL-3.0
*/
use Civi\DataProcessor\Output\UIOutputInterface;
use CRM_Dataprocessor_ExtensionUtil as E;
class CRM_DataprocessorSearch_ContributionSearch implements UIOutputInterface {
/**
* 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, $output=array()) {
$navigation = CRM_Dataprocessor_Utils_Navigation::singleton();
$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', 'contribution_id_field', E::ts('Contribution ID field'), $fields, true, array(
'style' => 'min-width:250px',
'class' => 'crm-select2 huge',
'placeholder' => E::ts('- select -'),
));
$form->add('select', 'hide_id_field', E::ts('Show Contribution ID field'), array(0=>'Contribution ID is Visible', 1=> 'Contribution ID is hidden'));
$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'])) {
$navigationPath = $navigation->getNavigationPathById($output['configuration']['navigation_id']);
unset($navigationOptions[$navigationPath]);
}
$form->add('select', 'navigation_parent_path', ts('Parent Menu'), array('' => ts('- select -')) + $navigationOptions, true);
$defaults = array();
if ($output) {
if (isset($output['permission'])) {
$defaults['permission'] = $output['permission'];
}
if (isset($output['configuration']) && is_array($output['configuration'])) {
if (isset($output['configuration']['contribution_id_field'])) {
$defaults['contribution_id_field'] = $output['configuration']['contribution_id_field'];
}
if (isset($output['configuration']['navigation_id'])) {
$defaults['navigation_parent_path'] = $navigation->getNavigationParentPathById($output['configuration']['navigation_id']);
}
if (isset($output['configuration']['hide_id_field'])) {
$defaults['hide_id_field'] = $output['configuration']['hide_id_field'];
}
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';
}
$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/DataprocessorSearch/Form/OutputConfiguration/ContributionSearch.tpl";
}
/**
* Process the submitted values and create a configuration array
*
* @param $submittedValues
* @param array $output
* @return array
*/
public function processConfiguration($submittedValues, &$output) {
$output['permission'] = $submittedValues['permission'];
$configuration['contribution_id_field'] = $submittedValues['contribution_id_field'];
$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;
}
/**
* This function is called prior to removing an output
*
* @param array $output
* @return void
*/
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_contribution_search/{$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_DataprocessorSearch_Controller_ContributionSearch';
}
/**
* 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
*/
/**
* This class is used by the Search functionality.
*
* - the search controller is used for building/processing multiform
* searches.
*
* Typically the first form will display the search criteria and it's results
*
* The second form is used to process search results with the associated actions.
*/
class CRM_DataprocessorSearch_Controller_ContributionSearch extends CRM_Core_Controller {
/**
* Class constructor.
*
* @param string $title
* @param bool $modal
* @param int|mixed|null $action
*/
public function __construct($title = NULL, $modal = TRUE, $action = CRM_Core_Action::NONE) {
parent::__construct($title, $modal);
$this->_stateMachine = new CRM_DataprocessorSearch_StateMachine_ContributionSearch($this, $action);
// create and instantiate the pages
$this->addPages($this->_stateMachine, $action);
// add all the actions
$this->addActions();
}
/**
* Process the request, overrides the default QFC run method
* This routine actually checks if the QFC is modal and if it
* is the first invalid page, if so it call the requested action
* if not, it calls the display action on the first invalid page
* avoids the issue of users hitting the back button and getting
* a broken page
*
* This run is basically a composition of the original run and the
* jump action
*
* @return mixed
*/
public function run() {
$actionName = $this->getActionName();
list($pageName, $action) = $actionName;
// Hack to replace to userContext for redirecting after a Task has been completed.
// We want the redirect
if (!$this->_pages[$pageName] instanceof CRM_DataprocessorSearch_Form_ContributionSearch) {
$session = CRM_Core_Session::singleton();
$qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $this);
$urlPath = CRM_Utils_System::getUrlPath();
$urlParams = 'force=1';
if ($qfKey) {
$urlParams .= "&qfKey=$qfKey";
}
$this->setDestination(CRM_Utils_System::url($urlPath, $urlParams));
}
return parent::run();
}
/**
* @return mixed
*/
public function selectorName() {
return $this->get('selectorName');
}
}
<?php
/**
* @author Jaap Jansma <jaap.jansma@civicoop.org>
* @license AGPL-3.0
*/
use CRM_Dataprocessor_ExtensionUtil as E;
class CRM_DataprocessorSearch_Form_ContributionSearch extends CRM_DataprocessorSearch_Form_AbstractSearch {
/**
* Returns the name of the default Entity
*
* @return string
*/
public function getDefaultEntity() {
return 'Contribution';
}
/**
* Returns the url for view of the record action
*
* @param $row
*
* @return false|string
*/
protected function link($row) {
return CRM_Utils_System::url('civicrm/contact/view/contribution', 'reset=1&id='.$row['id'].'&cid='.$row['id'].'&action=view');
}
/**
* Returns the link text for view of the record action
*
* @param $row
*
* @return false|string
*/
protected function linkText($row) {
return E::ts('View contribution');
}
/**
* Checks whether the output has a valid configuration
*
* @return bool
*/
protected function isConfigurationValid() {
if (!isset($this->dataProcessorOutput['configuration']['contribution_id_field'])) {
return false;
}
return true;
}
/**
* Return the data processor ID
*
* @return String
*/
protected function getDataProcessorName() {
$dataProcessorName = str_replace('civicrm/dataprocessor_contribution_search/', '', CRM_Utils_System::getUrlPath());
return $dataProcessorName;
}
/**
* Returns the name of the output for this search
*
* @return string
*/
protected function getOutputName() {
return 'contribution_search';
}
/**
* Returns the name of the ID field in the dataset.
*
* @return string
*/
protected function getIdFieldName() {
return $this->dataProcessorOutput['configuration']['contribution_id_field'];
}
/**
* @return string
*/
protected function getEntityTable() {
return 'civicrm_contribution';
}
/**
* Returns whether we want to use the prevnext cache.
* @return bool
*/
protected function usePrevNextCache() {
return true;
}
/**
* Builds the list of tasks or actions that a searcher can perform on a result set.
*
* @return array
*/
public function buildTaskList() {
if (!$this->_taskList) {
$taskParams['softCreditFiltering'] = FALSE;
$this->_taskList = CRM_Contribute_Task::permissionedTaskTitles(CRM_Core_Permission::getPermission(), $taskParams);
}
return $this->_taskList;
}
}
<?php
/**
* @author Jaap Jansma <jaap.jansma@civicoop.org>
* @license AGPL-3.0
*/
class CRM_DataprocessorSearch_StateMachine_ContributionSearch extends CRM_Core_StateMachine {
/**
* The task that the wizard is currently processing
*
* @var string
*/
protected $_task;
/**
* Class constructor.
*
* @param object $controller
* @param \const|int $action
*/
public function __construct($controller, $action = CRM_Core_Action::NONE) {
parent::__construct($controller, $action);
$this->_pages = array();
$this->_pages['Basic'] = array(
'className' => 'CRM_DataprocessorSearch_Form_ContributionSearch',
);
list($task, $result) = $this->taskName($controller);
$this->_task = $task;
if (is_array($task)) {
foreach ($task as $t) {
$this->_pages[$t] = NULL;
}
}
else {
$this->_pages[$task] = NULL;
}
if ($result) {
$this->_pages['CRM_Contribute_Form_Task_Result'] = NULL;
}
$this->addSequentialPages($this->_pages, $action);
}
/**
* Determine the form name based on the action. This allows us
* to avoid using conditional state machine, much more efficient
* and simpler
*
* @param CRM_Core_Controller $controller
* The controller object.
*
* @return array
* the name of the form that will handle the task
*/
public function taskName($controller) {
// total hack, check POST vars and then session to determine stuff
$value = CRM_Utils_Array::value('task', $_POST);
if (!isset($value)) {
$value = $controller->get('task');
}
$this->_controller->set('task', $value);
return CRM_Contribute_Task::getTask($value);
}
/**
* Return the form name of the task.
*
* @return string
*/
public function getTaskFormName() {
if (is_array($this->_task)) {
// return first page
return CRM_Utils_String::getClassName($this->_task[0]);
}
else {
return CRM_Utils_String::getClassName($this->_task);
}
}
/**
* Since this is a state machine for search and we want to come back to the same state
* we dont want to issue a reset of the state session when we are done processing a task
*/
public function shouldReset() {
return FALSE;
}
}
......@@ -125,6 +125,7 @@ class Factory {
$this->addOutput('contact_search', 'CRM_Contact_DataProcessorContactSearch', E::ts('Contact Search'));
$this->addOutput('activity_search', 'CRM_DataprocessorSearch_ActivitySearch', E::ts('Activity Search'));
$this->addOutput('case_search', 'CRM_DataprocessorSearch_CaseSearch', E::ts('Case Search'));
$this->addOutput('contribution_search', 'CRM_DataprocessorSearch_ContributionSearch', E::ts('Contribution Search'));
$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'));
......
{include file="CRM/DataprocessorSearch/Form/Search.tpl"}
\ No newline at end of file
{crmScope extensionKey='dataprocessor'}
<div class="crm-section">
<div class="label">{$form.navigation_parent_path.label}</div>
<div class="content">{$form.navigation_parent_path.html}</div>
<div class="clear"></div>
</div>
<div class="crm-section">
<div class="label">{$form.permission.label}</div>
<div class="content">{$form.permission.html}</div>
<div class="clear"></div>
</div>
<div class="crm-section">
<div class="label">{$form.contribution_id_field.label}</div>
<div class="content">{$form.contribution_id_field.html}</div>
<div class="clear"></div>
</div>
<div class="crm-section">
<div class="label">{$form.hide_id_field.label}</div>
<div class="content">{$form.hide_id_field.html}</div>
<div class="clear"></div>
</div>
<div class="crm-section">
<div class="label">{$form.expanded_search.label}</div>
<div class="content">{$form.expanded_search.html}</div>
<div class="clear"></div>
</div>
<div class="crm-section">
<div class="label">{$form.help_text.label}</div>
<div class="content">{$form.help_text.html}</div>
<div class="clear"></div>
</div>
{/crmScope}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment