Skip to content

Commit

Permalink
Add PDF letter functionality for Activities using new token processor
Browse files Browse the repository at this point in the history
Create CRM_Actvity_Form_Task_PDFLetterCommon extending CRM_Core_Form_Task_PDFLetterCommon

CRM_Activity_Tokens: add source, target and assignee tokens

Eg: {activity.target_N_display_name}
- N can be substituted for a number to show the details of the nth activity target contact
eg: {activity.target_1_display_name} is the display name of the first target
For ease of use, contacts are numbered from 1.
If the literal N is used or is replaced by 0, it is treated as 1.

Slim down alterActionScheduleQuery() and move most data fetching to prefetch()

Add tests for Activity PDF Letter
  • Loading branch information
aydun authored and mattwire committed Jan 3, 2020
1 parent ab09045 commit ed53716
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 6 deletions.
1 change: 0 additions & 1 deletion CRM/Activity/Form/Task/PDF.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ public function buildQuickForm() {
// for them to block pdf.
// @todo debug & fix....
$this->add('select', 'document_type', ts('Document Type'), ['pdf' => ts('Portable Document Format (.pdf)')]);

}

/**
Expand Down
108 changes: 107 additions & 1 deletion CRM/Activity/Tokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class CRM_Activity_Tokens extends \Civi\Token\AbstractTokenSubscriber {

private $basicTokens;
private $customFieldTokens;
private $specialTokens;

/**
* Mapping from tokenName to api return field
Expand All @@ -53,7 +54,8 @@ class CRM_Activity_Tokens extends \Civi\Token\AbstractTokenSubscriber {
public function __construct() {
parent::__construct('activity', array_merge(
$this->getBasicTokens(),
$this->getCustomFieldTokens()
$this->getCustomFieldTokens(),
$this->getSpecialTokens()
));
}

Expand Down Expand Up @@ -148,6 +150,10 @@ public function prefetch(\Civi\Token\Event\TokenValueEvent $e) {
]);
$prefetch['activity'] = $activities['values'];

// Get data for special tokens
list($prefetch['activityContact'], $prefetch['contact'])
= self::prefetchSpecialTokens($this->activeTokens, $activityIds);

// Store the activity types if needed
if (in_array('activity_type', $this->activeTokens)) {
$this->activityTypes = \CRM_Core_OptionGroup::values('activity_type');
Expand All @@ -166,6 +172,54 @@ public function prefetch(\Civi\Token\Event\TokenValueEvent $e) {
return $prefetch;
}

/**
* Do the prefetch for the special tokens
* @param array $activeTokens The list of active tokens
* @param array $activityIds list of activity ids
*
* @return array the prefetched data for these tokens
* @throws \CiviCRM_API3_Exception
*/
public function prefetchSpecialTokens($activeTokens, $activityIds) {
$activityContacts = $contacts = [];
// See if we need activity contacts
$needContacts = FALSE;
foreach ($activeTokens as $token) {
if (preg_match('/^source|target|assignee/', $token)) {
$needContacts = TRUE;
break;
}
}

// If we need ActivityContacts, load them
if ($needContacts) {
$result = civicrm_api3('ActivityContact', 'get', [
'sequential' => 1,
'activity_id' => ['IN' => $activityIds],
'options' => ['limit' => 0],
]);
$contactIds = [];
$types = ['1' => 'assignee', '2' => 'source', '3' => 'target'];
foreach ($result['values'] as $ac) {
if ($ac['record_type_id'] == 2) {
$activityContacts[$ac['activity_id']][$types[$ac['record_type_id']]] = $ac['contact_id'];
}
else {
$activityContacts[$ac['activity_id']][$types[$ac['record_type_id']]][] = $ac['contact_id'];
}
$contactIds[$ac['contact_id']] = 1;
}
// @TODO only return the wanted fields
// maybe use CRM_Contact_Tokens::prefetch() ?
$result = civicrm_api3('Contact', 'get', [
'id' => ['IN' => array_keys($contactIds)],
'options' => ['limit' => 0],
]);
$contacts = $result['values'];
}
return [$activityContacts, $contacts];
}

