* @copyright CiviCRM LLC https://civicrm.org/licensing
*/
+use Civi\Token\TokenProcessor;
+
/**
* Class CRM_Badge_Format_Badge.
*
if ($element) {
$value = $row[$element];
// hack to fix date field display format
- if (strpos($element, '_date')) {
+ if (in_array($element, ['event_start_date', 'event_end_date'], TRUE)) {
$value = CRM_Utils_Date::customFormat($value, "%B %E%f");
}
}
$this->imgRes = 300;
if ($img) {
- list($w, $h) = self::getImageProperties($img, $this->imgRes, $w, $h);
+ [$w, $h] = self::getImageProperties($img, $this->imgRes, $w, $h);
$this->pdf->Image($img, $x, $y, $w, $h, '', '', '', FALSE, 72, '', FALSE,
FALSE, $this->debug, FALSE, FALSE, FALSE);
}
public static function buildBadges(&$params, &$form) {
// get name badge layout info
$layoutInfo = CRM_Badge_BAO_Layout::buildLayout($params);
-
+ $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), ['schema' => ['participantId'], 'smarty' => FALSE]);
// split/get actual field names from token and individual contact image URLs
- $returnProperties = [];
+ $returnProperties = $processorTokens = [];
if (!empty($layoutInfo['data']['token'])) {
foreach ($layoutInfo['data']['token'] as $index => $value) {
- $element = '';
if ($value) {
$token = CRM_Utils_Token::getTokens($value);
- if (key($token) == 'contact') {
- $element = $token['contact'][0];
- }
- elseif (key($token) == 'event') {
+ if (strpos($value, '{event.') === 0) {
$element = $token['event'][0];
//FIX ME - we need to standardize event token names
if (substr($element, 0, 6) != 'event_') {
+ // legacy style.
$element = 'event_' . $element;
+ $returnProperties[$element] = 1;
+ // add actual field name to row element
+ $layoutInfo['data']['rowElements'][$index] = $element;
}
}
- elseif (key($token) == 'participant') {
- $element = $token['participant'][0];
+ else {
+ $tokenName = str_replace(['}', '{contact.', '{participant.'], '', $value);
+ $tokenProcessor->addMessage($tokenName, $value, 'text/plain');
+ $processorTokens[] = $tokenName;
+ $layoutInfo['data']['rowElements'][$index] = $tokenName;
}
-
- // build returnproperties for query
- $returnProperties[$element] = 1;
}
-
- // add actual field name to row element
- $layoutInfo['data']['rowElements'][$index] = $element;
}
}
// add additional required fields for query execution
- $additionalFields = ['participant_register_date', 'participant_id', 'event_id', 'contact_id', 'image_URL'];
+ $additionalFields = ['participant_id', 'event_id', 'contact_id'];
foreach ($additionalFields as $field) {
$returnProperties[$field] = 1;
}
CRM_Contact_BAO_Query::MODE_EVENT
);
- list($select, $from, $where, $having) = $query->query();
+ [$select, $from, $where, $having] = $query->query();
if (empty($where)) {
$where = "WHERE {$form->_componentClause}";
}
$dao = CRM_Core_DAO::executeQuery($queryString);
$rows = [];
+
while ($dao->fetch()) {
- $query->convertToPseudoNames($dao);
+ $tokenProcessor->addRow(['contactId' => $dao->contact_id, 'participantId' => $dao->participant_id]);
$rows[$dao->participant_id] = [];
foreach ($returnProperties as $key => $dontCare) {
- $value = $dao->$key ?? NULL;
- // Format custom fields
- if (strstr($key, 'custom_') && isset($value)) {
- $value = CRM_Core_BAO_CustomField::displayValue($value, substr($key, 7), $dao->contact_id);
- }
- $rows[$dao->participant_id][$key] = $value;
+ // we are now only resolving the 4 event tokens here.
+ $rows[$dao->participant_id][$key] = $dao->$key ?? NULL;
+ }
+ }
+ $tokenProcessor->evaluate();
+ foreach ($tokenProcessor->getRows() as $row) {
+ foreach ($processorTokens as $processorToken) {
+ $rows[$row->context['participantId']][$processorToken] = $row->render($processorToken);
}
}
// Someone else is kindly doing the refresh for us right now.
return;
}
+
+ // Get the list of expired smart groups that may need flushing
$params = [1 => [self::getCacheInvalidDateTime(), 'String']];
- $groupsDAO = CRM_Core_DAO::executeQuery("SELECT id FROM civicrm_group WHERE cache_date <= %1", $params);
+ $groupsThatMayNeedToBeFlushedSQL = "SELECT id FROM civicrm_group WHERE (saved_search_id IS NOT NULL OR children <> '') AND (cache_date <= %1 OR cache_date IS NULL)";
+ $groupsDAO = CRM_Core_DAO::executeQuery($groupsThatMayNeedToBeFlushedSQL, $params);
$expiredGroups = [];
while ($groupsDAO->fetch()) {
$expiredGroups[] = $groupsDAO->id;
}
- if (!empty($expiredGroups)) {
- $expiredGroups = implode(',', $expiredGroups);
- CRM_Core_DAO::executeQuery("DELETE FROM civicrm_group_contact_cache WHERE group_id IN ({$expiredGroups})");
+ if (empty($expiredGroups)) {
+ // There are no expired smart groups to flush
+ return;
+ }
+
+ $expiredGroupsCSV = implode(',', $expiredGroups);
+ $flushSQLParams = [1 => [$expiredGroupsCSV, 'CommaSeparatedIntegers']];
+ // Now check if we actually have any entries in the smart groups to flush
+ $groupsHaveEntriesToFlushSQL = 'SELECT group_id FROM civicrm_group_contact_cache gc WHERE group_id IN (%1) LIMIT 1';
+ $groupsHaveEntriesToFlush = (bool) CRM_Core_DAO::singleValueQuery($groupsHaveEntriesToFlushSQL, $flushSQLParams);
+
+ if ($groupsHaveEntriesToFlush) {
+ CRM_Core_DAO::executeQuery("DELETE FROM civicrm_group_contact_cache WHERE group_id IN (%1)", [1 => [$expiredGroupsCSV, 'CommaSeparatedIntegers']]);
// Clear these out without resetting them because we are not building caches here, only clearing them,
// so the state is 'as if they had never been built'.
- CRM_Core_DAO::executeQuery("UPDATE civicrm_group SET cache_date = NULL WHERE id IN ({$expiredGroups})");
+ CRM_Core_DAO::executeQuery("UPDATE civicrm_group SET cache_date = NULL WHERE id IN (%1)", [1 => [$expiredGroupsCSV, 'CommaSeparatedIntegers']]);
}
$lock->release();
}
*/
public $_templates;
+ /**
+ * Email addresses to send to.
+ *
+ * @var array
+ */
+ protected $emails = [];
+
/**
* Store "to" contact details.
* @var array
// are having to re-write contactIds afterwards due to this inappropriate variable setting
// If we don't have any contact IDs, use the logged in contact ID
$form->_contactIds = $form->_contactIds ?: [CRM_Core_Session::getLoggedInContactID()];
-
- $fromEmailValues = CRM_Core_BAO_Email::getFromEmail();
-
- if (empty($fromEmailValues)) {
- CRM_Core_Error::statusBounce(ts('Your user record does not have a valid email address and no from addresses have been configured.'));
- }
-
- $form->_emails = $fromEmailValues;
- $defaults = [];
- $form->_fromEmails = $fromEmailValues;
- if (is_numeric(key($form->_fromEmails))) {
- $emailID = (int) key($form->_fromEmails);
- $defaults = CRM_Core_BAO_Email::getEmailSignatureDefaults($emailID);
- }
- if (!Civi::settings()->get('allow_mail_from_logged_in_contact')) {
- $defaults['from_email_address'] = current(CRM_Core_BAO_Domain::getNameAndEmail(FALSE, TRUE));
- }
- $form->setDefaults($defaults);
}
/**
if ($to->getValue()) {
foreach ($this->getEmails($to) as $value) {
$contactId = $value['contact_id'];
- $email = $value['email'];
if ($contactId) {
$this->_contactIds[] = $this->_toContactIds[] = $contactId;
- $this->_toContactEmails[] = $email;
$this->_allContactIds[] = $contactId;
}
}
$this->add('text', 'subject', ts('Subject'), ['size' => 50, 'maxlength' => 254], TRUE);
- $this->add('select', 'from_email_address', ts('From'), $this->_fromEmails, TRUE);
+ $this->add('select', 'from_email_address', ts('From'), $this->getFromEmails(), TRUE);
CRM_Mailing_BAO_Mailing::commonCompose($this);
CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'templates/CRM/Contact/Form/Task/EmailCommon.js', 0, 'html-header');
}
+ /**
+ * Set relevant default values.
+ *
+ * @return array
+ *
+ * @throws \API_Exception
+ * @throws \CRM_Core_Exception
+ */
+ public function setDefaultValues(): array {
+ $defaults = parent::setDefaultValues();
+ $fromEmails = $this->getFromEmails();
+ if (is_numeric(key($fromEmails))) {
+ $emailID = (int) key($fromEmails);
+ $defaults = CRM_Core_BAO_Email::getEmailSignatureDefaults($emailID);
+ }
+ if (!Civi::settings()->get('allow_mail_from_logged_in_contact')) {
+ $defaults['from_email_address'] = current(CRM_Core_BAO_Domain::getNameAndEmail(FALSE, TRUE));
+ }
+ return $defaults;
+ }
+
/**
* Process the form after the input has been submitted and validated.
*
*/
public function postProcess() {
$this->bounceIfSimpleMailLimitExceeded(count($this->_contactIds));
-
- // check and ensure that
$formValues = $this->controller->exportValues($this->getName());
$this->submit($formValues);
}
* @param int $count
* The number of emails the user is attempting to send
*/
- protected function bounceIfSimpleMailLimitExceeded($count) {
+ protected function bounceIfSimpleMailLimitExceeded($count): void {
$limit = Civi::settings()->get('simple_mail_limit');
if ($count > $limit) {
CRM_Core_Error::statusBounce(ts('Please do not use this task to send a lot of emails (greater than %1). Many countries have legal requirements when sending bulk emails and the CiviMail framework has opt out functionality and domain tokens to help meet these.',
* @throws \Civi\API\Exception\UnauthorizedException
* @throws \API_Exception
*/
- public function submit($formValues) {
+ public function submit($formValues): void {
$this->saveMessageTemplate($formValues);
$from = $formValues['from_email_address'] ?? NULL;
if (!isset($this->_contactDetails[$contactId])) {
continue;
}
- $email = $this->_toContactEmails[$key];
+ $email = $this->getEmail($key);
// prevent duplicate emails if same email address is selected CRM-4067
// we should allow same emails for different contacts
$details = $this->_contactDetails[$contactId];
return NULL;
}
+ /**
+ * @return array
+ */
+ protected function getFromEmails(): array {
+ $fromEmailValues = CRM_Core_BAO_Email::getFromEmail();
+
+ if (empty($fromEmailValues)) {
+ CRM_Core_Error::statusBounce(ts('Your user record does not have a valid email address and no from addresses have been configured.'));
+ }
+ return $fromEmailValues;
+ }
+
+ /**
+ * Get the relevant emails.
+ *
+ * @param int $index
+ *
+ * @return string
+ */
+ protected function getEmail(int $index): string {
+ if (empty($this->emails)) {
+ $toEmails = explode(',', $this->getSubmittedValue('to'));
+ foreach ($toEmails as $value) {
+ $parts = explode('::', $value);
+ $this->emails[] = $parts[1];
+ }
+ }
+ return $this->emails[$index];
+ }
+
}
public function evaluateToken(TokenRow $row, $entity, $field, $prefetch = NULL) {
$this->prefetch = (array) $prefetch;
$fieldValue = $this->getFieldValue($row, $field);
+ if (is_array($fieldValue)) {
+ // eg. role_id for participant would be an array here.
+ $fieldValue = implode(',', $fieldValue);
+ }
if ($this->isPseudoField($field)) {
if (!empty($fieldValue)) {
/**
* Different type of Event Tokens.
*
+ * @deprecated
+ *
* @return array
*/
- public static function eventTokens() {
- return [
- '{event.event_id}' => ts('Event ID'),
- '{event.title}' => ts('Event Title'),
- '{event.start_date}' => ts('Event Start Date'),
- '{event.end_date}' => ts('Event End Date'),
- '{event.event_type}' => ts('Event Type'),
- '{event.summary}' => ts('Event Summary'),
- '{event.contact_email}' => ts('Event Contact Email'),
- '{event.contact_phone}' => ts('Event Contact Phone'),
- '{event.description}' => ts('Event Description'),
- '{event.location}' => ts('Event Location'),
- '{event.fee_amount}' => ts('Event Fees'),
- '{event.info_url}' => ts('Event Info URL'),
- '{event.registration_url}' => ts('Event Registration URL'),
- '{event.balance}' => ts('Event Balance'),
- ];
+ public static function eventTokens(): array {
+ $tokenProcessor = new TokenProcessor(Civi::dispatcher(), ['schema' => ['eventId']]);
+ $allTokens = $tokenProcessor->listTokens();
+ foreach (array_keys($allTokens) as $token) {
+ if (strpos($token, '{domain.') === 0) {
+ unset($allTokens[$token]);
+ }
+ }
+ return $allTokens;
}
/**
*/
public static function participantTokens(): array {
$tokens = [
- '{participant.participant_status_id}' => 'Status ID',
- '{participant.participant_role_id}' => 'Participant Role (ID)',
- '{participant.participant_register_date}' => 'Register date',
- '{participant.participant_source}' => 'Participant Source',
- '{participant.participant_fee_level}' => 'Fee level',
- '{participant.participant_fee_amount}' => 'Fee Amount',
- '{participant.participant_registered_by_id}' => 'Registered By Participant ID',
+ '{participant.status_id}' => 'Status ID',
+ '{participant.role_id}' => 'Participant Role (ID)',
+ '{participant.register_date}' => 'Register date',
+ '{participant.source}' => 'Participant Source',
+ '{participant.fee_level}' => 'Fee level',
+ '{participant.fee_amount}' => 'Fee Amount',
+ '{participant.registered_by_id}' => 'Registered By Participant ID',
'{participant.transferred_to_contact_id}' => 'Transferred to Contact ID',
- '{participant.participant_role}' => 'Participant Role (label)',
- '{participant.event_title}' => 'Event Title',
- '{participant.event_start_date}' => 'Event Start Date',
- '{participant.event_end_date}' => 'Event End Date',
+ '{participant.role_id:label}' => 'Participant Role (label)',
'{participant.fee_label}' => 'Fee Label',
- '{participant.default_role_id}' => 'Default Role',
- '{participant.template_title}' => 'Event Template Title',
- '{participant.currency}' => 'Currency',
- '{participant.participant_note}' => 'Participant Note',
];
$customFields = CRM_Core_BAO_CustomField::getFields('Participant');
--- /dev/null
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Class CRM_Event_ParticipantTokens
+ *
+ * Generate "participant.*" tokens.
+ */
+class CRM_Event_ParticipantTokens extends CRM_Core_EntityTokens {
+
+ /**
+ * Get the entity name for api v4 calls.
+ *
+ * @return string
+ */
+ protected function getApiEntityName(): string {
+ return 'Participant';
+ }
+
+ /**
+ * @return array
+ */
+ public function getCurrencyFieldName(): array {
+ return ['fee_currency'];
+ }
+
+}
+--------------------------------------------------------------------+
*/
+use Civi\ActionSchedule\Event\MailingQueryEvent;
+
/**
* Class CRM_Event_Tokens
*
* implementation which is not tied to scheduled reminders, although
* that is outside the current scope.
*/
-class CRM_Event_Tokens extends \Civi\Token\AbstractTokenSubscriber {
+class CRM_Event_Tokens extends CRM_Core_EntityTokens {
/**
- * Class constructor.
+ * Get the entity name for api v4 calls.
+ *
+ * @return string
+ */
+ protected function getApiEntityName(): string {
+ return 'Event';
+ }
+
+ /**
+ * Get all tokens.
+ *
+ * This function will be removed once the parent class can determine it.
*/
- public function __construct() {
- parent::__construct('event', array_merge(
+ public function getAllTokens(): array {
+ return array_merge(
[
'event_type' => ts('Event Type'),
'title' => ts('Event Title'),
'info_url' => ts('Event Info URL'),
'registration_url' => ts('Event Registration URL'),
'fee_amount' => ts('Event Fee'),
- 'contact_email' => ts('Event Contact (Email)'),
- 'contact_phone' => ts('Event Contact (Phone)'),
+ 'contact_email' => ts('Event Contact Email'),
+ 'contact_phone' => ts('Event Contact Phone'),
'balance' => ts('Event Balance'),
],
CRM_Utils_Token::getCustomFieldTokens('Event')
- ));
+ );
}
/**
*/
public function checkActive(\Civi\Token\TokenProcessor $processor) {
// Extracted from scheduled-reminders code. See the class description.
- return !empty($processor->context['actionMapping'])
- && $processor->context['actionMapping']->getEntity() === 'civicrm_participant';
+ return ((!empty($processor->context['actionMapping'])
+ && $processor->context['actionMapping']->getEntity() === 'civicrm_participant'))
+ || in_array($this->getEntityIDField(), $processor->context['schema'], TRUE);
}
/**
*
* @param \Civi\ActionSchedule\Event\MailingQueryEvent $e
*/
- public function alterActionScheduleQuery(\Civi\ActionSchedule\Event\MailingQueryEvent $e) {
+ public function alterActionScheduleQuery(MailingQueryEvent $e): void {
if ($e->mapping->getEntity() !== 'civicrm_participant') {
return;
}
$row->tokens($entity, $field, $actionSearchResult->$field);
}
elseif ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) {
- $row->customToken($entity, $cfID, $actionSearchResult->entity_id);
+ $row->customToken($entity, $cfID, $actionSearchResult->event_id);
}
else {
$row->tokens($entity, $field, '');
// set qfkey so that pager picks it up and use it in the "Next > Last >>" links.
// FIXME: Note setting it in $_GET doesn't work, since pager generates link based on QUERY_STRING
+ if (!isset($_SERVER['QUERY_STRING'])) {
+ // in php 7.4 can do this with less lines with ??=
+ $_SERVER['QUERY_STRING'] = '';
+ }
$_SERVER['QUERY_STRING'] .= "&qfKey={$this->controller->_key}";
}
}
/**
- * Updated a message token within a template.
+ * Updated a message token within a scheduled reminder.
*
* @param CRM_Queue_TaskContext $ctx
* @param string $old
return TRUE;
}
+ /**
+ * Updated a message token within a template.
+ *
+ * @param CRM_Queue_TaskContext $ctx
+ * @param string $old
+ * @param string $new
+ * @param $version
+ *
+ * @return bool
+ */
+ public static function updatePrintLabelToken($ctx, string $old, string $new, $version):bool {
+ $messageObj = new CRM_Upgrade_Incremental_MessageTemplates($version);
+ $messageObj->replaceTokenInPrintLabel($old, $new);
+ return TRUE;
+ }
+
/**
* Re-save any valid values from contribute settings into the normal setting
* format.
],
[
'version' => '5.43.alpha1',
- 'upgrade_descriptor' => ts('Missed text version from 5.20'),
+ 'upgrade_descriptor' => ts('Missed templates from earlier versions'),
'templates' => [
['name' => 'contribution_online_receipt', 'type' => 'text'],
+ ['name' => 'case_activity', 'type' => 'html'],
+ ['name' => 'case_activity', 'type' => 'text'],
+ ['name' => 'case_activity', 'type' => 'subject'],
],
],
];
");
}
+ /**
+ * Replace a token with the new preferred option in a print label.
+ *
+ * @param string $old
+ * @param string $new
+ */
+ public function replaceTokenInPrintLabel(string $old, string $new): void {
+ $oldToken = '{' . $old . '}';
+ $newToken = '{' . $new . '}';
+ CRM_Core_DAO::executeQuery("UPDATE civicrm_print_label
+ SET
+ data = REPLACE(data, '$oldToken', '$newToken')
+ ");
+ }
+
/**
* Get the upgrade messages.
*/
$this->addTask('Replace membership type token in action schedule',
'updateActionScheduleToken', 'membership.type', 'membership.membership_type_id:label', $rev
);
+ $this->addTask('Replace duplicate event title token in event badges',
+ 'updatePrintLabelToken', 'participant.event_title', 'event.title', $rev
+ );
+ $this->addTask('Replace duplicate event start date token in event badges',
+ 'updatePrintLabelToken', 'participant.event_start_date', 'event.start_date', $rev
+ );
+ $this->addTask('Replace duplicate event end date token in event badges',
+ 'updatePrintLabelToken', 'participant.event_end_date', 'event.end_date', $rev
+ );
+ $this->addTask('Update participant status id token in event badges',
+ 'updatePrintLabelToken', 'participant.participant_status_id', 'participant.status_id', $rev
+ );
+ $this->addTask('Update participant role id token in event badges',
+ 'updatePrintLabelToken', 'participant.participant_role_id', 'participant.role_id', $rev
+ );
+ $this->addTask('Update participant role label token in event badges',
+ 'updatePrintLabelToken', 'participant.participant_role', 'participant.role_id:label', $rev
+ );
+ $this->addTask('Update participant register date token in event badges',
+ 'updatePrintLabelToken', 'participant.participant_register_date', 'participant.register_date', $rev
+ );
+ $this->addTask('Update participant source token in event badges',
+ 'updatePrintLabelToken', 'participant.participant_source', 'participant.source', $rev
+ );
+ $this->addTask('Update participant fee level token in event badges',
+ 'updatePrintLabelToken', 'participant.participant_fee_level', 'participant.fee_level', $rev
+ );
+ $this->addTask('Update participant fee amount token in event badges',
+ 'updatePrintLabelToken', 'participant.participant_fee_amount', 'participant.fee_amount', $rev
+ );
+ $this->addTask('Update participant registered by id token in event badges',
+ 'updatePrintLabelToken', 'participant.participant_registered_by_id', 'participant.registered_by_id', $rev
+ );
+
}
/**
[]
))->addTag('kernel.event_subscriber')->setPublic(TRUE);
}
+ $container->setDefinition('crm_participant_tokens', new Definition(
+ 'CRM_Event_ParticipantTokens',
+ []
+ ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
$container->setDefinition('crm_contribution_recur_tokens', new Definition(
'CRM_Contribute_RecurTokens',
[]
* Load token data.
*
* @param \Civi\Token\Event\TokenValueEvent $e
+ *
* @throws TokenException
+ * @throws \CRM_Core_Exception
*/
public function onEvaluate(TokenValueEvent $e) {
- // For reasons unknown, replaceHookTokens used to require a pre-computed list of
- // hook *categories* (aka entities aka namespaces). We cache
- // this in the TokenProcessor's context but can likely remove it now.
-
- $e->getTokenProcessor()->context['hookTokenCategories'] = \CRM_Utils_Token::getTokenCategories();
-
$messageTokens = $e->getTokenProcessor()->getMessageTokens()['contact'] ?? [];
+ if (empty($messageTokens)) {
+ return;
+ }
+ $this->fieldMetadata = (array) civicrm_api4('Contact', 'getfields', ['checkPermissions' => FALSE], 'name');
foreach ($e->getRows() as $row) {
- if (empty($row->context['contactId'])) {
+ if (empty($row->context['contactId']) && empty($row->context['contact'])) {
continue;
}
unset($swapLocale);
$swapLocale = empty($row->context['locale']) ? NULL : \CRM_Utils_AutoClean::swapLocale($row->context['locale']);
- /** @var int $contactId */
- $contactId = $row->context['contactId'];
if (empty($row->context['contact'])) {
- $contact = $this->getContact($contactId, $messageTokens);
+ $row->context['contact'] = $this->getContact($row->context['contactId'], $messageTokens);
}
- else {
- $contact = $row->context['contact'];
+
+ foreach ($messageTokens as $token) {
+ if ($token === 'checksum') {
+ $cs = \CRM_Contact_BAO_Contact_Utils::generateChecksum($row->context['contactId'],
+ NULL,
+ NULL,
+ $row->context['hash'] ?? NULL
+ );
+ $row->format('text/html')
+ ->tokens('contact', $token, "cs={$cs}");
+ }
+ elseif (!empty($row->context['contact'][$token]) &&
+ $this->isDateField($token)
+ ) {
+ // Handle dates here, for now. Standardise with other token entities next round
+ $row->format('text/plain')->tokens('contact', $token, \CRM_Utils_Date::customFormat($row->context['contact'][$token]));
+ }
+ elseif (
+ ($row->context['contact'][$token] ?? '') == 0
+ && $this->isBooleanField($token)) {
+ // Note this will be the default behaviour once we fetch with apiv4.
+ $row->format('text/plain')->tokens('contact', $token, '');
+ }
+ elseif ($token === 'signature_html') {
+ $row->format('text/html')->tokens('contact', $token, html_entity_decode($row->context['contact'][$token]));
+ }
+ else {
+ $row->format('text/html')
+ ->tokens('contact', $token, $row->context['contact'][$token] ?? '');
+ }
}
- $row->context('contact', $contact);
}
}
+ /**
+ * Is the given field a boolean field.
+ *
+ * @param string $fieldName
+ *
+ * @return bool
+ */
+ public function isBooleanField(string $fieldName): bool {
+ // no metadata for these 2 non-standard fields
+ // @todo - fix to api v4 & have metadata for all fields. Migrate contact_is_deleted
+ // to {contact.is_deleted}. on hold feels like a token that exists by
+ // accident & could go.... since it's not from the main entity.
+ if (in_array($fieldName, ['contact_is_deleted', 'on_hold'])) {
+ return TRUE;
+ }
+ if (empty($this->getFieldMetadata()[$fieldName])) {
+ return FALSE;
+ }
+ return $this->getFieldMetadata()[$fieldName]['data_type'] === 'Boolean';
+ }
+
+ /**
+ * Is the given field a date field.
+ *
+ * @param string $fieldName
+ *
+ * @return bool
+ */
+ public function isDateField(string $fieldName): bool {
+ if (empty($this->getFieldMetadata()[$fieldName])) {
+ return FALSE;
+ }
+ return in_array($this->getFieldMetadata()[$fieldName]['data_type'], ['Timestamp', 'Date'], TRUE);
+ }
+
+ /**
+ * Get the metadata for the available fields.
+ *
+ * @return array
+ */
+ protected function getFieldMetadata(): array {
+ if (empty($this->fieldMetadata)) {
+ try {
+ // Tests fail without checkPermissions = FALSE
+ $this->fieldMetadata = (array) civicrm_api4('Contact', 'getfields', ['checkPermissions' => FALSE], 'name');
+ }
+ catch (\API_Exception $e) {
+ $this->fieldMetadata = [];
+ }
+ }
+ return $this->fieldMetadata;
+ }
+
/**
* Apply the various CRM_Utils_Token helpers.
*
* @param \Civi\Token\Event\TokenRenderEvent $e
+ *
+ * @throws \CRM_Core_Exception
*/
- public function onRender(TokenRenderEvent $e) {
+ public function onRender(TokenRenderEvent $e): void {
$isHtml = ($e->message['format'] === 'text/html');
$useSmarty = !empty($e->context['smarty']);
if (!empty($e->context['contact'])) {
- \CRM_Utils_Token::replaceGreetingTokens($e->string, $e->context['contact'], $e->context['contact']['contact_id'] ?? $e->context['contactId'], NULL, $useSmarty);
+ // @todo - remove this - it simply removes the last unresolved tokens before
+ // they break smarty.
+ // historically it was only called when context['contact'] so that is
+ // retained but it only works because it's almost always true.
+ $remainingTokens = array_keys(\CRM_Utils_Token::getTokens($e->string));
+ if (!empty($remainingTokens)) {
+ $e->string = \CRM_Utils_Token::replaceHookTokens($e->string, $e->context['contact'], $remainingTokens);
+ }
}
if ($useSmarty) {
$mappedFields = [
'email_greeting' => 'email_greeting_display',
'postal_greeting' => 'postal_greeting_display',
- 'addressee' => 'address_display',
+ 'addressee' => 'addressee_display',
];
+ if (!empty($returnProperties['checksum'])) {
+ $returnProperties['hash'] = 1;
+ }
+
foreach ($mappedFields as $tokenName => $realName) {
if (in_array($tokenName, $requiredFields, TRUE)) {
$returnProperties[$realName] = 1;
// Regex examples: '{foo.bar}', '{foo.bar|whiz}'
// Regex counter-examples: '{foobar}', '{foo bar}', '{$foo.bar}', '{$foo.bar|whiz}', '{foo.bar|whiz{bang}}'
// Key observations: Civi tokens MUST have a `.` and MUST NOT have a `$`. Civi filters MUST NOT have `{}`s or `$`s.
- $tokRegex = '([\w]+)\.([\w:]+)';
+ $tokRegex = '([\w]+)\.([\w:\.]+)';
$filterRegex = '(\w+)';
$event->string = preg_replace_callback(";\{$tokRegex(?:\|$filterRegex)?\};", $getToken, $message['string']);
$this->dispatcher->dispatch('civi.token.render', $event);
/**
* Cleanup after test.
*
- * @throws \CRM_Core_Exception
- * @throws \API_Exception
*/
public function tearDown(): void {
- $this->quickCleanup(['civicrm_batch']);
+ $this->quickCleanup(['civicrm_batch', 'civicrm_file', 'civicrm_entity_file']);
parent::tearDown();
}
*
* @throws \CRM_Core_Exception
*/
- public function testGetBatchFinancialItems() {
+ public function testGetBatchFinancialItems(): void {
// create two contributions: one check and one credit card
/**
* Test testExportFinancialBatch.
+ *
+ * @throws \CRM_Core_Exception
*/
- public function testExportFinancialBatch() {
+ public function testExportFinancialBatch(): void {
$this->createLoggedInUser();
$batchParams = ['title' => 'Test Batch'];
$batchParams['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Exported');
if ($this->callAPISuccessGetCount('membership', ['id' => $this->_membershipTypeID])) {
$this->membershipTypeDelete(['id' => $this->_membershipTypeID]);
}
- if ($this->callAPISuccessGetCount('MembershipStatus', ['id' => $this->_membershipStatusID])) {
- $this->membershipStatusDelete($this->_membershipStatusID);
- }
$this->contactDelete($this->_contactID);
$this->contactDelete($this->_contactID2);
$this->contactDelete($this->_orgContactID);
+ parent::tearDown();
}
/**
Civi::settings()->set('allow_mail_from_logged_in_contact', 1);
$loggedInContactID = $this->createLoggedInUser();
- /* @var CRM_Contact_Form_Task_Email $form*/
- $form = $this->getFormObject('CRM_Contact_Form_Task_Email');
-
- for ($i = 0; $i < 27; $i++) {
- $email = 'spy' . $i . '@secretsquirrels.com';
- $contactID = $this->individualCreate(['email' => $email]);
- $form->_contactIds[$contactID] = $contactID;
- $form->_toContactEmails[$this->callAPISuccessGetValue('Email', ['return' => 'id', 'email' => $email])] = $email;
- }
- $deceasedContactID = $this->individualCreate(['is_deceased' => 1, 'email' => 'dead@example.com']);
- $form->_contactIds[$deceasedContactID] = $deceasedContactID;
- $form->_toContactEmails[$this->callAPISuccessGetValue('Email', ['return' => 'id', 'email' => 'dead@example.com'])] = 'dead@example.com';
-
$loggedInEmail = $this->callAPISuccess('Email', 'create', [
'email' => 'mickey@mouse.com',
'location_type_id' => 1,
'signature_text' => 'This is a test Signature',
'signature_html' => '<p>This is a test Signature</p>',
]);
+
+ $to = $form_contactIds = $form_toContactEmails = [];
+ for ($i = 0; $i < 27; $i++) {
+ $email = 'spy' . $i . '@secretsquirrels.com';
+ $contactID = $this->individualCreate(['email' => $email]);
+ $form_contactIds[$contactID] = $contactID;
+ $to[] = $contactID . '::' . $email;
+ }
+ $deceasedContactID = $this->individualCreate(['is_deceased' => 1, 'email' => 'dead@example.com']);
+ $to[] = $deceasedContactID . '::' . 'email@example.com';
+ /* @var CRM_Contact_Form_Task_Email $form*/
+ $form = $this->getFormObject('CRM_Contact_Form_Task_Email', [
+ 'to' => implode(',', $to),
+ ]);
+ $form->_contactIds = $form_contactIds;
+ $form->_contactIds[$deceasedContactID] = $deceasedContactID;
+
$form->_allContactIds = $form->_toContactIds = $form->_contactIds;
- $form->_emails = [$loggedInEmail['id'] => 'mickey@mouse.com'];
$form->_fromEmails = [$loggedInEmail['id'] => 'mickey@mouse.com'];
// This rule somehow disappears if there's a form-related test before us,
// so register it again. See packages/HTML/QuickForm/file.php.
+ // update - actually - it's never registered. Even in form made
+ // I can see it missing - It's really weird.
$form->registerRule('maxfilesize', 'callback', '_ruleCheckMaxFileSize', 'HTML_QuickForm_file');
$form->isSearchContext = FALSE;
$form->buildForm();
$form->submit(array_merge($form->_defaultValues, [
+ // @todo - it's better to pass these into getForm
+ // and access them on the form using $this->getSubmittedValue().
'from_email_address' => $loggedInEmail['id'],
'subject' => 'Really interesting stuff',
'bcc_id' => $bcc,
/**
* Clean up DB.
- *
- * @throws \CRM_Core_Exception|\API_Exception
*/
public function tearDown(): void {
$this->quickCleanUpFinancialEntities();
/**
* Test that correct contribution status is fetched for both live and test contributions.
*/
- public function testLiveAndTestContributionStatus() {
+ public function testLiveAndTestContributionStatus(): void {
$paymentProcessorID = $this->paymentProcessorCreate(['payment_processor_type_id' => 'Dummy']);
$form = $this->getThankYouFormWithContribution($paymentProcessorID, FALSE, FALSE);
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Test Email task.
+ *
+ * @package CiviCRM_APIv3
+ * @subpackage API_Contribution
+ * @group headless
+ */
+class CRM_Contribute_Form_Task_EmailTest extends CiviUnitTestCase {
+
+ /**
+ * Clean up after each test.
+ *
+ * @throws \CRM_Core_Exception
+ * @throws \API_Exception
+ */
+ public function tearDown(): void {
+ $this->quickCleanUpFinancialEntities();
+ parent::tearDown();
+ }
+
+ /**
+ * Test that email tokens are rendered.
+ */
+ public function testEmailTokens(): void {
+ Civi::settings()->set('max_attachments', 0);
+ $contact1 = $this->individualCreate();
+ $contact2 = $this->individualCreate();
+ $userID = $this->createLoggedInUser();
+ Civi::settings()->set('allow_mail_from_logged_in_contact', TRUE);
+ $this->callAPISuccess('Email', 'create', [
+ 'contact_id' => $userID,
+ 'email' => 'benny_jetts@example.com',
+ 'signature_html' => 'Benny, Benny',
+ 'is_primary' => 1,
+ ]);
+ $contribution1 = $this->contributionCreate(['contact_id' => $contact2]);
+ $contribution2 = $this->contributionCreate(['total_amount' => 999, 'contact_id' => $contact1]);
+ $form = $this->getFormObject('CRM_Contribute_Form_Task_Email', [
+ 'cc_id' => '',
+ 'bcc_id' => '',
+ 'to' => implode(',', [
+ $contact1 . '::teresajensen-nielsen65@spamalot.co.in',
+ $contact2 . '::bob@example.com',
+ ]),
+ 'subject' => '{contact.display_name}',
+ 'text_message' => '{contribution.total_amount}',
+ 'html_message' => '{domain.name}',
+ ], [], [
+ 'radio_ts' => 'ts_sel',
+ 'task' => CRM_Core_Task::TASK_EMAIL,
+ 'mark_x_' . $contribution1 => 1,
+ 'mark_x_' . $contribution2 => 1,
+ ]);
+ $form->set('cid', $contact1 . ',' . $contact2);
+ $form->buildForm();
+ $this->assertEquals('<br/><br/>--Benny, Benny', $form->_defaultValues['html_message']);
+ $form->postProcess();
+ }
+
+}
'{contact.display_name}',
// funny legacy contact token
'{contact.gender}',
- // funny legacy contact token
- '{contact.gender_id}',
// domain token
'{domain.name}',
// action-scheduler token
]);
// Note: The behavior of domain-tokens on a scheduled reminder is undefined. All we
// can really do is check that it has something.
- $someTokensExpected = 'Churmondleia Ōtākou;;Female;;Female;;[a-zA-Z0-9 ]+;;Phone Call';
+ $someTokensExpected = 'Churmondleia Ōtākou;;Female;;[a-zA-Z0-9 ]+;;Phone Call';
$manyTokensExpected = sprintf('%s;;Dear Churmondleia;;%s', $someTokensExpected, '{contactCustomTokenValue}');
// In this example, we use a lot of tokens cutting across multiple components.
/**
* Clean up after test.
- *
- * @throws \Exception
*/
public function tearDown(): void {
- $this->quickCleanup([], TRUE);
+ $this->quickCleanup(['civicrm_file', 'civicrm_entity_file'], TRUE);
parent::tearDown();
}
/**
* Test creating a custom field.
*/
- public function testCreateCustomField() {
+ public function testCreateCustomField(): void {
$customGroup = $this->createCustomField();
$customFieldID = $this->assertDBNotNull('CRM_Core_DAO_CustomField', $customGroup['id'], 'id', 'custom_group_id',
'Database check for created CustomField.'
/**
* Check that outputting the display value for a file field with No description doesn't generate error
*/
- public function testFileDisplayValueNoDescription() {
+ public function testFileDisplayValueNoDescription(): void {
$customGroup = $this->customGroupCreate([
'extends' => 'Individual',
'title' => 'Test Contact File Custom Group',
';
$expected .= $this->getExpectedContactOutput($address['id'], $tokenData, $messageContent['html']);
$this->assertEquals($expected, $messageContent['html']);
+ $textDifferences = [
+ '<p>',
+ '</p>',
+ '<a href="http://civicrm.org" ',
+ 'target="_blank">',
+ '</a>',
+ ];
+ foreach ($textDifferences as $html) {
+ $expected = str_replace($html, '', $expected);
+ }
$this->assertEquals($expected, $messageContent['text']);
$checksum_position = strpos($messageContent['subject'], 'cs=');
$this->assertTrue($checksum_position !== FALSE);
country:United States
phone:123-456
phone_ext:77
-phone_type_id:
+phone_type_id:2
phone_type:Mobile
email:anthony_anderson@civicrm.org
on_hold:
signature_text:Yours sincerely
-signature_html:<p>Yours</p>
+signature_html:<p>Yours</p>
im_provider:1
im:IM Screen Name
openid:OpenID
];
}
- public function testNewPriceField() {
+ public function testNewPriceField(): void {
$this->createLoggedInUser();
$priceSetId = $this->callAPISuccess('PriceSet', 'create', [
<?php
+use Civi\Api4\PrintLabel;
+
/**
* Test CRM_Event_Form_Registration functions.
*
use CRMTraits_Custom_CustomDataTrait;
+ public function tearDown(): void {
+ $this->quickCleanup(['civicrm_participant', 'civicrm_print_label'], TRUE);
+ parent::tearDown();
+ }
+
/**
* Test the the submit function on the event participant submit function.
*/
public function testSubmit(): void {
$this->createCustomGroupWithFieldOfType(['extends' => 'Participant']);
- $contactID = $this->individualCreate();
- $participantID = $this->participantCreate(['contact_id' => $contactID]);
+ $contactID = $this->individualCreate(['employer_id' => 1]);
+ $participantID = $this->participantCreate([
+ 'contact_id' => $contactID,
+ 'fee_level' => 'low',
+ ]);
+
+ $badgeLayout = PrintLabel::get()->addSelect('data')->execute()->first();
+ $values = [
+ 'data' => array_merge($badgeLayout['data'], ['token' => [], 'font_name' => [''], 'font_size' => [], 'text_alignment' => []]),
+ ];
+ foreach (array_keys($this->getAvailableTokens()) as $id => $token) {
+ $index = $id + 1;
+ $values['data']['token'][$index] = $token;
+ $values['data']['font_name'][$index] = 'dejavusans';
+ $values['data']['font_size'][$index] = '20';
+ $values['data']['font_style'][$index] = '';
+ $values['data']['text_alignment'][$index] = 'C';
+ }
+ PrintLabel::update()->addWhere('id', '=', 1)->setValues($values)->execute();
$_REQUEST['context'] = 'view';
$_REQUEST['id'] = $participantID;
catch (CRM_Core_Exception_PrematureExitException $e) {
$tokens = $e->errorData['formattedRow']['token'];
$this->assertEquals([
- 1 => [
- 'value' => 'Annual CiviCRM meet',
- 'font_name' => 'dejavusans',
- 'font_size' => '9',
- 'font_style' => '',
- 'text_alignment' => 'L',
- 'token' => '{event.title}',
- ],
- 2 =>
- [
- 'value' => 'Mr. Anthony Anderson II',
- 'font_name' => 'dejavusans',
- 'font_size' => '20',
- 'font_style' => '',
- 'text_alignment' => 'C',
- 'token' => '{contact.display_name}',
- ],
- 3 =>
- [
- 'value' => NULL,
- 'font_name' => 'dejavusans',
- 'font_size' => '15',
- 'font_style' => '',
- 'text_alignment' => 'C',
- 'token' => '{contact.current_employer}',
- ],
- 4 =>
- [
- 'value' => 'October 21st',
- 'font_name' => 'dejavusans',
- 'font_size' => '9',
- 'font_style' => '',
- 'text_alignment' => 'R',
- 'token' => '{event.start_date}',
- ],
- ], $tokens);
+ 'value' => 'Annual CiviCRM meet',
+ 'font_name' => 'dejavusans',
+ 'font_size' => '20',
+ 'font_style' => '',
+ 'text_alignment' => 'C',
+ 'token' => '{event.title}',
+ ], $tokens[1]);
+ $index = 1;
+ foreach ($this->getAvailableTokens() as $token => $expected) {
+ $this->assertEquals($expected, $tokens[$index]['value'], 'failure in token ' . $token);
+ $index++;
+ }
return;
}
$this->fail('Should not be reached');
}
+ /**
+ * @return string[]
+ */
+ protected function getAvailableTokens(): array {
+ return [
+ '{event.title}' => 'Annual CiviCRM meet',
+ '{contact.display_name}' => 'Mr. Anthony Anderson II',
+ '{contact.current_employer}' => 'Default Organization',
+ '{event.start_date}' => 'October 21st',
+ '{participant.status_id}' => 2,
+ '{participant.role_id}' => 1,
+ '{participant.register_date}' => 'February 19th, 2007 12:00 AM',
+ '{participant.source}' => 'Wimbeldon',
+ '{participant.fee_level}' => 'low',
+ '{participant.fee_amount}' => NULL,
+ '{participant.registered_by_id}' => NULL,
+ '{participant.transferred_to_contact_id}' => NULL,
+ '{participant.role_id:label}' => 'Attendee',
+ '{participant.fee_label}' => NULL,
+ '{event.end_date}' => 'October 23rd',
+ '{event.id}' => 1,
+ ];
+ }
+
}
*/
public function getParticipantTokens(): array {
return [
- '{participant.participant_status_id}' => 'Status ID',
- '{participant.participant_role_id}' => 'Participant Role (ID)',
- '{participant.participant_register_date}' => 'Register date',
- '{participant.participant_source}' => 'Participant Source',
- '{participant.participant_fee_level}' => 'Fee level',
- '{participant.participant_fee_amount}' => 'Fee Amount',
- '{participant.participant_registered_by_id}' => 'Registered By Participant ID',
+ '{participant.status_id}' => 'Status ID',
+ '{participant.role_id}' => 'Participant Role (ID)',
+ '{participant.register_date}' => 'Register date',
+ '{participant.source}' => 'Participant Source',
+ '{participant.fee_level}' => 'Fee level',
+ '{participant.fee_amount}' => 'Fee Amount',
+ '{participant.registered_by_id}' => 'Registered By Participant ID',
'{participant.transferred_to_contact_id}' => 'Transferred to Contact ID',
- '{participant.participant_role}' => 'Participant Role (label)',
- '{participant.event_title}' => 'Event Title',
- '{participant.event_start_date}' => 'Event Start Date',
- '{participant.event_end_date}' => 'Event End Date',
+ '{participant.role_id:label}' => 'Participant Role (label)',
'{participant.fee_label}' => 'Fee Label',
- '{participant.default_role_id}' => 'Default Role',
- '{participant.template_title}' => 'Event Template Title',
- '{participant.currency}' => 'Currency',
- '{participant.participant_note}' => 'Participant Note',
'{participant.' . $this->getCustomFieldName('text') . '}' => 'Enter text here :: Group with field text',
];
}
];
}
+ /**
+ * Test that domain tokens are consistently rendered.
+ */
+ public function testEventTokenConsistency(): void {
+ $tokens = CRM_Core_SelectValues::eventTokens();
+ $this->assertEquals($this->getEventTokens(), $tokens);
+ $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
+ 'controller' => __CLASS__,
+ 'smarty' => FALSE,
+ 'schema' => ['eventId'],
+ ]);
+ $this->assertEquals(array_merge($tokens, $this->getDomainTokens()), $tokenProcessor->listTokens());
+ }
+
+ /**
+ * Get expected event tokens.
+ *
+ * @return string[]
+ */
+ protected function getEventTokens(): array {
+ return [
+ '{event.event_id}' => 'Event ID',
+ '{event.title}' => 'Event Title',
+ '{event.start_date}' => 'Event Start Date',
+ '{event.end_date}' => 'Event End Date',
+ '{event.event_type}' => 'Event Type',
+ '{event.summary}' => 'Event Summary',
+ '{event.contact_email}' => 'Event Contact Email',
+ '{event.contact_phone}' => 'Event Contact Phone',
+ '{event.description}' => 'Event Description',
+ '{event.location}' => 'Event Location',
+ '{event.fee_amount}' => 'Event Fee',
+ '{event.info_url}' => 'Event Info URL',
+ '{event.registration_url}' => 'Event Registration URL',
+ '{event.balance}' => 'Event Balance',
+ ];
+ }
+
}
/**
* Create default domain contacts for the two domains added during test class.
* database population.
- *
- * @throws \API_Exception
*/
public function createDomainContacts(): void {
- $this->organizationCreate(['api.Email.create' => ['email' => 'fixme.domainemail@example.org']]);
- $this->organizationCreate([
- 'organization_name' => 'Second Domain',
- 'api.Email.create' => ['email' => 'domainemail2@example.org'],
- 'api.Address.create' => [
- 'street_address' => '15 Main St',
- 'location_type_id' => 1,
- 'city' => 'Collinsville',
- 'country_id' => 1228,
- 'state_province_id' => 1003,
- 'postal_code' => 6022,
- ],
- ]);
- OptionValue::replace(FALSE)->addWhere(
- 'option_group_id:name', '=', 'from_email_address'
- )->setDefaults([
- 'is_default' => 1,
- 'name' => '"FIXME" <info@EXAMPLE.ORG>',
- 'label' => '"FIXME" <info@EXAMPLE.ORG>',
- ])->setRecords([['domain_id' => 1], ['domain_id' => 2]])->execute();
+ try {
+ $this->organizationCreate(['api.Email.create' => ['email' => 'fixme.domainemail@example.org']]);
+ $this->organizationCreate([
+ 'organization_name' => 'Second Domain',
+ 'api.Email.create' => ['email' => 'domainemail2@example.org'],
+ 'api.Address.create' => [
+ 'street_address' => '15 Main St',
+ 'location_type_id' => 1,
+ 'city' => 'Collinsville',
+ 'country_id' => 1228,
+ 'state_province_id' => 1003,
+ 'postal_code' => 6022,
+ ],
+ ]);
+ OptionValue::replace(FALSE)->addWhere(
+ 'option_group_id:name', '=', 'from_email_address'
+ )->setDefaults([
+ 'is_default' => 1,
+ 'name' => '"FIXME" <info@EXAMPLE.ORG>',
+ 'label' => '"FIXME" <info@EXAMPLE.ORG>',
+ ])->setRecords([['domain_id' => 1], ['domain_id' => 2]])->execute();
+ }
+ catch (API_Exception $e) {
+ $this->fail('failed to re-instate domain contacts ' . $e->getMessage());
+ }
}
/**
* Common teardown functions for all unit tests.
- *
- * @throws \API_Exception
*/
protected function tearDown(): void {
$this->_apiversion = 3;
* @throws \CRM_Core_Exception
*/
public function testCreateIndividualNoCacheClear(): void {
-
$contact = $this->callAPISuccess('contact', 'create', $this->_params);
- $groupID = $this->groupCreate();
+
+ $smartGroupParams = ['form_values' => ['contact_type' => ['IN' => ['Household']]]];
+ $savedSearch = CRM_Contact_BAO_SavedSearch::create($smartGroupParams);
+ $groupID = $this->groupCreate(['saved_search_id' => $savedSearch->id]);
$this->putGroupContactCacheInClearableState($groupID, $contact);