/**
* @inheritDoc
*/
Expand Down Expand Up @@ -207,6 +261,37 @@ public function evaluateToken(\Civi\Token\TokenRow $row, $entity, $field, $prefe
elseif (isset($activity->$field)) {
$row->tokens($entity, $field, $activity->$field);
}
elseif (preg_match('/^(target|assignee|source)_/', $field, $match)) {
if ($match[1] == 'source') {
$fieldParts = explode('_', $field, 2);
$contactId = \CRM_Utils_Array::value($fieldParts[0], $prefetch['activityContact'][$activity->id]);
$wantedField = $fieldParts[1];
}
else {
$fieldParts = explode('_', $field, 3);
$contactIds = \CRM_Utils_Array::value($fieldParts[0], $prefetch['activityContact'][$activity->id]);
$selectedId = (int) $fieldParts[1] > 0 ? $fieldParts[1] - 1 : 0;
$contactId = \CRM_Utils_Array::value($selectedId, $contactIds);
$wantedField = $fieldParts[2];
}
$contact = \CRM_Utils_Array::value($contactId, $prefetch['contact']);
if (!$contact) {
$row->tokens($entity, $field, '');
}
else {
$contact = (object) $contact;
// This is OK for simple tokens, but would be better for this to be handled by
// CRM_Contact_Tokens ... but that doesn't exist yet.
$row->tokens($entity, $field, $contact->$wantedField);
}
}
elseif (preg_match('/^(targets|assignees)_count/', $field, $match)) {
$type = rtrim($match[1], 's');
;
$row->tokens($entity, $field, count(
\CRM_Utils_Array::value($type, $prefetch['activityContact'][$activity->id], [])
));
}
}

/**
Expand All @@ -230,6 +315,8 @@ protected function getBasicTokens() {
'duration' => ts('Activity Duration'),
'campaign' => ts('Activity Campaign'),
'campaign_id' => ts('Activity Campaign ID'),
'targets_count' => ts('Count of Activity Targets'),
'assignees_count' => ts('Count of Activity Assignees'),
];
if (array_key_exists('CiviCase', CRM_Core_Component::getEnabledComponents())) {
$this->basicTokens['case_id'] = ts('Activity Case ID');
Expand All @@ -249,4 +336,23 @@ protected function getCustomFieldTokens() {
return $this->customFieldTokens;
}

/**
* Get the special tokens - ie tokens that need special handling
* @return array token name => token label
*/
protected function getSpecialTokens() {
if (!isset($this->specialTokens)) {
$this->specialTokens = [];
foreach (\CRM_Core_SelectValues::contactTokens() as $label => $name) {
$match = [];
if (preg_match('/{contact\.(.*)}/', $label, $match)) {
$this->specialTokens['source_' . $match[1]] = "(Source) " . $name;
$this->specialTokens['target_N_' . $match[1]] = "(Target N) " . $name;
$this->specialTokens['assignee_N_' . $match[1]] = "(Assignee N) " . $name;
}
}
}
return $this->specialTokens;
}

}
7 changes: 4 additions & 3 deletions CRM/Core/Form/Task/PDFLetterCommon.php
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,9 @@ public static function formatMessage(&$message) {
* @param array $rows Array of \Civi\Token\TokenRow
* @param string $msgPart The name registered with the TokenProcessor
* @param string $formValues The values submitted through the form
* @return string
* $html if formValues['is_unit_test'] is true, otherwise outputs document to browser
*
* @return string|void
* $html if formValues['is_unit_test'] is true, otherwise outputs document to browser
*/
public static function renderFromRows($rows, $msgPart, $formValues) {
$html = array();
Expand All @@ -340,7 +340,8 @@ public static function renderFromRows($rows, $msgPart, $formValues) {
}

if (!empty($formValues['is_unit_test'])) {
return $html;
// @fixme check if this is the correct separator
return implode(CRM_Utils_String::LINEFEED, $html);
}

if (!empty($html)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public function testCreateDocumentCustomFieldTokens() {
}

public function testCreateDocumentSpecialTokens() {
$this->markTestIncomplete('special tokens not yet merged - see https://github.com/civicrm/civicrm-core/pull/12012');
$activity = $this->activityCreate();
$data = [
["Source First Name: {activity.source_first_name}", "Source First Name: Anthony"],
Expand Down

0 comments on commit ed53716

Please sign in to comment.