From 194ae0f4206d0de00dea44553ac8a679cd771ea4 Mon Sep 17 00:00:00 2001 From: eileen Date: Sat, 13 Jun 2020 10:50:44 +1200 Subject: [PATCH 001/834] PropertyBag - add cardNumber to getters As https://github.com/civicrm/civicrm-core/pull/17594 demonstrates, existing processors require this parameter --- Civi/Payment/PropertyBag.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Civi/Payment/PropertyBag.php b/Civi/Payment/PropertyBag.php index a97c8d113f86..fe1f239da633 100644 --- a/Civi/Payment/PropertyBag.php +++ b/Civi/Payment/PropertyBag.php @@ -38,12 +38,14 @@ class PropertyBag implements \ArrayAccess { 'billingPostalCode' => TRUE, 'billingCounty' => TRUE, 'billingCountry' => TRUE, + 'cardNumber' => TRUE, 'contactID' => TRUE, 'contact_id' => 'contactID', 'contributionID' => TRUE, 'contribution_id' => 'contributionID', 'contributionRecurID' => TRUE, 'contribution_recur_id' => 'contributionRecurID', + 'credit_card_number' => 'cardNumber', 'currency' => TRUE, 'currencyID' => 'currency', 'description' => TRUE, @@ -555,6 +557,29 @@ public function setBillingCountry($input, $label = 'default') { } /** + * Get the (generally credit) card number. + * + * @param string $label + * + * @return string + */ + public function getCardNumber($label = 'default'): string { + return $this->get('cardNumber', $label); + } + + /** + * @param string $cardNumber + * @param string $label + * + * @return \Civi\Payment\PropertyBag + */ + public function setCardNumber($cardNumber, $label = 'default'): PropertyBag { + return $this->set('cardNumber', $label, (string) $cardNumber); + } + + /** + * @param string $label + * * @return int */ public function getContactID($label = 'default'): int { @@ -564,6 +589,8 @@ public function getContactID($label = 'default'): int { /** * @param int $contactID * @param string $label + * + * @return \Civi\Payment\PropertyBag */ public function setContactID($contactID, $label = 'default') { // We don't use this because it counts zero as positive: CRM_Utils_Type::validate($contactID, 'Positive'); From 25f128c302a724c4ed4194ed57e87b6871bf38be Mon Sep 17 00:00:00 2001 From: Camilo Rodriguez Date: Fri, 3 Jul 2020 01:31:06 +0100 Subject: [PATCH 002/834] dev/core#1854: Fix Status Calculation of Overridden Membership Types --- CRM/Member/BAO/Membership.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CRM/Member/BAO/Membership.php b/CRM/Member/BAO/Membership.php index c927be1aff4a..f035c74289c4 100644 --- a/CRM/Member/BAO/Membership.php +++ b/CRM/Member/BAO/Membership.php @@ -1429,9 +1429,14 @@ public static function createRelatedMemberships(&$params, &$dao, $reset = FALSE) $relMembership = new CRM_Member_DAO_Membership(); $relMembership->contact_id = $contactId; $relMembership->owner_membership_id = $membership->id; + if ($relMembership->find(TRUE)) { $params['id'] = $relMembership->id; } + else { + unset($params['id']); + } + $params['contact_id'] = $contactId; $params['owner_membership_id'] = $membership->id; From eec58f8a91308cff16614b5a2bdf2a4e043c7d8e Mon Sep 17 00:00:00 2001 From: eileen Date: Tue, 28 Jul 2020 12:21:05 +1200 Subject: [PATCH 003/834] Move function to delete merged contacts to the Merger class and include all instances of the pair The PrevNextCache class is a bit of a mess. It has 2 roles 1) be a cache 2) provide functionality specific to the Merger class I am working to move this second functionality to the merger class and also to remove the cacheKey filter on it. The cacheKey pertains to a particular search set. However if 2 people are deduping different search sets and one person merges a contact pair they should not remain available in the other person's result set. (Likewise, and to follow, if 2 contacts have a conflict we should update all search sets with that information, not just the one that was actively being deduped) --- CRM/Core/BAO/PrevNextCache.php | 26 -------------------------- CRM/Dedupe/Merger.php | 21 ++++++++++++++++++++- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/CRM/Core/BAO/PrevNextCache.php b/CRM/Core/BAO/PrevNextCache.php index 4eae51e91a13..f84454ae72ed 100644 --- a/CRM/Core/BAO/PrevNextCache.php +++ b/CRM/Core/BAO/PrevNextCache.php @@ -121,32 +121,6 @@ public static function deleteItem($id = NULL, $cacheKey = NULL, $entityTable = ' CRM_Core_DAO::executeQuery($sql, $params); } - /** - * Delete pair from the previous next cache table to remove it from further merge consideration. - * - * The pair may have been flipped, so make sure we delete using both orders - * - * @param int $id1 - * @param int $id2 - * @param string $cacheKey - */ - public static function deletePair($id1, $id2, $cacheKey = NULL) { - $sql = "DELETE FROM civicrm_prevnext_cache WHERE entity_table = 'civicrm_contact'"; - - $pair = "(entity_id1 = %2 AND entity_id2 = %3) OR (entity_id1 = %3 AND entity_id2 = %2)"; - $sql .= " AND ( {$pair} )"; - $params[2] = [$id1, 'Integer']; - $params[3] = [$id2, 'Integer']; - - if (isset($cacheKey)) { - $sql .= " AND cachekey LIKE %4"; - // used % to address any row with conflict-cacheKey e.g "merge Individual_8_0_conflicts" - $params[4] = ["{$cacheKey}%", 'String']; - } - - CRM_Core_DAO::executeQuery($sql, $params); - } - /** * Mark contacts as being in conflict. * diff --git a/CRM/Dedupe/Merger.php b/CRM/Dedupe/Merger.php index 1a918695103c..bcf68cca719e 100644 --- a/CRM/Dedupe/Merger.php +++ b/CRM/Dedupe/Merger.php @@ -1911,12 +1911,31 @@ protected static function dedupePair($dupes, $mode = 'safe', $checkPermissions = CRM_Core_BAO_PrevNextCache::markConflict($mainId, $otherId, $cacheKeyString, $conflicts, $mode); } else { - CRM_Core_BAO_PrevNextCache::deletePair($mainId, $otherId, $cacheKeyString); + self::deletePairFromPrevNextCache((int) $mainId, (int) $otherId); } self::releaseLocks($locks); return $resultStats; } + /** + * Delete merged pair from the previous next cache table as the are no longer a merge candidate. + * + * It's possible there may be more than one set of merge results cached, with different cache keys. + * Once we have merged a pair these should all go (even from a different merge search) as they + * can only be merged once. + * + * @param int $contactID1 + * @param int $contactID2 + */ + protected static function deletePairFromPrevNextCache(int $contactID1, int $contactID2) { + CRM_Core_DAO::executeQuery(" + DELETE FROM civicrm_prevnext_cache + WHERE entity_table = 'civicrm_contact' + AND (entity_id1 = %1 AND entity_id2 = %2) OR (entity_id1 = %2 AND entity_id2 = %1)", + [1 => [$contactID1, 'Integer'], 2 => [$contactID2, 'Integer']] + ); + } + /** * Replace the pseudo QFKey with zero if it is present. * From a3ca6fe36ec7e8c077497a6da2b71bbf88d24fe0 Mon Sep 17 00:00:00 2001 From: Matthew Wire Date: Fri, 14 Aug 2020 16:33:28 +0100 Subject: [PATCH 004/834] Move add/submit membership buttons to PHP level on membership tab --- CRM/Member/Page/Tab.php | 22 ++++++++++++++++++---- templates/CRM/Member/Page/Tab.tpl | 25 +++++++------------------ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/CRM/Member/Page/Tab.php b/CRM/Member/Page/Tab.php index ec2300d0ba5d..ef4d4551d510 100644 --- a/CRM/Member/Page/Tab.php +++ b/CRM/Member/Page/Tab.php @@ -48,6 +48,23 @@ public function browse() { $permissions = [CRM_Core_Permission::VIEW]; if (CRM_Core_Permission::check('edit memberships')) { $permissions[] = CRM_Core_Permission::EDIT; + $linkButtons['add_membership'] = [ + 'title' => ts('Add Membership'), + 'url' => 'civicrm/contact/view/membership', + 'qs' => "reset=1&action=add&cid={$this->_contactId}&context=membership", + 'icon' => 'fa-plus-circle', + 'accessKey' => 'N', + ]; + if ($this->_accessContribution && CRM_Core_Config::isEnabledBackOfficeCreditCardPayments()) { + $linkButtons['creditcard_membership'] = [ + 'title' => ts('Submit Credit Card Membership'), + 'url' => 'civicrm/contact/view/membership', + 'qs' => "reset=1&action=add&cid={$this->_contactId}&context=membership&mode=live", + 'icon' => 'fa-credit-card', + 'accessKey' => 'C', + ]; + } + $this->assign('linkButtons', $linkButtons ?? []); } if (CRM_Core_Permission::check('delete in CiviMember')) { $permissions[] = CRM_Core_Permission::DELETE; @@ -317,10 +334,7 @@ public function run() { $this->preProcess(); // check if we can process credit card membership - $newCredit = CRM_Core_Config::isEnabledBackOfficeCreditCardPayments(); - $this->assign('newCredit', $newCredit); - - if ($newCredit) { + if (CRM_Core_Config::isEnabledBackOfficeCreditCardPayments()) { $this->_isPaymentProcessor = TRUE; } else { diff --git a/templates/CRM/Member/Page/Tab.tpl b/templates/CRM/Member/Page/Tab.tpl index 249634931c3a..7e807c94822b 100644 --- a/templates/CRM/Member/Page/Tab.tpl +++ b/templates/CRM/Member/Page/Tab.tpl @@ -15,31 +15,20 @@ {elseif $action eq 32768} {* renew *} {include file="CRM/Member/Form/MembershipRenewal.tpl"} {elseif $action eq 16} {* Browse memberships for a contact *} - {if $permission EQ 'edit'} - {capture assign=newURL}{crmURL p="civicrm/contact/view/membership" q="reset=1&action=add&cid=`$contactId`&context=membership"}{/capture}{/if} - {if $action ne 1 and $action ne 2 and $permission EQ 'edit'}
- {if $permission EQ 'edit'} - {capture assign="link"}class="action-item" href="{$newURL}"{/capture} - {ts 1=$link}Click Add Membership to record a new membership.{/ts} - {if $newCredit} - {capture assign=newCreditURL}{crmURL p="civicrm/contact/view/membership" q="reset=1&action=add&cid=`$contactId`&context=membership&mode=live"}{/capture} - {capture assign="link"}class="action-item" href="{$newCreditURL}"{/capture} - {ts 1=$link}Click Submit Credit Card Membership to process a Membership on behalf of the member using their credit card.{/ts} - {/if} + {if $linkButtons.add_membership} + {ts}Click Add Membership to record a new membership.{/ts} {else} - {ts 1=$displayName}Current and inactive memberships for %1 are listed below.{/ts} + {ts 1=$displayName}Current and inactive memberships for %1 are listed below.{/ts} + {/if} + {if $linkButtons.creditcard_membership} + {ts}Click Submit Credit Card Membership to process a Membership on behalf of the member using their credit card.{/ts} {/if}
{/if} {if NOT ($activeMembers or $inActiveMembers) and $action ne 2 and $action ne 1 and $action ne 8 and $action ne 4 and $action ne 32768} From 04b1abad5dcf19ec18dc9bca9e04af2e2c618d59 Mon Sep 17 00:00:00 2001 From: demeritcowboy Date: Sun, 9 Aug 2020 13:19:32 -0400 Subject: [PATCH 005/834] allow mysql SSL connection for civicrm-setup --- Civi/Install/Requirements.php | 19 +++++- .../CheckDbWellFormed.civi-setup.php | 19 +++--- .../CoreRequirementsAdapter.civi-setup.php | 1 + .../InstallSettingsFile.civi-setup.php | 7 +++ setup/src/Setup/DbUtil.php | 51 +++++++++++++++- .../CRM/common/civicrm.settings.php.template | 2 +- tests/phpunit/Civi/Setup/DbUtilTest.php | 59 +++++++++++++++++++ 7 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 tests/phpunit/Civi/Setup/DbUtilTest.php diff --git a/Civi/Install/Requirements.php b/Civi/Install/Requirements.php index da7b2cc76597..7d30855076b7 100644 --- a/Civi/Install/Requirements.php +++ b/Civi/Install/Requirements.php @@ -132,7 +132,24 @@ protected function connect($db_config) { elseif (!empty($db_config['server'])) { $host = $db_config['server']; } - $conn = @mysqli_connect($host, $db_config['username'], $db_config['password'], $db_config['database'], !empty($db_config['port']) ? $db_config['port'] : NULL); + if (empty($db_config['ssl_params'])) { + $conn = @mysqli_connect($host, $db_config['username'], $db_config['password'], $db_config['database'], !empty($db_config['port']) ? $db_config['port'] : NULL); + } + else { + $conn = NULL; + $init = mysqli_init(); + mysqli_ssl_set( + $init, + $db_config['ssl_params']['key'] ?? NULL, + $db_config['ssl_params']['cert'] ?? NULL, + $db_config['ssl_params']['ca'] ?? NULL, + $db_config['ssl_params']['capath'] ?? NULL, + $db_config['ssl_params']['cipher'] ?? NULL + ); + if (@mysqli_real_connect($init, $host, $db_config['username'], $db_config['password'], $db_config['database'], (!empty($db_config['port']) ? $db_config['port'] : NULL), NULL, MYSQLI_CLIENT_SSL)) { + $conn = $init; + } + } return $conn; } diff --git a/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php b/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php index d48925c3de92..4f09d7c8328a 100644 --- a/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php +++ b/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php @@ -23,19 +23,24 @@ $expectedKeys = array('server', 'username', 'password', 'database'); sort($expectedKeys); if ($keys !== $expectedKeys) { - $e->addError('database', $dbField, sprintf("The database credentials for \"%s\" should be specified as (%s) not (%s)", - $dbField, - implode(',', $expectedKeys), - implode(',', $keys) - )); - $errors++; + // if it failed it might be because of the optional ssl parameters + $expectedKeys[] = 'ssl_params'; + sort($expectedKeys); + if ($keys !== $expectedKeys) { + $e->addError('database', $dbField, sprintf("The database credentials for \"%s\" should be specified as (%s) not (%s)", + $dbField, + implode(',', $expectedKeys), + implode(',', $keys) + )); + $errors++; + } } foreach ($db as $k => $v) { if ($k === 'password' && empty($v)) { $e->addWarning('database', "$dbField.$k", "The property \"$dbField.$k\" is blank. This may be correct in some controlled environments; it could also be a mistake or a symptom of an insecure configuration."); } - elseif (!is_scalar($v)) { + elseif ($k !== 'ssl_params' && !is_scalar($v)) { $e->addError('database', "$dbField.$k", "The property \"$dbField.$k\" is not well-formed."); $errors++; } diff --git a/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php b/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php index f0d15507c3fe..4e4e051f31a7 100644 --- a/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php +++ b/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php @@ -29,6 +29,7 @@ 'username' => $model->db['username'], 'password' => $model->db['password'], 'database' => $model->db['database'], + 'ssl_params' => $model->db['ssl_params'] ?? NULL, )); _corereqadapter_addMessages($e, 'database', $dbMsgs); }); diff --git a/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php b/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php index 3581eb3bed8e..29aedb2b073b 100644 --- a/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php +++ b/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php @@ -59,6 +59,13 @@ $params['dbPass'] = addslashes($m->db['password']); $params['dbHost'] = addslashes($m->db['server']); $params['dbName'] = addslashes($m->db['database']); + // The '&' prefix is awkward, but we don't know what's already in the file. + // At the time of writing, it has ?new_link=true. If that is removed, + // then need to update this. + // The PHP_QUERY_RFC3986 is important because PEAR::DB will interpret plus + // signs as a reference to its old DSN format and mangle the DSN, so we + // need to use %20 for spaces. + $params['dbSSL'] = empty($m->db['ssl_params']) ? '' : addslashes('&' . http_build_query($m->db['ssl_params'], '', '&', PHP_QUERY_RFC3986)); $params['cms'] = addslashes($m->cms); $params['CMSdbUser'] = addslashes($m->cmsDb['username']); $params['CMSdbPass'] = addslashes($m->cmsDb['password']); diff --git a/setup/src/Setup/DbUtil.php b/setup/src/Setup/DbUtil.php index 054fe8d731b0..87849057c6d8 100644 --- a/setup/src/Setup/DbUtil.php +++ b/setup/src/Setup/DbUtil.php @@ -12,14 +12,16 @@ class DbUtil { public static function parseDsn($dsn) { $parsed = parse_url($dsn); return array( - 'server' => self::encodeHostPort($parsed['host'], $parsed['port']), + 'server' => self::encodeHostPort($parsed['host'], $parsed['port'] ?? NULL), 'username' => $parsed['user'] ?: NULL, 'password' => $parsed['pass'] ?: NULL, 'database' => $parsed['path'] ? ltrim($parsed['path'], '/') : NULL, + 'ssl_params' => self::parseSSL($parsed['query'] ?? NULL), ); } /** + * @todo Is this used anywhere? It doesn't support SSL as-is. * Convert an datasource from array notation to URL notation. * * @param array $db @@ -40,7 +42,25 @@ public static function encodeDsn($db) { */ public static function softConnect($db) { list($host, $port) = self::decodeHostPort($db['server']); - $conn = @mysqli_connect($host, $db['username'], $db['password'], $db['database'], $port); + if (empty($db['ssl_params'])) { + $conn = @mysqli_connect($host, $db['username'], $db['password'], $db['database'], $port); + } + else { + $conn = NULL; + $init = mysqli_init(); + mysqli_ssl_set( + $init, + $db['ssl_params']['key'] ?? NULL, + $db['ssl_params']['cert'] ?? NULL, + $db['ssl_params']['ca'] ?? NULL, + $db['ssl_params']['capath'] ?? NULL, + $db['ssl_params']['cipher'] ?? NULL + ); + // @todo socket parameter, but if you're using sockets do you need SSL? + if (@mysqli_real_connect($init, $host, $db['username'], $db['password'], $db['database'], $port, NULL, MYSQLI_CLIENT_SSL)) { + $conn = $init; + } + } return $conn; } @@ -94,6 +114,33 @@ public static function encodeHostPort($host, $port) { return $host . ($port ? (':' . $port) : ''); } + /** + * For SSL you can have client certificates, which has some required and + * optional parameters, or you can have anonymous SSL, which just requires + * some indication that you want that. + * + * @param string $query_string + * @return array + */ + public static function parseSSL($query_string) { + if (empty($query_string)) { + return []; + } + parse_str($query_string, $parsed_query); + $sensible_parameters = [ + // ssl=1 alone means no client certificate - it's not a real mysqli option + 'ssl' => NULL, + 'key' => NULL, + 'cert' => NULL, + 'ca' => NULL, + 'capath' => NULL, + 'cipher' => NULL, + ]; + // Only want to include a param if it's in our list of sensibles, e.g. + // we don't want new_link=true. + return array_intersect_key($parsed_query, $sensible_parameters); + } + /** * @param array $db * @param string $SQLcontent diff --git a/templates/CRM/common/civicrm.settings.php.template b/templates/CRM/common/civicrm.settings.php.template index e36756840f30..3d0575569fb8 100644 --- a/templates/CRM/common/civicrm.settings.php.template +++ b/templates/CRM/common/civicrm.settings.php.template @@ -106,7 +106,7 @@ if (!defined('CIVICRM_DSN')) { define('CIVICRM_DSN', $GLOBALS['_CV']['TEST_DB_DSN']); } else { - define('CIVICRM_DSN', 'mysql://%%dbUser%%:%%dbPass%%@%%dbHost%%/%%dbName%%?new_link=true'); + define('CIVICRM_DSN', 'mysql://%%dbUser%%:%%dbPass%%@%%dbHost%%/%%dbName%%?new_link=true%%dbSSL%%'); } } diff --git a/tests/phpunit/Civi/Setup/DbUtilTest.php b/tests/phpunit/Civi/Setup/DbUtilTest.php new file mode 100644 index 000000000000..7c42ff7551d1 --- /dev/null +++ b/tests/phpunit/Civi/Setup/DbUtilTest.php @@ -0,0 +1,59 @@ +assertSame($expected, \Civi\Setup\DbUtil::parseSSL($input)); + } + + /** + * Data provider for testParseSSL + * @return array + */ + public function queryStringProvider():array { + return [ + ['', []], + ['new_link=true', []], + ['ssl=1', ['ssl' => '1']], + ['new_link=true&ssl=1', ['ssl' => '1']], + ['ca=%2Ftmp%2Fcacert.crt', ['ca' => '/tmp/cacert.crt']], + [ + 'ca=%2Ftmp%2Fcacert.crt&cert=%2Ftmp%2Fcert.crt&key=%2Ftmp%2Fmy.key', + [ + 'ca' => '/tmp/cacert.crt', + 'cert' => '/tmp/cert.crt', + 'key' => '/tmp/my.key', + ], + ], + [ + 'ca=%2Fpath%20with%20spaces%2Fcacert.crt', + [ + 'ca' => '/path with spaces/cacert.crt', + ], + ], + ['cipher=aes', ['cipher' => 'aes']], + ['capath=%2Ftmp', ['capath' => '/tmp']], + [ + 'cipher=aes&capath=%2Ftmp&food=banana', + [ + 'cipher' => 'aes', + 'capath' => '/tmp', + ], + ], + ['food=banana&cipher=aes', ['cipher' => 'aes']], + ]; + } + +} From 209fd988c4186e74464e4b2dbaec96fcda38bb52 Mon Sep 17 00:00:00 2001 From: Matthew Wire Date: Fri, 21 Aug 2020 16:37:33 +0100 Subject: [PATCH 006/834] Fix setAmount should not return localized amount --- Civi/Payment/PropertyBag.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Civi/Payment/PropertyBag.php b/Civi/Payment/PropertyBag.php index 49a4ea4e8c99..f8817b8c4ecb 100644 --- a/Civi/Payment/PropertyBag.php +++ b/Civi/Payment/PropertyBag.php @@ -423,8 +423,7 @@ public function setAmount($value, $label = 'default') { if (!is_numeric($value)) { throw new \InvalidArgumentException("setAmount requires a numeric amount value"); } - - return $this->set('amount', $label, \CRM_Utils_Money::format($value, NULL, NULL, TRUE)); + return $this->set('amount', $label, filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)); } /** From e64435f1646d80e760e688f961c139f69ae2da14 Mon Sep 17 00:00:00 2001 From: eileen Date: Sat, 15 Aug 2020 11:04:31 +1200 Subject: [PATCH 007/834] Improve consistency of metadata type declarations --- CRM/Contact/BAO/Contact.php | 4 ++ CRM/Export/BAO/ExportProcessor.php | 65 +++++++++------------ tests/phpunit/CRM/Export/BAO/ExportTest.php | 36 ++++++------ 3 files changed, 50 insertions(+), 55 deletions(-) diff --git a/CRM/Contact/BAO/Contact.php b/CRM/Contact/BAO/Contact.php index 29c82bd90095..96e847a12074 100644 --- a/CRM/Contact/BAO/Contact.php +++ b/CRM/Contact/BAO/Contact.php @@ -1543,14 +1543,17 @@ public static function &exportableFields($contactType = 'Individual', $status = [ 'name' => 'organization_name', 'title' => ts('Current Employer'), + 'type' => CRM_Utils_Type::T_STRING, ], ]); + // This probably would be added anyway by appendPseudoConstantsToFields. $locationType = [ 'location_type' => [ 'name' => 'location_type', 'where' => 'civicrm_location_type.name', 'title' => ts('Location Type'), + 'type' => CRM_Utils_Type::T_STRING, ], ]; @@ -1559,6 +1562,7 @@ public static function &exportableFields($contactType = 'Individual', $status = 'name' => 'im_provider', 'where' => 'civicrm_im.provider_id', 'title' => ts('IM Provider'), + 'type' => CRM_Utils_Type::T_STRING, ], ]; diff --git a/CRM/Export/BAO/ExportProcessor.php b/CRM/Export/BAO/ExportProcessor.php index c1231fecb714..7c2fbb859217 100644 --- a/CRM/Export/BAO/ExportProcessor.php +++ b/CRM/Export/BAO/ExportProcessor.php @@ -639,6 +639,9 @@ public function setQueryFields($queryFields) { $queryFields['world_region']['context'] = 'country'; $queryFields['state_province']['context'] = 'province'; $queryFields['contact_id'] = ['title' => ts('Contact ID'), 'type' => CRM_Utils_Type::T_INT]; + $queryFields['tags']['type'] = CRM_Utils_Type::T_LONGTEXT; + $queryFields['groups']['type'] = CRM_Utils_Type::T_LONGTEXT; + $queryFields['notes']['type'] = CRM_Utils_Type::T_LONGTEXT; // Set the label to gender for gender_id as we it's ... magic (not in a good way). // In other places the query object offers e.g contribution_status & contribution_status_id $queryFields['gender_id']['title'] = ts('Gender'); @@ -803,7 +806,7 @@ public function runQuery($params, $order) { // CRM-13982 - check if is deleted foreach ($params as $value) { - if ($value[0] == 'contact_is_deleted') { + if ($value[0] === 'contact_is_deleted') { unset($whereClauses['trash_clause']); } } @@ -825,10 +828,10 @@ public function runQuery($params, $order) { } if (empty($where)) { - $where = "WHERE " . implode(' AND ', $whereClauses); + $where = 'WHERE ' . implode(' AND ', $whereClauses); } else { - $where .= " AND " . implode(' AND ', $whereClauses); + $where .= ' AND ' . implode(' AND ', $whereClauses); } $groupBy = $this->getGroupBy($query); @@ -1431,21 +1434,20 @@ public function getValidLocationFields() { public function getSqlColumnDefinition($fieldName, $columnName) { // early exit for master_id, CRM-12100 - // in the DB it is an ID, but in the export, we retrive the display_name of the master record - // also for current_employer, CRM-16939 - if ($columnName == 'master_id' || $columnName == 'current_employer') { + // in the DB it is an ID, but in the export, we retrieve the display_name of the master record + if ($columnName === 'master_id') { return "`$fieldName` varchar(128)"; } $queryFields = $this->getQueryFields(); - // @todo remove the enotice avoidance here, ensure all columns are declared. + // @todo remove the e-notice avoidance here, ensure all columns are declared. // tests will fail on the enotices until they all are & then all the 'else' // below can go. $fieldSpec = $queryFields[$columnName] ?? []; - + $type = $fieldSpec['type'] ?? ($fieldSpec['data_type'] ?? ''); // set the sql columns - if (isset($fieldSpec['type'])) { - switch ($fieldSpec['type']) { + if ($type) { + switch ($type) { case CRM_Utils_Type::T_INT: case CRM_Utils_Type::T_BOOLEAN: if (in_array(CRM_Utils_Array::value('data_type', $fieldSpec), ['Country', 'StateProvince', 'ContactReference'])) { @@ -1488,39 +1490,28 @@ public function getSqlColumnDefinition($fieldName, $columnName) { return "`$fieldName` text"; } else { - $changeFields = [ - 'groups', - 'tags', - 'notes', - ]; + // set the sql columns for custom data + if (isset($queryFields[$columnName]['data_type'])) { - if (in_array($fieldName, $changeFields)) { - return "`$fieldName` text"; - } - else { - // set the sql columns for custom data - if (isset($queryFields[$columnName]['data_type'])) { + switch ($queryFields[$columnName]['data_type']) { + case 'String': + // May be option labels, which could be up to 512 characters + $length = max(512, CRM_Utils_Array::value('text_length', $queryFields[$columnName])); + return "`$fieldName` varchar($length)"; - switch ($queryFields[$columnName]['data_type']) { - case 'String': - // May be option labels, which could be up to 512 characters - $length = max(512, CRM_Utils_Array::value('text_length', $queryFields[$columnName])); - return "`$fieldName` varchar($length)"; + case 'Link': + return "`$fieldName` varchar(255)"; - case 'Link': - return "`$fieldName` varchar(255)"; + case 'Memo': + return "`$fieldName` text"; - case 'Memo': - return "`$fieldName` text"; - - default: - return "`$fieldName` varchar(255)"; - } - } - else { - return "`$fieldName` text"; + default: + return "`$fieldName` varchar(255)"; } } + else { + return "`$fieldName` text"; + } } } } diff --git a/tests/phpunit/CRM/Export/BAO/ExportTest.php b/tests/phpunit/CRM/Export/BAO/ExportTest.php index b2359ed4f080..deb43c526f16 100644 --- a/tests/phpunit/CRM/Export/BAO/ExportTest.php +++ b/tests/phpunit/CRM/Export/BAO/ExportTest.php @@ -1807,7 +1807,7 @@ public function testGetSQLColumnsAndHeaders($exportMode, $expected, $expectedHea // We need some data so that we can get to the end of the export // function. Hopefully one day that won't be required to get metadata info out. // eventually aspire to call $provider->getSQLColumns straight after it - // is intiated. + // is initiated. $this->setupBaseExportData($exportMode); $this->doExportTest(['selectAll' => TRUE, 'exportMode' => $exportMode, 'ids' => [1]]); $this->assertEquals($expected, $this->processor->getSQLColumns()); @@ -1913,7 +1913,7 @@ public function getAllSpecifiableParticipantReturnColumns() { 'participant_fee_level' => '`participant_fee_level` longtext', 'participant_is_pay_later' => '`participant_is_pay_later` varchar(16)', 'participant_id' => '`participant_id` varchar(16)', - 'participant_note' => '`participant_note` text', + 'participant_note' => '`participant_note` longtext', 'participant_role_id' => '`participant_role_id` varchar(128)', 'participant_role' => '`participant_role` varchar(255)', 'participant_source' => '`participant_source` varchar(128)', @@ -2508,8 +2508,8 @@ protected function getBasicSqlColumnDefinition($isContactExport) { 'addressee' => '`addressee` varchar(255)', 'email_greeting' => '`email_greeting` varchar(255)', 'postal_greeting' => '`postal_greeting` varchar(255)', - 'current_employer' => '`current_employer` varchar(128)', - 'location_type' => '`location_type` text', + 'current_employer' => '`current_employer` varchar(255)', + 'location_type' => '`location_type` varchar(255)', 'address_id' => '`address_id` varchar(16)', 'street_address' => '`street_address` varchar(96)', 'street_number' => '`street_number` varchar(16)', @@ -2538,14 +2538,14 @@ protected function getBasicSqlColumnDefinition($isContactExport) { 'is_bulkmail' => '`is_bulkmail` varchar(16)', 'signature_text' => '`signature_text` longtext', 'signature_html' => '`signature_html` longtext', - 'im_provider' => '`im_provider` text', + 'im_provider' => '`im_provider` varchar(255)', 'im' => '`im` varchar(64)', 'openid' => '`openid` varchar(255)', 'world_region' => '`world_region` varchar(128)', 'url' => '`url` varchar(128)', - 'groups' => '`groups` text', - 'tags' => '`tags` text', - 'notes' => '`notes` text', + 'groups' => '`groups` longtext', + 'tags' => '`tags` longtext', + 'notes' => '`notes` longtext', 'phone_type' => '`phone_type` varchar(255)', ]; if (!$isContactExport) { @@ -2628,7 +2628,7 @@ protected function getParticipantSqlColumns() { 'participant_status_id' => '`participant_status_id` varchar(16)', 'participant_role' => '`participant_role` varchar(255)', 'participant_role_id' => '`participant_role_id` varchar(128)', - 'participant_note' => '`participant_note` text', + 'participant_note' => '`participant_note` longtext', 'participant_register_date' => '`participant_register_date` varchar(32)', 'participant_source' => '`participant_source` varchar(128)', 'participant_fee_level' => '`participant_fee_level` longtext', @@ -2696,8 +2696,8 @@ public function getContributionSqlColumns() { 'addressee' => '`addressee` varchar(255)', 'email_greeting' => '`email_greeting` varchar(255)', 'postal_greeting' => '`postal_greeting` varchar(255)', - 'current_employer' => '`current_employer` varchar(128)', - 'location_type' => '`location_type` text', + 'current_employer' => '`current_employer` varchar(255)', + 'location_type' => '`location_type` varchar(255)', 'street_address' => '`street_address` varchar(96)', 'street_number' => '`street_number` varchar(16)', 'street_number_suffix' => '`street_number_suffix` varchar(8)', @@ -2723,7 +2723,7 @@ public function getContributionSqlColumns() { 'is_bulkmail' => '`is_bulkmail` varchar(16)', 'signature_text' => '`signature_text` longtext', 'signature_html' => '`signature_html` longtext', - 'im_provider' => '`im_provider` text', + 'im_provider' => '`im_provider` varchar(255)', 'im' => '`im` varchar(64)', 'openid' => '`openid` varchar(255)', 'world_region' => '`world_region` varchar(128)', @@ -2753,15 +2753,15 @@ public function getContributionSqlColumns() { 'contribution_status' => '`contribution_status` varchar(255)', 'contribution_recur_id' => '`contribution_recur_id` varchar(16)', 'amount_level' => '`amount_level` longtext', - 'contribution_note' => '`contribution_note` text', + 'contribution_note' => '`contribution_note` longtext', 'contribution_batch' => '`contribution_batch` text', 'contribution_campaign_title' => '`contribution_campaign_title` varchar(255)', 'contribution_campaign_id' => '`contribution_campaign_id` varchar(16)', 'contribution_soft_credit_name' => '`contribution_soft_credit_name` varchar(255)', - 'contribution_soft_credit_amount' => '`contribution_soft_credit_amount` varchar(255)', + 'contribution_soft_credit_amount' => '`contribution_soft_credit_amount` varchar(32)', 'contribution_soft_credit_type' => '`contribution_soft_credit_type` varchar(255)', - 'contribution_soft_credit_contact_id' => '`contribution_soft_credit_contact_id` varchar(255)', - 'contribution_soft_credit_contribution_id' => '`contribution_soft_credit_contribution_id` varchar(255)', + 'contribution_soft_credit_contact_id' => '`contribution_soft_credit_contact_id` varchar(16)', + 'contribution_soft_credit_contribution_id' => '`contribution_soft_credit_contribution_id` varchar(16)', ]; } @@ -2781,9 +2781,9 @@ public function getPledgeSqlColumns() { 'pledge_next_pay_amount' => '`pledge_next_pay_amount` text', 'pledge_status' => '`pledge_status` varchar(255)', 'pledge_is_test' => '`pledge_is_test` varchar(16)', - 'pledge_contribution_page_id' => '`pledge_contribution_page_id` varchar(255)', + 'pledge_contribution_page_id' => '`pledge_contribution_page_id` varchar(16)', 'pledge_financial_type' => '`pledge_financial_type` text', - 'pledge_frequency_interval' => '`pledge_frequency_interval` varchar(255)', + 'pledge_frequency_interval' => '`pledge_frequency_interval` varchar(16)', 'pledge_frequency_unit' => '`pledge_frequency_unit` varchar(255)', 'pledge_currency' => '`pledge_currency` text', 'pledge_campaign_id' => '`pledge_campaign_id` varchar(16)', From 4c54e0bd41f31c5bf40855ff23e55e77d3d35115 Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 29 Jul 2020 18:53:51 +1200 Subject: [PATCH 008/834] [REF] Extract function to get locations to merge, rename 'operation' to is_replace' This is a preliminary refactor. I have a bug to fix in this code but cannot yet make sense of it. The mergeHandler class is simply an object to refactor the functions onto. Much of this code is hard to work with as the use of static functions necessitates a lot of compilation and compiling so the process of cleaning it up involves extracting functionns to this new class (which may one day replace the Merger class). As it is a refactoring process the functions reflect the old code more than the ideal code. However, it provides the change to give some documentation regarding the locBlock array and also to rename the confusing field 'operation' to 'is_replace' --- CRM/Dedupe/MergeHandler.php | 61 ++++++++++++++++++++++++++++ CRM/Dedupe/Merger.php | 49 ++++++---------------- tests/phpunit/api/v3/ContactTest.php | 14 ++----- 3 files changed, 78 insertions(+), 46 deletions(-) diff --git a/CRM/Dedupe/MergeHandler.php b/CRM/Dedupe/MergeHandler.php index 891bc96c90e1..6b86b2f9b91d 100644 --- a/CRM/Dedupe/MergeHandler.php +++ b/CRM/Dedupe/MergeHandler.php @@ -33,6 +33,30 @@ class CRM_Dedupe_MergeHandler { */ protected $toRemoveID; + /** + * Migration info array. + * + * This is a nasty bunch of data used in mysterious ways. We want to work to make it more + * sensible but for now we store it. + * + * @var array + */ + protected $migrationInfo = []; + + /** + * @return array + */ + public function getMigrationInfo(): array { + return $this->migrationInfo; + } + + /** + * @param array $migrationInfo + */ + public function setMigrationInfo(array $migrationInfo) { + $this->migrationInfo = $migrationInfo; + } + /** * @return mixed */ @@ -129,4 +153,41 @@ public function getTablesDynamicallyRelatedToContactTable() { return \Civi::$statics[__CLASS__]['dynamic']; } + /** + * Get the location blocks designated to be moved during the merge. + * + * Note this is a refactoring step and future refactors should develop a more coherent array + * + * @return array + * The format is ['address' => [0 => ['is_replace' => TRUE]], 'email' => [0...],[1....] + * where the entity is address, the internal indexing for the addresses on both contact is 1 + * and the intent to replace the existing address is TRUE. + */ + public function getLocationBlocksToMerge(): array { + $locBlocks = []; + foreach ($this->getMigrationInfo() as $key => $value) { + $isLocationField = (substr($key, 0, 14) === 'move_location_' and $value != NULL); + if (!$isLocationField) { + continue; + } + $locField = explode('_', $key); + $fieldName = $locField[2]; + $fieldCount = $locField[3]; + + // Set up the operation type (add/overwrite) + // Ignore operation for websites + // @todo Tidy this up + $operation = 0; + if ($fieldName !== 'website') { + $operation = $this->getMigrationInfo()['location_blocks'][$fieldName][$fieldCount]['operation'] ?? NULL; + } + // default operation is overwrite. + if (!$operation) { + $operation = 2; + } + $locBlocks[$fieldName][$fieldCount]['is_replace'] = $operation === 2; + } + return $locBlocks; + } + } diff --git a/CRM/Dedupe/Merger.php b/CRM/Dedupe/Merger.php index 2300680614fa..d6b1c7bb7ce0 100644 --- a/CRM/Dedupe/Merger.php +++ b/CRM/Dedupe/Merger.php @@ -1388,7 +1388,9 @@ public static function moveAllBelongings($mainId, $otherId, $migrationInfo, $che $removeTables = array_merge($moveTables, $relTables[substr($key, 5)]['tables']); } } - self::mergeLocations($mainId, $otherId, $migrationInfo); + $mergeHandler = new CRM_Dedupe_MergeHandler((int) $mainId, (int) $otherId); + $mergeHandler->setMigrationInfo($migrationInfo); + self::mergeLocations($mergeHandler); // **** Do contact related migrations // @todo - move all custom field processing to the move class & eventually have an @@ -1396,7 +1398,7 @@ public static function moveAllBelongings($mainId, $otherId, $migrationInfo, $che $customFieldBAO = new CRM_Core_BAO_CustomField(); $customFieldBAO->move($otherId, $mainId, $submittedCustomFields); // add the related tables and unset the ones that don't sport any of the duplicate contact's info - $mergeHandler = new CRM_Dedupe_MergeHandler((int) $mainId, (int) $otherId); + CRM_Dedupe_Merger::moveContactBelongings($mergeHandler, $moveTables, $tableOperations); unset($moveTables, $tableOperations); @@ -1775,43 +1777,19 @@ public static function getMergeContactDetails($contact_id) { * The use of the new hook is tested, including the fact it is called before contributions are merged, as this * is likely to be significant data in merge hooks. * - * @param int $mainId - * @param int $otherId - * - * @param array $migrationInfo - * Migration info for the merge. This is passed to the hook as informational only. + * @param \CRM_Dedupe_MergeHandler $mergeHandler */ - public static function mergeLocations($mainId, $otherId, $migrationInfo) { - foreach ($migrationInfo as $key => $value) { - $isLocationField = (substr($key, 0, 14) === 'move_location_' and $value != NULL); - if (!$isLocationField) { - continue; - } - $locField = explode('_', $key); - $fieldName = $locField[2]; - $fieldCount = $locField[3]; - - // Set up the operation type (add/overwrite) - // Ignore operation for websites - // @todo Tidy this up - $operation = 0; - if ($fieldName !== 'website') { - $operation = $migrationInfo['location_blocks'][$fieldName][$fieldCount]['operation'] ?? NULL; - } - // default operation is overwrite. - if (!$operation) { - $operation = 2; - } - $locBlocks[$fieldName][$fieldCount]['operation'] = $operation; - } + public static function mergeLocations($mergeHandler) { + $locBlocks = $mergeHandler->getLocationBlocksToMerge(); $blocksDAO = []; + $migrationInfo = $mergeHandler->getMigrationInfo(); // @todo Handle OpenID (not currently in API). if (!empty($locBlocks)) { $locationBlocks = self::getLocationBlockInfo(); - $primaryBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mainId, ['is_primary' => 1]); - $billingBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mainId, ['is_billing' => 1]); + $primaryBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mergeHandler->getToKeepID(), ['is_primary' => 1]); + $billingBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mergeHandler->getToKeepID(), ['is_billing' => 1]); foreach ($locBlocks as $name => $block) { $blocksDAO[$name] = ['delete' => [], 'update' => []]; @@ -1832,7 +1810,7 @@ public static function mergeLocations($mainId, $otherId, $migrationInfo) { // For the block which belongs to other-contact, link the location block to main-contact $otherBlockDAO = new $daoName(); - $otherBlockDAO->contact_id = $mainId; + $otherBlockDAO->contact_id = $mergeHandler->getToKeepID(); // Get the ID of this block on the 'other' contact, otherwise skip $otherBlockDAO->id = $otherBlockId; @@ -1871,9 +1849,8 @@ public static function mergeLocations($mainId, $otherId, $migrationInfo) { $otherBlockDAO->is_billing = 0; } - $operation = CRM_Utils_Array::value('operation', $values, 2); // overwrite - need to delete block which belongs to main-contact. - if (!empty($mainBlockId) && ($operation == 2)) { + if (!empty($mainBlockId) && $values['is_replace']) { $deleteDAO = new $daoName(); $deleteDAO->id = $mainBlockId; $deleteDAO->find(TRUE); @@ -1893,7 +1870,7 @@ public static function mergeLocations($mainId, $otherId, $migrationInfo) { } } - CRM_Utils_Hook::alterLocationMergeData($blocksDAO, $mainId, $otherId, $migrationInfo); + CRM_Utils_Hook::alterLocationMergeData($blocksDAO, $mergeHandler->getToKeepID(), $mergeHandler->getToRemoveID(), $migrationInfo); foreach ($blocksDAO as $blockDAOs) { if (!empty($blockDAOs['update'])) { foreach ($blockDAOs['update'] as $blockDAO) { diff --git a/tests/phpunit/api/v3/ContactTest.php b/tests/phpunit/api/v3/ContactTest.php index 62c1ff50e584..4d99ff2562ec 100644 --- a/tests/phpunit/api/v3/ContactTest.php +++ b/tests/phpunit/api/v3/ContactTest.php @@ -3968,23 +3968,17 @@ public function testMergeBizzareOldParams() { */ public function testMerge() { $this->createLoggedInUser(); - $otherContact = $this->callAPISuccess('contact', 'create', $this->_params); - $retainedContact = $this->callAPISuccess('contact', 'create', $this->_params); - $this->callAPISuccess('contact', 'merge', [ - 'to_keep_id' => $retainedContact['id'], - 'to_remove_id' => $otherContact['id'], - 'auto_flip' => FALSE, - ]); + $this->ids['contact'][0] = $this->callAPISuccess('Contact', 'create', $this->_params)['id']; + $this->ids['contact'][1] = $this->callAPISuccess('Contact', 'create', $this->_params)['id']; + $retainedContact = $this->doMerge(); - $contacts = $this->callAPISuccess('contact', 'get', $this->_params); - $this->assertEquals($retainedContact['id'], $contacts['id']); $activity = $this->callAPISuccess('Activity', 'getsingle', [ 'target_contact_id' => $retainedContact['id'], 'activity_type_id' => 'Contact Merged', ]); $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($activity['activity_date_time']))); $activity2 = $this->callAPISuccess('Activity', 'getsingle', [ - 'target_contact_id' => $otherContact['id'], + 'target_contact_id' => $this->ids['contact'][1], 'activity_type_id' => 'Contact Deleted by Merge', ]); $this->assertEquals($activity['id'], $activity2['parent_id']); From 6e948f0d2d1661b0d98cc3b73b400135383b1fff Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Sat, 22 Aug 2020 16:26:47 +0530 Subject: [PATCH 009/834] dev/core#1942 handle multiple membership of same membership type to update status --- CRM/Contribute/BAO/Contribution.php | 11 +-- tests/phpunit/api/v3/MembershipTest.php | 98 +++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 5 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 849daf7a4e5d..76281bcd768f 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -3185,10 +3185,10 @@ public function _gatherMessageValues($input, &$values, $ids = []) { foreach ($lineItems as &$eachItem) { if (isset($this->_relatedObjects['membership']) && is_array($this->_relatedObjects['membership']) - && array_key_exists($eachItem['membership_type_id'], $this->_relatedObjects['membership'])) { - $eachItem['join_date'] = CRM_Utils_Date::customFormat($this->_relatedObjects['membership'][$eachItem['membership_type_id']]->join_date); - $eachItem['start_date'] = CRM_Utils_Date::customFormat($this->_relatedObjects['membership'][$eachItem['membership_type_id']]->start_date); - $eachItem['end_date'] = CRM_Utils_Date::customFormat($this->_relatedObjects['membership'][$eachItem['membership_type_id']]->end_date); + && array_key_exists($eachItem['entity_id'] . '_' . $eachItem['membership_type_id'], $this->_relatedObjects['membership'])) { + $eachItem['join_date'] = CRM_Utils_Date::customFormat($this->_relatedObjects['membership'][$eachItem['entity_id'] . '_' . $eachItem['membership_type_id']]->join_date); + $eachItem['start_date'] = CRM_Utils_Date::customFormat($this->_relatedObjects['membership'][$eachItem['entity_id'] . '_' . $eachItem['membership_type_id']]->start_date); + $eachItem['end_date'] = CRM_Utils_Date::customFormat($this->_relatedObjects['membership'][$eachItem['entity_id'] . '_' . $eachItem['membership_type_id']]->end_date); } // This is actually used in conjunction with is_quick_config in the template & we should deprecate it. // However, that does create upgrade pain so would be better to be phased in. @@ -4706,7 +4706,8 @@ public function loadRelatedMembershipObjects($ids = []) { $membership->join_date = CRM_Utils_Date::isoToMysql($membership->join_date); $membership->start_date = CRM_Utils_Date::isoToMysql($membership->start_date); $membership->end_date = CRM_Utils_Date::isoToMysql($membership->end_date); - $this->_relatedObjects['membership'][$membership->membership_type_id] = $membership; + $this->_relatedObjects['membership'][$membership->id . '_' . $membership->membership_type_id] = $membership; + } } } diff --git a/tests/phpunit/api/v3/MembershipTest.php b/tests/phpunit/api/v3/MembershipTest.php index 7e03eee9a3e5..4a7486df121c 100644 --- a/tests/phpunit/api/v3/MembershipTest.php +++ b/tests/phpunit/api/v3/MembershipTest.php @@ -173,6 +173,104 @@ public function testActivityForCancelledContribution() { ]); } + /** + * Test Multiple Membership Status for same contribution id. + */ + public function testMultipleMembershipsContribution() { + // Main contact + $memStatus = CRM_Member_PseudoConstant::membershipStatus(); + // Pending Membership Status + $pendingMembershipId = array_search('Pending', $memStatus); + // New Membership Status + $newMembershipId = array_search('test status', $memStatus); + + $membershipParam = [ + 'membership_type_id' => $this->_membershipTypeID, + 'source' => 'Webform Payment', + 'status_id' => $pendingMembershipId, + 'is_pay_later' => 1, + 'skipStatusCal' => 1, + ]; + + // Contact 1 + $contactId1 = $this->individualCreate(); + $membershipParam['contact_id'] = $contactId1; + $membershipID1 = $this->contactMembershipCreate($membershipParam); + + // Pending Payment Status + $ContributionCreate = $this->callAPISuccess('Contribution', 'create', [ + 'financial_type_id' => '1', + 'total_amount' => 100, + 'contact_id' => $contactId1, + 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'), + 'contribution_status_id' => 2, + 'is_pay_later' => 1, + 'receive_date' => date('Ymd'), + ]); + $this->callAPISuccess('MembershipPayment', 'create', [ + 'sequential' => 1, + 'contribution_id' => $ContributionCreate['id'], + 'membership_id' => $membershipID1, + ]); + + // Contact 2 + $contactId2 = $this->individualCreate(); + $membershipParam['contact_id'] = $contactId2; + $membershipID2 = $this->contactMembershipCreate($membershipParam); + $this->callAPISuccess('MembershipPayment', 'create', [ + 'sequential' => 1, + 'contribution_id' => $ContributionCreate['id'], + 'membership_id' => $membershipID2, + ]); + + // Contact 3 + $contactId3 = $this->individualCreate(); + $membershipParam['contact_id'] = $contactId3; + $membershipID3 = $this->contactMembershipCreate($membershipParam); + $this->callAPISuccess('MembershipPayment', 'create', [ + 'sequential' => 1, + 'contribution_id' => $ContributionCreate['id'], + 'membership_id' => $membershipID3, + ]); + + // Change Payment Status to Completed + $form = new CRM_Contribute_Form_Contribution(); + $form->_id = $ContributionCreate['id']; + $params = ['id' => $ContributionCreate['id']]; + $values = $ids = []; + CRM_Contribute_BAO_Contribution::getValues($params, $values, $ids); + $form->_values = $values; + $form->testSubmit([ + 'total_amount' => 100, + 'financial_type_id' => '1', + 'contact_id' => $contactId1, + 'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'), + 'contribution_status_id' => 1, + ], + CRM_Core_Action::UPDATE); + + // check for Membership 1 + $params = ['id' => $membershipID1]; + $membership1 = $this->callAPISuccess('membership', 'get', $params); + $result1 = $membership1['values'][$membershipID1]; + $this->assertEquals($result1['contact_id'], $contactId1); + $this->assertEquals($result1['status_id'], $newMembershipId); + + // check for Membership 2 + $params = ['id' => $membershipID2]; + $membership2 = $this->callAPISuccess('membership', 'get', $params); + $result2 = $membership2['values'][$membershipID2]; + $this->assertEquals($result2['contact_id'], $contactId2); + $this->assertEquals($result2['status_id'], $newMembershipId); + + // check for Membership 3 + $params = ['id' => $membershipID3]; + $membership3 = $this->callAPISuccess('membership', 'get', $params); + $result3 = $membership3['values'][$membershipID3]; + $this->assertEquals($result3['contact_id'], $contactId3); + $this->assertEquals($result3['status_id'], $newMembershipId); + } + /** * Test membership get. */ From bbe2bc5301d38adacf2506993006cf16eb0c8bbc Mon Sep 17 00:00:00 2001 From: Matthew Wire Date: Sat, 22 Aug 2020 22:54:59 +0100 Subject: [PATCH 010/834] Remove 'hack' that overwrites result of searchColumns hook in mailings list --- CRM/Mailing/Page/Browse.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CRM/Mailing/Page/Browse.php b/CRM/Mailing/Page/Browse.php index 9982bd1a79b5..a02b6adcbb70 100644 --- a/CRM/Mailing/Page/Browse.php +++ b/CRM/Mailing/Page/Browse.php @@ -243,11 +243,6 @@ public function run() { $controller->setEmbedded(TRUE); $controller->run(); - // hack to display results as per search - $rows = $controller->getRows($controller); - - $this->assign('rows', $rows); - $urlParams = 'reset=1'; $urlString = 'civicrm/mailing/browse'; if ($this->get('sms')) { From 4917a4fdaeba8e81e8271534061e1ae0fcb67ed9 Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Mon, 24 Aug 2020 08:25:43 +0530 Subject: [PATCH 011/834] dev/core#1942 , correct IPN Test for related membership object key name correction --- tests/phpunit/CRM/Core/Payment/BaseIPNTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/CRM/Core/Payment/BaseIPNTest.php b/tests/phpunit/CRM/Core/Payment/BaseIPNTest.php index acc4eb5433ee..d2d777f9faf3 100644 --- a/tests/phpunit/CRM/Core/Payment/BaseIPNTest.php +++ b/tests/phpunit/CRM/Core/Payment/BaseIPNTest.php @@ -104,8 +104,8 @@ public function testLoadMembershipObjects() { $this->_setUpRecurringContribution(); $this->IPN->loadObjects($this->input, $this->ids, $this->objects, FALSE, $this->_processorId); $this->assertNotEmpty($this->objects['membership']); - $this->assertArrayHasKey($this->_membershipTypeID, $this->objects['membership']); - $this->assertTrue(is_a($this->objects['membership'][$this->_membershipTypeID], 'CRM_Member_BAO_Membership')); + $this->assertArrayHasKey($this->_membershipId . '_' . $this->_membershipTypeID, $this->objects['membership']); + $this->assertTrue(is_a($this->objects['membership'][$this->_membershipId . '_' . $this->_membershipTypeID], 'CRM_Member_BAO_Membership')); $this->assertTrue(is_a($this->objects['financialType'], 'CRM_Financial_BAO_FinancialType')); $this->assertNotEmpty($this->objects['contributionRecur']); $this->assertNotEmpty($this->objects['paymentProcessor']); @@ -145,8 +145,8 @@ public function testLoadMembershipObjectsLoadAll() { $contribution->find(TRUE); $contribution->loadRelatedObjects($this->input, $this->ids, TRUE); $this->assertNotEmpty($contribution->_relatedObjects['membership']); - $this->assertArrayHasKey($this->_membershipTypeID, $contribution->_relatedObjects['membership']); - $this->assertTrue(is_a($contribution->_relatedObjects['membership'][$this->_membershipTypeID], 'CRM_Member_BAO_Membership')); + $this->assertArrayHasKey($this->_membershipId . '_' . $this->_membershipTypeID, $contribution->_relatedObjects['membership']); + $this->assertTrue(is_a($contribution->_relatedObjects['membership'][$this->_membershipId . '_' . $this->_membershipTypeID], 'CRM_Member_BAO_Membership')); $this->assertTrue(is_a($contribution->_relatedObjects['financialType'], 'CRM_Financial_BAO_FinancialType')); $this->assertFalse(empty($contribution->_relatedObjects['contributionRecur'])); $this->assertFalse(empty($contribution->_relatedObjects['paymentProcessor'])); From 1f7272b128156e8f948d8a0a86838aee57c4e021 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sun, 23 Aug 2020 23:16:32 -0400 Subject: [PATCH 012/834] Remove old dropped items from the schema xml Removes unused fields and indexes which were dropped prior to 4.4.7 which is currently the min upgradable version. --- xml/schema/Activity/Activity.xml | 84 ------------------- xml/schema/Activity/ActivityAssignment.xml | 53 ------------ xml/schema/Batch/Batch.xml | 9 -- xml/schema/Case/Case.xml | 62 -------------- xml/schema/Case/CaseActivity.xml | 27 ------ xml/schema/Contact/Contact.xml | 62 -------------- xml/schema/Contact/Individual.xml | 34 -------- xml/schema/Contact/SavedSearch.xml | 17 ---- xml/schema/Contribute/Contribution.xml | 43 ---------- xml/schema/Contribute/ContributionPage.xml | 14 ---- xml/schema/Contribute/ContributionProduct.xml | 11 --- xml/schema/Contribute/ContributionRecur.xml | 24 ------ xml/schema/Contribute/PremiumsProduct.xml | 8 -- xml/schema/Core/Cache.xml | 8 -- xml/schema/Core/CustomField.xml | 8 -- xml/schema/Core/CustomGroup.xml | 8 -- xml/schema/Core/Dashboard.xml | 15 ---- xml/schema/Core/Discount.xml | 19 ----- xml/schema/Core/Domain.xml | 47 ----------- xml/schema/Core/EntityTag.xml | 23 ----- xml/schema/Core/Job.xml | 9 -- xml/schema/Core/Mapping.xml | 10 --- xml/schema/Core/MappingField.xml | 8 -- xml/schema/Core/Phone.xml | 10 --- xml/schema/Core/UFField.xml | 32 ------- xml/schema/Core/UFGroup.xml | 28 ------- xml/schema/Core/UFMatch.xml | 17 ---- xml/schema/Dedupe/RuleGroup.xml | 17 ---- xml/schema/Event/Event.xml | 21 ----- xml/schema/Financial/EntityFinancialTrxn.xml | 9 -- xml/schema/Financial/FinancialAccount.xml | 7 -- xml/schema/Financial/FinancialTrxn.xml | 46 ---------- xml/schema/Financial/FinancialType.xml | 10 --- xml/schema/Financial/PaymentProcessor.xml | 8 -- xml/schema/Grant/Grant.xml | 9 -- xml/schema/Member/MembershipType.xml | 22 ----- xml/schema/PCP/PCP.xml | 23 ----- xml/schema/PCP/PCPBlock.xml | 7 -- xml/schema/Pledge/Pledge.xml | 17 ---- xml/schema/Price/LineItem.xml | 9 -- xml/schema/Price/PriceField.xml | 8 -- xml/schema/Price/PriceSet.xml | 17 ---- 42 files changed, 920 deletions(-) diff --git a/xml/schema/Activity/Activity.xml b/xml/schema/Activity/Activity.xml index 8a37ea3d7615..4f329c1929dc 100644 --- a/xml/schema/Activity/Activity.xml +++ b/xml/schema/Activity/Activity.xml @@ -21,30 +21,6 @@ id true - - source_contact_id - int unsigned - Source Contact - true - /(activity.)?source(.contact(.id)?)?/i - Contact ID of the person scheduling or logging this Activity. Usually the authenticated user. - 1.1 - 4.4 - - - source_contact_id - civicrm_contact
- id - 1.1 - 4.4 - SET NULL -
- - UI_source_contact_id - source_contact_id - 2.0 - 3.2 - source_record_id int unsigned @@ -80,23 +56,6 @@ activity_type_id 1.6 - - target_entity_table - varchar - 64 - true - Name of table where item being referenced is stored. - 1.1 - 2.0 - - - target_entity_id - int unsigned - true - Foreign key to the referenced item. - 1.1 - 2.0 - subject activity_subject @@ -112,13 +71,6 @@ 1.1 2.0 - - scheduled_date - datetime - Date and time meeting is scheduled to occur. - 1.1 - 2.0 - activity_date_time true @@ -140,27 +92,6 @@ activity_date_time 4.7 - - due_date_time - datetime - Date and time this activity is due. - 2.0 - 3.0 - - - duration_hours - int unsigned - Planned or actual duration of meeting - hours. - 1.1 - 2.0 - - - duration_minutes - int unsigned - Planned or actual duration of meeting - minutes. - 1.1 - 2.0 - duration activity_duration @@ -231,15 +162,6 @@ 1.1 - - status - enum - Status - Scheduled, Completed - What is the status of this meeting? Completed meeting status results in activity history entry. - 1.1 - 2.0 - status_id activity_status_id @@ -374,12 +296,6 @@ 2.2 CASCADE - - UI_original_id - original_id - 2.2 - 3.2 - result activity_result diff --git a/xml/schema/Activity/ActivityAssignment.xml b/xml/schema/Activity/ActivityAssignment.xml index f3df06bb54ab..192af7dc8aa7 100644 --- a/xml/schema/Activity/ActivityAssignment.xml +++ b/xml/schema/Activity/ActivityAssignment.xml @@ -18,59 +18,6 @@ id true - - - activity_entity_table - varchar - 64 - true - Name of table where item being referenced is stored (activity, phonecall or meeting). - 1.8 - 2.0 - - - - activity_entity_id - int unsigned - true - Entity (activity, phonecall or meeting) id for which the assigment is created - 1.8 - 2.0 - - - - activity_entity_id - activity_entity_table - 1.8 - 2.0 - - - - target_entity_table - varchar - 64 - true - Name of table where item being referenced is stored (contact assigned to given activity). - 1.8 - 2.0 - - - - target_entity_id - int unsigned - true - Foreign key to the referenced item. - 1.1 - 2.0 - - - - target_entity_id - target_entity_table - 1.8 - 2.0 - - activity_id int unsigned diff --git a/xml/schema/Batch/Batch.xml b/xml/schema/Batch/Batch.xml index 3bcbc4440921..811ecf4f219f 100644 --- a/xml/schema/Batch/Batch.xml +++ b/xml/schema/Batch/Batch.xml @@ -35,15 +35,6 @@ true 4.2 - - label - varchar - 64 - true - Friendly Name. - 3.3 - 4.2 - title Batch Title diff --git a/xml/schema/Case/Case.xml b/xml/schema/Case/Case.xml index a91e955a55eb..492a1df43d46 100644 --- a/xml/schema/Case/Case.xml +++ b/xml/schema/Case/Case.xml @@ -24,23 +24,6 @@ id true - - contact_id - int unsigned - case_contact_id - true - Contact ID of contact record given case belongs to. - 1.8 - 2.1 - - - contact_id - civicrm_contact
- id - 1.8 - 2.1 - CASCADE -
case_type_id int unsigned @@ -70,51 +53,6 @@ id 4.5 - - casetag1_id - varchar - 128 - true - Id of first case category. - 1.8 - 2.0 - - - index_casetag1_id - casetag1_id - 1.8 - 2.0 - - - casetag2_id - varchar - 128 - true - Id of second case category. - 1.8 - 2.0 - - - index_casetag2_id - casetag2_id - 1.8 - 2.0 - - - casetag3_id - varchar - 128 - true - Id of third case category. - 1.8 - 2.0 - - - index_casetag3_id - casetag3_id - 1.8 - 2.0 - subject varchar diff --git a/xml/schema/Case/CaseActivity.xml b/xml/schema/Case/CaseActivity.xml index 0ae6710e4d74..daa0a895d216 100644 --- a/xml/schema/Case/CaseActivity.xml +++ b/xml/schema/Case/CaseActivity.xml @@ -54,31 +54,4 @@ activity_id 2.0 - - - activity_entity_table - varchar - 64 - true - Name of table where item being referenced is stored (activity, phonecall or meeting). - 1.8 - 2.0 - - - - activity_entity_id - int unsigned - true - Entity (activity, phonecall or meeting) id for which the assigment is created - 1.8 - 2.0 - - - - activity_entity_id - activity_entity_table - 1.8 - 2.0 - - diff --git a/xml/schema/Contact/Contact.xml b/xml/schema/Contact/Contact.xml index f904c09cff1e..b3721ab2b85e 100644 --- a/xml/schema/Contact/Contact.xml +++ b/xml/schema/Contact/Contact.xml @@ -246,19 +246,6 @@ 1.1 - - home_URL - url - Website - varchar - 128 - true - /^(home\sURL)|URL|web|site/i - /^[\w\/\:\.]+$/ - optional "home page" URL for this contact. - 1.1 - 3.2 - image_URL text @@ -509,32 +496,6 @@ communication_style_id 4.4 - - greeting_type - varchar - 128 - true - Preferred greeting format. - 1.1 - 2.2 - - - greeting_type_id - Greeting Type - int unsigned - FK to civicrm_option_value.id, that has to be valid, registered Greeting type. - 2.2 - 3.0 - - - custom_greeting - varchar - 128 - true - Custom greeting message. - 1.1 - 3.0 - email_greeting_id int unsigned @@ -722,23 +683,6 @@ Individual - - mail_to_household_id - Mail to Household ID - int unsigned - OPTIONAL FK to civicrm_contact_household record. If NOT NULL, direct mail communications to household rather than individual location. - true - 1.1 - 3.3 - - - mail_to_household_id - civicrm_contact
- id - 2.1 - SET NULL - 3.3 -
household_name varchar @@ -858,12 +802,6 @@ access deleted contacts - - index_is_deleted - is_deleted - 3.2 - 4.4 - index_is_deleted_sort_name is_deleted diff --git a/xml/schema/Contact/Individual.xml b/xml/schema/Contact/Individual.xml index 45f272067786..9956f17bebaf 100644 --- a/xml/schema/Contact/Individual.xml +++ b/xml/schema/Contact/Individual.xml @@ -74,14 +74,6 @@ last_name 1.8 - - gender - enum - true - Female, Male, Other - 1.1 - 1.2 - prefix_id int unsigned @@ -108,32 +100,6 @@ suffix_id 1.6 - - prefix - varchar - 64 - true - Prefix to Name. - 1.1 - 1.2 - - - suffix - varchar - 64 - true - Suffix to Name. - 1.1 - 1.2 - - - greeting_type - varchar - 128 - Preferred greeting format. - 1.1 - 2.2 - greeting_type_id Greeting Type diff --git a/xml/schema/Contact/SavedSearch.xml b/xml/schema/Contact/SavedSearch.xml index 89f578084b33..2d73182ac5c5 100644 --- a/xml/schema/Contact/SavedSearch.xml +++ b/xml/schema/Contact/SavedSearch.xml @@ -18,15 +18,6 @@ id false - - query - SQL Query - text - true - SQL query for this search - 1.1 - 1.5 - form_values Submitted Form Values @@ -36,14 +27,6 @@ PHP 1.1 - - is_active - boolean - Saved Search Enabled - Is this entry active? - 1.1 - 1.5 - mapping_id int unsigned diff --git a/xml/schema/Contribute/Contribution.xml b/xml/schema/Contribute/Contribution.xml index e98363baa93b..5edebe7bc3d3 100644 --- a/xml/schema/Contribute/Contribution.xml +++ b/xml/schema/Contribute/Contribution.xml @@ -46,39 +46,6 @@ 1.3 CASCADE - - solicitor_id - Solicitor ID - int unsigned - FK to Solicitor ID - 1.4 - 2.2 - - - solicitor_id - civicrm_contact
- id - 1.4 - 2.2 - SET NULL -
- - contribution_type_id - Contribution Type - false - int unsigned - FK to Contribution Type - 1.3 - 4.3 - - - contribution_type_id - civicrm_contribution_type
- id - 1.3 - 4.3 - SET NULL -
financial_type_id Financial Type @@ -360,16 +327,6 @@ Text - - note - text - Note and/or Comment. - true - /Note|Comment/i - // - 1.4 - 1.7 - UI_contrib_trxn_id trxn_id diff --git a/xml/schema/Contribute/ContributionPage.xml b/xml/schema/Contribute/ContributionPage.xml index 15bd5218edaa..fb93703f7476 100644 --- a/xml/schema/Contribute/ContributionPage.xml +++ b/xml/schema/Contribute/ContributionPage.xml @@ -41,20 +41,6 @@ Text and html allowed. Displayed below title. 1.3
- - contribution_type_id - int unsigned - true - default Contribution type assigned to contributions submitted via this page, e.g. Contribution, Campaign Contribution - 1.3 - 4.3 - - - contribution_type_id - civicrm_contribution_type
- id - 4.3 -
financial_type_id Financial Type diff --git a/xml/schema/Contribute/ContributionProduct.xml b/xml/schema/Contribute/ContributionProduct.xml index d5ff804a1009..1e3526d27c5a 100644 --- a/xml/schema/Contribute/ContributionProduct.xml +++ b/xml/schema/Contribute/ContributionProduct.xml @@ -55,17 +55,6 @@ 1.4 - - total_cost - decimal - true - true - /^total|(.?^am(ou)?nt)/i - /^\d+(\.\d{2})?$/ - quantity X civicrm_product.cost. - 1.3 - 4.1 - fulfilled_date date diff --git a/xml/schema/Contribute/ContributionRecur.xml b/xml/schema/Contribute/ContributionRecur.xml index eddc342d9c5a..11e70b6afc49 100644 --- a/xml/schema/Contribute/ContributionRecur.xml +++ b/xml/schema/Contribute/ContributionRecur.xml @@ -286,13 +286,6 @@ Text - - - next_sched_contribution - datetime - At Groundspring this was used by the cron job which triggered payments. If we''re not doing that but we know about payments, it might still be useful to store for display to org andor contributors. - 1.6 - 4.4 next_sched_contribution_date @@ -366,23 +359,6 @@ 3.3 SET NULL - - contribution_type_id - Contribution Type - false - int unsigned - FK to Contribution Type - 4.1 - 4.3 - - - contribution_type_id - civicrm_contribution_type
- id - 4.1 - 4.3 - SET NULL -
financial_type_id Financial Type diff --git a/xml/schema/Contribute/PremiumsProduct.xml b/xml/schema/Contribute/PremiumsProduct.xml index 7f9b86604767..6ed7ca1ce617 100644 --- a/xml/schema/Contribute/PremiumsProduct.xml +++ b/xml/schema/Contribute/PremiumsProduct.xml @@ -47,14 +47,6 @@ id 1.4 - - sort_position - Sort Position - int unsigned - true - 1.4 - 2.0 - weight Order diff --git a/xml/schema/Core/Cache.xml b/xml/schema/Core/Cache.xml index 564a2773292b..1e8d9a389ff7 100644 --- a/xml/schema/Core/Cache.xml +++ b/xml/schema/Core/Cache.xml @@ -35,14 +35,6 @@ Unique path name for cache element 2.1 - - UI_group_path - group_name - path - true - 2.1 - 4.2 - UI_group_path_date group_name diff --git a/xml/schema/Core/CustomField.xml b/xml/schema/Core/CustomField.xml index 2bfd28999a9e..8d49adce76d0 100644 --- a/xml/schema/Core/CustomField.xml +++ b/xml/schema/Core/CustomField.xml @@ -211,14 +211,6 @@ Date may be up to end_date_years years after the current date. 1.4 - - date_parts - varchar - 255 - which date part included in display - 1.4 - 3.1 - date_format varchar diff --git a/xml/schema/Core/CustomGroup.xml b/xml/schema/Core/CustomGroup.xml index e0f424ddfc49..e21458ecbafa 100644 --- a/xml/schema/Core/CustomGroup.xml +++ b/xml/schema/Core/CustomGroup.xml @@ -47,14 +47,6 @@ Type of object this group extends (can add other options later e.g. contact_address, etc.). 1.1 - - extends_entity_column_name - varchar - 64 - linking custom group for dynamic object - 1.6 - 2.2 - extends_entity_column_id int unsigned diff --git a/xml/schema/Core/Dashboard.xml b/xml/schema/Core/Dashboard.xml index 00500943a8da..6b34614dbcc2 100644 --- a/xml/schema/Core/Dashboard.xml +++ b/xml/schema/Core/Dashboard.xml @@ -62,13 +62,6 @@ url in case of external dashlet 3.1 - - content - text - dashlet content - 3.1 - 3.3 - permission varchar @@ -155,12 +148,4 @@ true 4.7 - - created_date - datetime - Dashlet Created Date - When was content populated - 3.1 - 3.3 - diff --git a/xml/schema/Core/Discount.xml b/xml/schema/Core/Discount.xml index 01edced71da3..54535e485764 100644 --- a/xml/schema/Core/Discount.xml +++ b/xml/schema/Core/Discount.xml @@ -46,25 +46,6 @@ entity_id 2.1 - - option_group_id - participant_discount_name - Discount Name - int unsigned - true - true - FK to civicrm_price_set - 2.1 - 4.3 - - - option_group_id - civicrm_price_set
- id - 2.1 - CASCADE - 4.3 -
price_set_id participant_discount_name diff --git a/xml/schema/Core/Domain.xml b/xml/schema/Core/Domain.xml index ddb2a0dc6a2f..8e4e119a7f31 100644 --- a/xml/schema/Core/Domain.xml +++ b/xml/schema/Core/Domain.xml @@ -46,46 +46,6 @@ true 1.1 - - contact_name - varchar - 64 - Name of the person responsible for this domain - 1.1 - 1.9 - - - email_name - varchar - 255 - The default email name that is used in the from address for all outgoing emails - 1.9 - 2.2 - - - email_address - varchar - 255 - The default email address that is used as the from address for all outgoing emails - 1.9 - 2.2 - - - email_domain - varchar - 64 - The domain from which outgoing email for this domain will appear to originate - 1.1 - 2.2 - - - email_return_path - varchar - 64 - The domain from which outgoing email for this domain will appear to originate - 1.1 - 2.2 - config_backend text @@ -103,13 +63,6 @@ The civicrm version this instance is running 2.0 - - loc_block_id - int unsigned - FK to Location Block ID. This is specifically not an FK to avoid circular constraints - 2.0 - 4.3 - contact_id int unsigned diff --git a/xml/schema/Core/EntityTag.xml b/xml/schema/Core/EntityTag.xml index 0fed7e7cf9ac..301706a42b4d 100644 --- a/xml/schema/Core/EntityTag.xml +++ b/xml/schema/Core/EntityTag.xml @@ -19,22 +19,6 @@ id true - - contact_id - int unsigned - true - FK to contact table. - 2.0 - 3.2 - - - contact_id - civicrm_contact
- id - 2.0 - 3.2 - CASCADE -
entity_table varchar @@ -59,13 +43,6 @@ entity_table 3.2 - - index_entity - entity_table - entity_id - 3.2 - 3.4 - tag_id int unsigned diff --git a/xml/schema/Core/Job.xml b/xml/schema/Core/Job.xml index 45429a4afff9..a135360f051b 100644 --- a/xml/schema/Core/Job.xml +++ b/xml/schema/Core/Job.xml @@ -85,15 +85,6 @@ Description of the job 4.1 - - api_prefix - varchar - 255 - "civicrm_api3" - Prefix of the job api call - 4.1 - 4.3 - api_entity API Entity diff --git a/xml/schema/Core/Mapping.xml b/xml/schema/Core/Mapping.xml index 54ab26d3d0be..bd5420ea2715 100644 --- a/xml/schema/Core/Mapping.xml +++ b/xml/schema/Core/Mapping.xml @@ -35,16 +35,6 @@ Description of Mapping. 1.2 - - mapping_type - enum - Export, Import, Export Contributions, Import Contributions, Import Activity, Search Builder, Import - Memberships, Import Participants - - Type of Mapping. - 1.2 - 2.1 - mapping_type_id int unsigned diff --git a/xml/schema/Core/MappingField.xml b/xml/schema/Core/MappingField.xml index d28b69b30c94..0c444143756a 100644 --- a/xml/schema/Core/MappingField.xml +++ b/xml/schema/Core/MappingField.xml @@ -78,14 +78,6 @@ id 1.2 - - phone_type - varchar - 64 - Phone type, if required - 1.2 - 2.2 - phone_type_id Phone Type diff --git a/xml/schema/Core/Phone.xml b/xml/schema/Core/Phone.xml index 91ab823d39ff..eb0ad8e1f6a9 100644 --- a/xml/schema/Core/Phone.xml +++ b/xml/schema/Core/Phone.xml @@ -137,16 +137,6 @@ phone_numeric 4.3 - - phone_type - enum - Phone, Mobile, Fax, Pager - /phone\s+type/i - /phone|mobile|fax|pager/i - What type of telecom device is this. - 1.1 - 2.2 - phone_type_id Phone Type ID diff --git a/xml/schema/Core/UFField.xml b/xml/schema/Core/UFField.xml index fdcdcf83b3f3..9b012ab4c837 100644 --- a/xml/schema/Core/UFField.xml +++ b/xml/schema/Core/UFField.xml @@ -103,22 +103,6 @@ Description and/or help text to display before this field. 3.2 - - is_registration - boolean - 0 - Is this field included in new user registration forms? - 1.1 - 1.3 - - - is_match - boolean - 0 - Is this field part of the key for matching users to contacts? - 1.1 - 1.3 - visibility Profile Field Visibility @@ -134,14 +118,6 @@ Select - - listings_title - varchar - 64 - Page title for listings page (users who share a common value for this property). - 1.1 - 1.2 - in_selector Profile Field Is a Filter @@ -172,14 +148,6 @@ 1.3 SET NULL - - phone_type - varchar - 64 - Phone type, if required - 1.3 - 2.2 - phone_type_id Profile Field Phone Type diff --git a/xml/schema/Core/UFGroup.xml b/xml/schema/Core/UFGroup.xml index cafe160ac631..37d5d0a8aa8d 100644 --- a/xml/schema/Core/UFGroup.xml +++ b/xml/schema/Core/UFGroup.xml @@ -37,13 +37,6 @@ COMMA 2.1 - - form_type - enum - CiviCRM Profile - Type of form. - 2.1 - title Profile Name @@ -81,14 +74,6 @@ Optional verbose description of the profile. 4.4 - - collapse_display - int unsigned - 0 - Will this group be in collapsed or expanded mode on initial display ? - 1.1 - 2.2 - help_pre text @@ -114,19 +99,6 @@ 1.2 - - weight - Profile Weight - int - true - 1 - - Text - - Controls display order when multiple user framework groups are setup for concurrent display. - 1.2 - 1.3 - limit_listings_group_id Profile Search Limit Group diff --git a/xml/schema/Core/UFMatch.xml b/xml/schema/Core/UFMatch.xml index fb3a05425dfb..9e573a5a79df 100644 --- a/xml/schema/Core/UFMatch.xml +++ b/xml/schema/Core/UFMatch.xml @@ -73,15 +73,6 @@ 1.1 CASCADE - - email - varchar - 64 - email - Email address - 1.1 - 2.0 - language Preferred Language @@ -90,14 +81,6 @@ UI language preferred by the given user/contact 2.1 - - UI_uf_id_domain_id - uf_id - domain_id - true - 1.5 - 1.7 - UI_uf_name_domain_id uf_name diff --git a/xml/schema/Dedupe/RuleGroup.xml b/xml/schema/Dedupe/RuleGroup.xml index 6adbe3dba41e..09a587362838 100644 --- a/xml/schema/Dedupe/RuleGroup.xml +++ b/xml/schema/Dedupe/RuleGroup.xml @@ -45,15 +45,6 @@ Text - - level - Level - enum - Strict, Fuzzy - Whether the rule should be used for cases where strict matching of the given contact type is required or a fuzzy one - 2.1 - 4.3 - used varchar @@ -69,14 +60,6 @@ Radio - - is_default - Default> - boolean - Is this a default rule (one rule for every contact type + level combination should be default) - 2.1 - 4.3 - name Name diff --git a/xml/schema/Event/Event.xml b/xml/schema/Event/Event.xml index 4c7e51143f85..4593c92d4e56 100644 --- a/xml/schema/Event/Event.xml +++ b/xml/schema/Event/Event.xml @@ -218,14 +218,6 @@ CheckBox - - contribution_type_id - int unsigned - 0 - Contribution type assigned to paid event registrations for this event. Required if is_monetary is true. - 1.7 - 4.3 - financial_type_id int unsigned @@ -318,19 +310,6 @@ SET NULL 2.0 - - receipt_text - varchar - - TextArea - 6 - 50 - - 255 - Receipt Text for off-line event participation - 2.0 - 2.1 - default_role_id default_role_id diff --git a/xml/schema/Financial/EntityFinancialTrxn.xml b/xml/schema/Financial/EntityFinancialTrxn.xml index 653fc792d5d6..f48a3f3c6574 100644 --- a/xml/schema/Financial/EntityFinancialTrxn.xml +++ b/xml/schema/Financial/EntityFinancialTrxn.xml @@ -63,15 +63,6 @@ allocated amount of transaction to this entity 3.2 - - currency - varchar - 3 - NULL - 3 character string, value from config setting or input via user. - 3.2 - 4.3 - UI_entity_financial_trxn_entity_table entity_table diff --git a/xml/schema/Financial/FinancialAccount.xml b/xml/schema/Financial/FinancialAccount.xml index eedf5c621069..3188e5847e31 100644 --- a/xml/schema/Financial/FinancialAccount.xml +++ b/xml/schema/Financial/FinancialAccount.xml @@ -42,13 +42,6 @@ 4.3 SET NULL - - account_type_id - int unsigned - true - 3.2 - 4.3 - financial_account_type_id int unsigned diff --git a/xml/schema/Financial/FinancialTrxn.xml b/xml/schema/Financial/FinancialTrxn.xml index 94a2e3f023e5..e34d9b91123c 100644 --- a/xml/schema/Financial/FinancialTrxn.xml +++ b/xml/schema/Financial/FinancialTrxn.xml @@ -17,34 +17,6 @@ id true - - from_account_id - int unsigned - FK to financial_account table. - 3.2 - 4.3 - - - from_account_id - civicrm_financial_account
- id - 3.2 - 4.3 -
- - to_account_id - int unsigned - FK to financial_account table. - 3.2 - 4.3 - - - to_account_id - civicrm_financial_account
- id - 3.2 - 4.3 -
from_financial_account_id int unsigned @@ -99,15 +71,6 @@ activityDateTime - - trxn_type - Financial Transaction Type - enum - Debit,Credit - true - 1.3 - 4.3 - total_amount Financial Total Amount @@ -161,15 +124,6 @@ Is this entry either a payment or a reversal of a payment? 4.7 - - payment_processor - varchar - 64 - true - derived from Processor setting in civicrm.settings.php. - 1.3 - 4.3 - trxn_id Transaction ID diff --git a/xml/schema/Financial/FinancialType.xml b/xml/schema/Financial/FinancialType.xml index bbdca886dae8..3d02bf8b0991 100644 --- a/xml/schema/Financial/FinancialType.xml +++ b/xml/schema/Financial/FinancialType.xml @@ -37,16 +37,6 @@ 1.3 - - accounting_code - Accounting Code - varchar - 64 - true - Optional value for mapping contributions to accounting system codes for each type/category of contribution. - 1.3 - 4.3 - description varchar diff --git a/xml/schema/Financial/PaymentProcessor.xml b/xml/schema/Financial/PaymentProcessor.xml index e1aac7cab331..a363ce878467 100644 --- a/xml/schema/Financial/PaymentProcessor.xml +++ b/xml/schema/Financial/PaymentProcessor.xml @@ -70,14 +70,6 @@ Payment Processor Description. 1.8 - - payment_processor_type - varchar - 255 - Payment Processor Type. - 1.8 - 4.3 - payment_processor_type_id Payment Processor Type ID diff --git a/xml/schema/Grant/Grant.xml b/xml/schema/Grant/Grant.xml index 6e222f8e0d69..b6c2c8923519 100644 --- a/xml/schema/Grant/Grant.xml +++ b/xml/schema/Grant/Grant.xml @@ -179,15 +179,6 @@ Select - - currency - varchar - 8 - NULL - 3 character string, value from config setting or input via user. - 3.2 - 4.3 - rationale text diff --git a/xml/schema/Member/MembershipType.xml b/xml/schema/Member/MembershipType.xml index 0007eefaf2fe..cd528adaf26f 100644 --- a/xml/schema/Member/MembershipType.xml +++ b/xml/schema/Member/MembershipType.xml @@ -84,21 +84,6 @@ 1.5 RESTRICT - - contribution_type_id - int unsigned - true - If membership is paid by a contribution - what contribution type should be used. FK to Contribution Type ID - 1.5 - 4.3 - - - contribution_type_id - civicrm_contribution_type
- id - 1.5 - 4.3 -
financial_type_id Membership Financial Type @@ -192,13 +177,6 @@ 1.5 SEPARATOR_TRIMMED - - relationship_type_id - civicrm_relationship_type
- id - 1.5 - 3.3 -
index_relationship_type_id relationship_type_id diff --git a/xml/schema/PCP/PCP.xml b/xml/schema/PCP/PCP.xml index dfb8a13f8c74..0ef29ee2563c 100644 --- a/xml/schema/PCP/PCP.xml +++ b/xml/schema/PCP/PCP.xml @@ -91,20 +91,6 @@ Text
- - contribution_page_id - int unsigned - true - The Contribution Page which triggered this pcp - 2.2 - 4.1 - - - contribution_page_id - civicrm_contribution_page
- id - 4.1 -
page_id Contribution Page @@ -182,15 +168,6 @@ Select - - referer - Referer - varchar - 255 - NULL - 2.2 - 4.1 - is_active Enabled? diff --git a/xml/schema/PCP/PCPBlock.xml b/xml/schema/PCP/PCPBlock.xml index b07bc5b03555..d25b6eb7c221 100644 --- a/xml/schema/PCP/PCPBlock.xml +++ b/xml/schema/PCP/PCPBlock.xml @@ -39,13 +39,6 @@ entity_table 2.2 - - entity_id - civicrm_contribution_page
- id - 2.2 - 4.1 -
target_entity_type Target Entity diff --git a/xml/schema/Pledge/Pledge.xml b/xml/schema/Pledge/Pledge.xml index 39943c58efa9..32b806f39f52 100644 --- a/xml/schema/Pledge/Pledge.xml +++ b/xml/schema/Pledge/Pledge.xml @@ -40,23 +40,6 @@ 2.1 CASCADE - - contribution_type_id - pledge_contribution_type_id - false - int unsigned - FK to Contribution Type. This is propagated to contribution record when pledge payments are made. - 2.1 - 4.3 - - - contribution_type_id - civicrm_contribution_type
- id - 2.1 - 4.3 - SET NULL -
financial_type_id Type diff --git a/xml/schema/Price/LineItem.xml b/xml/schema/Price/LineItem.xml index a1f006c9b535..82b9c9a7cc56 100644 --- a/xml/schema/Price/LineItem.xml +++ b/xml/schema/Price/LineItem.xml @@ -67,15 +67,6 @@ id SET NULL - - option_group_id - Line Item Option Group - int unsigned - true - FK to option group - 1.7 - 3.3 - label Line Item Label diff --git a/xml/schema/Price/PriceField.xml b/xml/schema/Price/PriceField.xml index 0c2c1af3c1a8..ce61ecc87c4f 100644 --- a/xml/schema/Price/PriceField.xml +++ b/xml/schema/Price/PriceField.xml @@ -221,12 +221,4 @@ Select - - count - int unsigned - NULL - Number of Participants Per field - 3.2 - 3.3 - diff --git a/xml/schema/Price/PriceSet.xml b/xml/schema/Price/PriceSet.xml index 25266f1dbfc4..d84ac533927c 100644 --- a/xml/schema/Price/PriceSet.xml +++ b/xml/schema/Price/PriceSet.xml @@ -137,23 +137,6 @@ Text - - contribution_type_id - Price Set Contribution Type - int unsigned - NULL - FK to Contribution Type(for membership price sets only). - 3.4 - 4.3 - - - contribution_type_id - civicrm_contribution_type
- id - 3.4 - 4.3 - SET NULL -
financial_type_id Financial Type From effbcf8507b70128d3bc0b4a33428180f5a6682a Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Mon, 24 Aug 2020 16:44:23 +0530 Subject: [PATCH 013/834] report#45 Ability Search Smart or Normal Group using additional filter on Manage Group page --- CRM/Contact/BAO/Group.php | 8 ++++++++ CRM/Group/Form/Search.php | 8 ++++++++ CRM/Group/Page/AJAX.php | 1 + templates/CRM/Group/Form/Search.tpl | 8 ++++++++ 4 files changed, 25 insertions(+) diff --git a/CRM/Contact/BAO/Group.php b/CRM/Contact/BAO/Group.php index fae22f03b844..cff225818282 100644 --- a/CRM/Contact/BAO/Group.php +++ b/CRM/Contact/BAO/Group.php @@ -1223,6 +1223,14 @@ public static function whereClause(&$params, $sortBy = TRUE, $excludeHidden = TR $clauses[] = '`groups`.parents IS NULL'; } + $savedSearch = CRM_Utils_Array::value('savedSearch', $params); + if ($savedSearch == 1) { + $clauses[] = 'groups.saved_search_id IS NOT NULL'; + } + else if ($savedSearch == 2) { + $clauses[] = 'groups.saved_search_id IS NULL'; + } + // only show child groups of a specific parent group $parent_id = $params['parent_id'] ?? NULL; if ($parent_id) { diff --git a/CRM/Group/Form/Search.php b/CRM/Group/Form/Search.php index 819b061f21d6..a56623fefc01 100644 --- a/CRM/Group/Form/Search.php +++ b/CRM/Group/Form/Search.php @@ -40,6 +40,14 @@ public function buildQuickForm() { CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Group', 'title') ); + $optionTypes = [ + '1' => ts('Smart Group'), + '2' => ts('Normal Group'), + ]; + $this->add('select', 'saved_search', ts('Group Type'), + ['' => ts('- any -')] + $optionTypes + ); + $groupTypes = CRM_Core_OptionGroup::values('group_type', TRUE); $config = CRM_Core_Config::singleton(); if ($config->userFramework == 'Joomla') { diff --git a/CRM/Group/Page/AJAX.php b/CRM/Group/Page/AJAX.php index b314d5cb6168..9e6d809f3730 100644 --- a/CRM/Group/Page/AJAX.php +++ b/CRM/Group/Page/AJAX.php @@ -42,6 +42,7 @@ public static function getGroupList() { 'status' => 'Integer', 'parentsOnly' => 'Integer', 'showOrgInfo' => 'Boolean', + 'savedSearch' => 'Integer', // Ignore 'parent_id' as that case is handled above ]; $params = CRM_Core_Page_AJAX::defaultSortAndPagerParams(); diff --git a/templates/CRM/Group/Form/Search.tpl b/templates/CRM/Group/Form/Search.tpl index a2019f4d1c2c..e01ece5d192b 100644 --- a/templates/CRM/Group/Form/Search.tpl +++ b/templates/CRM/Group/Form/Search.tpl @@ -55,6 +55,13 @@ {$form.component_mode.html} + + + {$form.saved_search.label}
{$form.saved_search.html} + + + + @@ -116,6 +123,7 @@ d.group_type = groupTypes, d.visibility = $(".crm-group-search-form-block select#visibility").val(), d.status = groupStatus, + d.savedSearch = $('.crm-group-search-form-block select#saved_search').val(), d.component_mode = $(".crm-group-search-form-block select#component_mode").val(), d.showOrgInfo = {/literal}"{$showOrgInfo}"{literal}, d.parentsOnly = parentsOnly From 637368b5efbb8aa007a8fa35b514421371bc2c5c Mon Sep 17 00:00:00 2001 From: Monish Deb Date: Mon, 24 Aug 2020 17:48:25 +0530 Subject: [PATCH 014/834] Optimise proximity custom search, by reducing addGeocodingData fn call --- CRM/Contact/Form/Search/Custom/Proximity.php | 22 ++++---------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/CRM/Contact/Form/Search/Custom/Proximity.php b/CRM/Contact/Form/Search/Custom/Proximity.php index 34026c06de81..8d211aafcf7b 100644 --- a/CRM/Contact/Form/Search/Custom/Proximity.php +++ b/CRM/Contact/Form/Search/Custom/Proximity.php @@ -19,9 +19,6 @@ */ class CRM_Contact_Form_Search_Custom_Proximity extends CRM_Contact_Form_Search_Custom_Base implements CRM_Contact_Form_Search_Interface { - protected $_latitude = NULL; - protected $_longitude = NULL; - protected $_distance = NULL; protected $_aclFrom = NULL; protected $_aclWhere = NULL; @@ -39,21 +36,6 @@ public function __construct(&$formValues) { unset($this->_formValues['uf_group_id']); unset($this->_formValues['component_mode']); unset($this->_formValues['operator']); - - if (!empty($this->_formValues)) { - // add the country and state - self::addGeocodingData($this->_formValues); - $this->_latitude = $this->_formValues['geo_code_1']; - $this->_longitude = $this->_formValues['geo_code_2']; - - if ($this->_formValues['prox_distance_unit'] == "miles") { - $conversionFactor = 1609.344; - } - else { - $conversionFactor = 1000; - } - $this->_distance = $this->_formValues['distance'] * $conversionFactor; - } $this->_group = $this->_formValues['group'] ?? NULL; $this->_tag = $this->_formValues['tag'] ?? NULL; @@ -192,6 +174,10 @@ public function sql( $isCountOnly = TRUE; } + if (empty($this->_formValues['geo_code_1']) || empty($this->_formValues['geo_code_2'])) { + self::addGeocodingData($this->_formValues); + } + $searchParams = [ ['prox_distance_unit', '=', $this->_formValues['prox_distance_unit'], 0, 0], ['prox_distance', '=', $this->_formValues['distance'], 0, 0], From ff1767f31187a0ae47c834e24ca60292300c7fec Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Mon, 24 Aug 2020 18:19:13 +0530 Subject: [PATCH 015/834] Test case , look for smart group only --- tests/phpunit/CRM/Group/Page/AjaxTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/phpunit/CRM/Group/Page/AjaxTest.php b/tests/phpunit/CRM/Group/Page/AjaxTest.php index 8a88946e4d34..686e16fc9f3c 100644 --- a/tests/phpunit/CRM/Group/Page/AjaxTest.php +++ b/tests/phpunit/CRM/Group/Page/AjaxTest.php @@ -553,6 +553,8 @@ public function testGroupDontRegenerateSmartGroups() { // Load the Manage Group page code and we should get a count from our // group because the cache is fresh. $_GET = $this->_params; + // look for Smart Group only + $_GET['savedSearch'] = 1; try { CRM_Group_Page_AJAX::getGroupList(); } From f33893fdebd2c077404e8cd0ee14943a91160880 Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Mon, 24 Aug 2020 18:22:26 +0530 Subject: [PATCH 016/834] Small Correction --- CRM/Contact/BAO/Group.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CRM/Contact/BAO/Group.php b/CRM/Contact/BAO/Group.php index cff225818282..5fe886756d20 100644 --- a/CRM/Contact/BAO/Group.php +++ b/CRM/Contact/BAO/Group.php @@ -1227,7 +1227,7 @@ public static function whereClause(&$params, $sortBy = TRUE, $excludeHidden = TR if ($savedSearch == 1) { $clauses[] = 'groups.saved_search_id IS NOT NULL'; } - else if ($savedSearch == 2) { + elseif ($savedSearch == 2) { $clauses[] = 'groups.saved_search_id IS NULL'; } From e066681dfad3025f9edb1681ceb4a263b913b1c2 Mon Sep 17 00:00:00 2001 From: Seamus Lee Date: Tue, 25 Aug 2020 17:57:32 +1000 Subject: [PATCH 017/834] [REF] Ensure that the form param _id is set when adding a new group with the newly created group id so that consumers of the hook_civicrm_postProcess can access the id --- CRM/Group/Form/Edit.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CRM/Group/Form/Edit.php b/CRM/Group/Form/Edit.php index d9be20cdfb86..e384dc36cc7f 100644 --- a/CRM/Group/Form/Edit.php +++ b/CRM/Group/Form/Edit.php @@ -371,7 +371,8 @@ public function postProcess() { ); $group = CRM_Contact_BAO_Group::create($params); - + // Set the entity id so it is available to postProcess hook consumers + $this->setEntityId($group->id); //Remove any parent groups requested to be removed if (!empty($this->_groupValues['parents'])) { $parentGroupIds = explode(',', $this->_groupValues['parents']); From d07e605a2d32249f23c32608e85442a466dae471 Mon Sep 17 00:00:00 2001 From: John Kingsnorth Date: Tue, 25 Aug 2020 11:26:30 +0100 Subject: [PATCH 018/834] dev/core#1971 Test for domain-specific option values --- tests/phpunit/CRM/Core/OptionGroupTest.php | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/phpunit/CRM/Core/OptionGroupTest.php b/tests/phpunit/CRM/Core/OptionGroupTest.php index 863daff7d816..d09af84302c9 100644 --- a/tests/phpunit/CRM/Core/OptionGroupTest.php +++ b/tests/phpunit/CRM/Core/OptionGroupTest.php @@ -91,4 +91,52 @@ public function testSanitizeFromEmailAddress($dirty, $clean) { $this->assertEquals($actual, $clean); } + public function testDomainSpecificValueCache() { + $original_domain = \CRM_Core_Config::domainID(); + $domainIDs = []; + $optionValues = []; + + // Create domains + $domainIDs[] = $this->callAPISuccess('Domain', 'create', [ + 'name' => "Test extra domain 1", + 'domain_version' => CRM_Utils_System::version(), + ])['id']; + $domainIDs[] = $this->callAPISuccess('Domain', 'create', [ + 'name' => "Test extra domain 2", + 'domain_version' => CRM_Utils_System::version(), + ])['id']; + + // Create 'from' email addresses + foreach ($domainIDs as $domainID) { + $result = $this->callAPISuccess('option_value', 'create', [ + 'option_group_id' => 'from_email_address', + 'name' => '"Test ' . $domainID . '" ', + 'label' => '"Test ' . $domainID . '" ', + 'value' => 'test' . $domainID, + 'is_active' => 1, + 'domain_id' => $domainID, + ]); + $optionValues[] = $result['id']; + } + + // Check values are as expected for each domain + foreach ($domainIDs as $domainID) { + \CRM_Core_Config::domainID($domainID); + $result = CRM_Core_OptionGroup::values('from_email_address'); + $this->assertEquals(array_keys($result)[0], 'test' . $domainID); + } + + // Clean up + \CRM_Core_Config::domainID($original_domain); + foreach ($optionValues as $id) { + $this->callAPISuccess('option_value', 'delete', ['id' => $id]); + } + // @todo There is no domain delete API + foreach ($domainIDs as $domainID) { + CRM_Core_DAO::executeQuery('DELETE FROM civicrm_domain where id = %1', [1 => [$domainID, 'Int']]); + } + unset($original_domain, $domainIDs, $optionValues); + + } + } From aedfc3ed14e5a999a2c7c284a3478229841e9342 Mon Sep 17 00:00:00 2001 From: Matthew Wire Date: Wed, 22 Apr 2020 10:48:36 +0100 Subject: [PATCH 019/834] Support more table names for utf8mb4 conversions and database name --- CRM/Core/BAO/SchemaHandler.php | 52 +++++++++++++++++++++++----------- api/v3/System.php | 18 +++++++++++- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/CRM/Core/BAO/SchemaHandler.php b/CRM/Core/BAO/SchemaHandler.php index 5523858d6ac9..519b152cfd5b 100644 --- a/CRM/Core/BAO/SchemaHandler.php +++ b/CRM/Core/BAO/SchemaHandler.php @@ -796,30 +796,50 @@ public static function getFieldAlterSQL($params, $indexExist) { * * @param bool $revert * Being able to revert if primarily for unit testing. + * @param array $patterns + * Defaults to ['civicrm\_%'] but can be overridden to specify any pattern. eg ['civicrm\_%', 'civi%\_%', 'veda%\_%']. + * @param array $databaseList + * Allows you to specify an alternative database to the configured CiviCRM database. * * @return bool */ - public static function migrateUtf8mb4($revert = FALSE) { + public static function migrateUtf8mb4($revert = FALSE, $patterns = ['civicrm\_%'], $databaseList = NULL) { $newCharSet = $revert ? 'utf8' : 'utf8mb4'; $newCollation = $revert ? 'utf8_unicode_ci' : 'utf8mb4_unicode_ci'; $newBinaryCollation = $revert ? 'utf8_bin' : 'utf8mb4_bin'; $tables = []; $dao = new CRM_Core_DAO(); - $database = $dao->_database; - CRM_Core_DAO::executeQuery("ALTER DATABASE $database CHARACTER SET = $newCharSet COLLATE = $newCollation"); - $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS WHERE Engine = 'InnoDB' AND Name LIKE 'civicrm\_%'"); - while ($dao->fetch()) { - $tables[$dao->Name] = [ - 'Engine' => $dao->Engine, - ]; - } - $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN); - $logging_database = $dsn['database']; - $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS FROM `$logging_database` WHERE Engine <> 'MyISAM' AND Name LIKE 'log\_civicrm\_%'"); - while ($dao->fetch()) { - $tables["$logging_database.{$dao->Name}"] = [ - 'Engine' => $dao->Engine, - ]; + $databases = $databaseList ?? [$dao->_database]; + + $tableNameLikePatterns = []; + $logTableNameLikePatterns = []; + + foreach ($patterns as $pattern) { + $pattern = CRM_Utils_Type::escape($pattern, 'String'); + $tableNameLikePatterns[] = "Name LIKE '{$pattern}'"; + $logTableNameLikePatterns[] = "Name LIKE 'log\_{$pattern}'"; + } + + foreach ($databases as $database) { + CRM_Core_DAO::executeQuery("ALTER DATABASE $database CHARACTER SET = $newCharSet COLLATE = $newCollation"); + $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS FROM `{$database}` WHERE Engine = 'InnoDB' AND (" . implode(' OR ', $tableNameLikePatterns) . ")"); + while ($dao->fetch()) { + $tables["{$database}.{$dao->Name}"] = [ + 'Engine' => $dao->Engine, + ]; + } + } + // If we specified a list of databases assume the user knows what they are doing. + // If they specify the database they should also specify the pattern. + if (!$databaseList) { + $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN); + $logging_database = $dsn['database']; + $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS FROM `{$logging_database}` WHERE Engine <> 'MyISAM' AND (" . implode(' OR ', $logTableNameLikePatterns) . ")"); + while ($dao->fetch()) { + $tables["{$logging_database}.{$dao->Name}"] = [ + 'Engine' => $dao->Engine, + ]; + } } foreach ($tables as $table => $param) { $query = "ALTER TABLE $table"; diff --git a/api/v3/System.php b/api/v3/System.php index b597c48793fc..c207ac9e13e5 100644 --- a/api/v3/System.php +++ b/api/v3/System.php @@ -397,7 +397,14 @@ function civicrm_api3_system_updatelogtables($params) { * @throws \API_Exception */ function civicrm_api3_system_utf8conversion($params) { - if (CRM_Core_BAO_SchemaHandler::migrateUtf8mb4($params['is_revert'])) { + $params['patterns'] = explode(',', $params['patterns']); + $params['databases'] = empty($params['databases']) ? NULL : explode(',', $params['databases']); + if (CRM_Core_BAO_SchemaHandler::migrateUtf8mb4( + $params['is_revert'], + $params['patterns'], + $params['databases'] + ) + ) { return civicrm_api3_create_success(1); } throw new API_Exception('Conversion failed'); @@ -414,6 +421,15 @@ function _civicrm_api3_system_utf8conversion_spec(&$params) { 'type' => CRM_Utils_Type::T_BOOLEAN, 'api.default' => FALSE, ]; + $params['patterns'] = [ + 'title' => ts('CSV list of table patterns (defaults to "civicrm\_%")'), + 'type' => CRM_Utils_Type::T_STRING, + 'api.default' => 'civicrm\_%', + ]; + $params['databases'] = [ + 'title' => ts('CSV list of database names (defaults to CiviCRM database)'), + 'type' => CRM_Utils_Type::T_STRING, + ]; } /** From aed025fbd6fee78376f2ccac45a0650d1eaef57d Mon Sep 17 00:00:00 2001 From: John Kingsnorth Date: Tue, 25 Aug 2020 11:35:57 +0100 Subject: [PATCH 020/834] dev/core#1971 Domain-specific option value caching --- CRM/Core/OptionGroup.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CRM/Core/OptionGroup.php b/CRM/Core/OptionGroup.php index 89018c1a21cf..727f2b22141b 100644 --- a/CRM/Core/OptionGroup.php +++ b/CRM/Core/OptionGroup.php @@ -106,7 +106,12 @@ public static function &values( $orderBy = 'weight' ) { $cache = CRM_Utils_Cache::singleton(); - $cacheKey = self::createCacheKey($name, $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy); + if (in_array($name, self::$_domainIDGroups)) { + $cacheKey = self::createCacheKey($name, $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy, CRM_Core_Config::domainID()); + } + else { + $cacheKey = self::createCacheKey($name, $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy); + } if (!$fresh) { // Fetch from static var @@ -186,8 +191,7 @@ protected static function flushValues($name, $flip, $grouping, $localize, $condi * @return string */ protected static function createCacheKey($id) { - $cacheKey = "CRM_OG_" . preg_replace('/[^a-zA-Z0-9]/', '', $id) . '_' . md5(serialize(func_get_args())); - return $cacheKey; + return "CRM_OG_" . preg_replace('/[^a-zA-Z0-9]/', '', $id) . '_' . md5(serialize(func_get_args())); } /** From baebe8d6ac2632357c094f3fbeecfef0786e6693 Mon Sep 17 00:00:00 2001 From: Pradeep Nayak Date: Tue, 25 Aug 2020 15:39:33 +0100 Subject: [PATCH 021/834] Fix js for case type status change on load --- templates/CRM/Case/Form/Activity/OpenCase.tpl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/templates/CRM/Case/Form/Activity/OpenCase.tpl b/templates/CRM/Case/Form/Activity/OpenCase.tpl index 4627f81de3a2..c1a955771e23 100644 --- a/templates/CRM/Case/Form/Activity/OpenCase.tpl +++ b/templates/CRM/Case/Form/Activity/OpenCase.tpl @@ -42,15 +42,17 @@ var caseStatusLabels = {/literal}{$caseStatusLabels.values|@json_encode}{literal}; var caseStatusNames = {/literal}{$caseStatusNames.values|@json_encode}{literal}; if ($('#case_type_id, #status_id', $form).length === 2) { - $('#case_type_id', $form).change(function() { - if ($(this).val()) { - var definition = caseTypes[$(this).val()].definition; + updateCaseStatusOptions(); + $('#case_type_id', $form).change(updateCaseStatusOptions); + function updateCaseStatusOptions() { + if ($('#case_type_id', $form).val()) { + var definition = caseTypes[$('#case_type_id', $form).val()].definition; var newOptions = CRM._.filter(caseStatusLabels, function(opt) { return !definition.statuses || !definition.statuses.length || definition.statuses.indexOf(caseStatusNames[opt.key]) > -1; }); CRM.utils.setOptions($('#status_id', $form), newOptions); } - }) + } } }); From b6b6cb2d4f3f386ded0a44930756deba18574e2b Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Tue, 25 Aug 2020 11:33:09 -0400 Subject: [PATCH 022/834] Expose field label to APIv4 and Search creaor --- Civi/Api4/Generic/BasicGetFieldsAction.php | 9 ++++++++ Civi/Api4/Service/Spec/FieldSpec.php | 23 ++++++++++++++++++++ Civi/Api4/Service/Spec/SpecFormatter.php | 1 + ext/search/CRM/Search/Page/Ang.php | 4 ++-- ext/search/ang/search/crmSearch.component.js | 4 ++-- 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Civi/Api4/Generic/BasicGetFieldsAction.php b/Civi/Api4/Generic/BasicGetFieldsAction.php index ec58714fe46d..5f5d25ef3ded 100644 --- a/Civi/Api4/Generic/BasicGetFieldsAction.php +++ b/Civi/Api4/Generic/BasicGetFieldsAction.php @@ -129,6 +129,7 @@ protected function formatResults(&$values) { 'data_type' => \CRM_Utils_Array::value('type', $field, 'String'), ], array_flip($fields)); $field += $defaults; + $field['label'] = $field['label'] ?? $field['title']; if (isset($defaults['options'])) { $field['options'] = $this->formatOptionList($field['options']); } @@ -217,14 +218,22 @@ public function fields() { [ 'name' => 'name', 'data_type' => 'String', + 'description' => ts('Unique field identifier'), ], [ 'name' => 'title', 'data_type' => 'String', + 'description' => ts('Technical name of field, shown in API and exports'), + ], + [ + 'name' => 'label', + 'data_type' => 'String', + 'description' => ts('User-facing label, shown on most forms and displays'), ], [ 'name' => 'description', 'data_type' => 'String', + 'description' => ts('Explanation of the purpose of the field'), ], [ 'name' => 'default_value', diff --git a/Civi/Api4/Service/Spec/FieldSpec.php b/Civi/Api4/Service/Spec/FieldSpec.php index 8b9ba5ca2405..eb35cf1d3082 100644 --- a/Civi/Api4/Service/Spec/FieldSpec.php +++ b/Civi/Api4/Service/Spec/FieldSpec.php @@ -32,6 +32,11 @@ class FieldSpec { */ protected $name; + /** + * @var string + */ + protected $label; + /** * @var string */ @@ -165,6 +170,24 @@ public function setName($name) { return $this; } + /** + * @return string + */ + public function getLabel() { + return $this->label; + } + + /** + * @param string $label + * + * @return $this + */ + public function setLabel($label) { + $this->label = $label; + + return $this; + } + /** * @return string */ diff --git a/Civi/Api4/Service/Spec/SpecFormatter.php b/Civi/Api4/Service/Spec/SpecFormatter.php index 1da26d1520a9..23209a4e242c 100644 --- a/Civi/Api4/Service/Spec/SpecFormatter.php +++ b/Civi/Api4/Service/Spec/SpecFormatter.php @@ -73,6 +73,7 @@ public static function arrayToField(array $data, $entity) { $field = new FieldSpec($name, $entity, $dataTypeName); $field->setRequired(!empty($data['required'])); $field->setTitle($data['title'] ?? NULL); + $field->setLabel($data['html']['label'] ?? NULL); $field->setOptions(!empty($data['pseudoconstant'])); } $field->setSerialize($data['serialize'] ?? NULL); diff --git a/ext/search/CRM/Search/Page/Ang.php b/ext/search/CRM/Search/Page/Ang.php index a13d8297b59c..069dc0ee973f 100644 --- a/ext/search/CRM/Search/Page/Ang.php +++ b/ext/search/CRM/Search/Page/Ang.php @@ -66,7 +66,7 @@ private function getSchema() { ->setChain([ 'get' => ['$name', 'getActions', ['where' => [['name', '=', 'get']]], ['params']], ])->execute(); - $getFields = ['name', 'title', 'description', 'options', 'input_type', 'input_attrs', 'data_type', 'serialize']; + $getFields = ['name', 'label', 'description', 'options', 'input_type', 'input_attrs', 'data_type', 'serialize']; foreach ($schema as $entity) { // Skip if entity doesn't have a 'get' action or the user doesn't have permission to use get if ($entity['get']) { @@ -78,7 +78,7 @@ private function getSchema() { $entity['fields'] = civicrm_api4($entity['name'], 'getFields', [ 'select' => $getFields, 'where' => [['permission', 'IS NULL']], - 'orderBy' => ['title'], + 'orderBy' => ['label'], 'loadOptions' => $loadOptions, ]); // Get the names of params this entity supports (minus some obvious ones) diff --git a/ext/search/ang/search/crmSearch.component.js b/ext/search/ang/search/crmSearch.component.js index 1695f95701d8..e2d8c564d15e 100644 --- a/ext/search/ang/search/crmSearch.component.js +++ b/ext/search/ang/search/crmSearch.component.js @@ -297,7 +297,7 @@ this.getFieldLabel = function(col) { var info = searchMeta.parseExpr(col), - label = info.field.title; + label = info.field.label; if (info.fn) { label = '(' + info.fn.title + ') ' + label; } @@ -393,7 +393,7 @@ return _.transform(searchMeta.getEntity(entityName).fields, function(result, field) { var item = { id: prefix + field.name + (field.options ? suffix : ''), - text: field.title, + text: field.label, description: field.description }; if (disabledIf(item.id)) { From a1d226d6655afebbb63b14032180f217ee68b762 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Tue, 25 Aug 2020 14:20:51 -0400 Subject: [PATCH 023/834] Separate titles from labels in exportable fields Follow-up to #18255 and #18114 - this separates field titles (used in export) from labels (used in forms and other displays). The previous approach was to try to use the title for both, and if that didn't work, override it somewhere. --- CRM/Activity/BAO/Activity.php | 1 - CRM/Activity/DAO/Activity.php | 8 +++++--- CRM/Contact/BAO/Contact.php | 1 - CRM/Contact/DAO/Contact.php | 5 +++-- CRM/Contribute/BAO/Contribution.php | 2 -- CRM/Contribute/DAO/Contribution.php | 5 +++-- xml/schema/Activity/Activity.xml | 6 ++++-- xml/schema/Contact/Contact.xml | 3 ++- xml/schema/Contribute/Contribution.xml | 3 ++- 9 files changed, 19 insertions(+), 15 deletions(-) diff --git a/CRM/Activity/BAO/Activity.php b/CRM/Activity/BAO/Activity.php index 3c8a0eb1551f..40f6fdc90665 100644 --- a/CRM/Activity/BAO/Activity.php +++ b/CRM/Activity/BAO/Activity.php @@ -2164,7 +2164,6 @@ public static function exportableFields($name = 'Activity') { ], ]; $fields = array_merge($activityFields, $exportableFields); - $fields['activity_type_id']['title'] = ts('Activity Type ID'); $fields['activity_priority_id'] = $fields['priority_id']; if ($name === 'Case') { diff --git a/CRM/Activity/DAO/Activity.php b/CRM/Activity/DAO/Activity.php index 3221932f12ea..03ad751cd6b7 100644 --- a/CRM/Activity/DAO/Activity.php +++ b/CRM/Activity/DAO/Activity.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Activity/Activity.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:acbed5c46fa2f00de06493210358c684) + * (GenCodeChecksum:cbcbcbb6720f015deae4097b01196c9a) */ /** @@ -288,7 +288,7 @@ public static function &fields() { 'activity_type_id' => [ 'name' => 'activity_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Activity Type'), + 'title' => ts('Activity Type ID'), 'description' => ts('FK to civicrm_option_value.id, that has to be valid, registered activity type.'), 'required' => TRUE, 'import' => TRUE, @@ -302,6 +302,7 @@ public static function &fields() { 'localizable' => 0, 'html' => [ 'type' => 'Select', + 'label' => ts("Activity Type"), ], 'pseudoconstant' => [ 'optionGroupName' => 'activity_type', @@ -391,7 +392,7 @@ public static function &fields() { 'phone_id' => [ 'name' => 'phone_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Phone (called)'), + 'title' => ts('Phone (called) ID'), 'description' => ts('Phone ID of the number called (optional - used if an existing phone number is selected).'), 'where' => 'civicrm_activity.phone_id', 'table_name' => 'civicrm_activity', @@ -401,6 +402,7 @@ public static function &fields() { 'FKClassName' => 'CRM_Core_DAO_Phone', 'html' => [ 'type' => 'EntityRef', + 'label' => ts("Phone (called)"), ], 'add' => '2.0', ], diff --git a/CRM/Contact/BAO/Contact.php b/CRM/Contact/BAO/Contact.php index 29c82bd90095..5f42dce4faa5 100644 --- a/CRM/Contact/BAO/Contact.php +++ b/CRM/Contact/BAO/Contact.php @@ -1609,7 +1609,6 @@ public static function &exportableFields($contactType = 'Individual', $status = ); } } - $fields['current_employer_id']['title'] = ts('Current Employer ID'); //fix for CRM-791 if ($export) { $fields = array_merge($fields, [ diff --git a/CRM/Contact/DAO/Contact.php b/CRM/Contact/DAO/Contact.php index 18129eecc974..f91dc3264b1e 100644 --- a/CRM/Contact/DAO/Contact.php +++ b/CRM/Contact/DAO/Contact.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/Contact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7f796d14a0de2afa9ec1d5b658ab9329) + * (GenCodeChecksum:f58884560d4f49764182cd97f1bbbcdf) */ /** @@ -1402,7 +1402,7 @@ public static function &fields() { 'current_employer_id' => [ 'name' => 'employer_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Current Employer'), + 'title' => ts('Current Employer ID'), 'description' => ts('OPTIONAL FK to civicrm_contact record.'), 'where' => 'civicrm_contact.employer_id', 'export' => TRUE, @@ -1414,6 +1414,7 @@ public static function &fields() { 'FKClassName' => 'CRM_Contact_DAO_Contact', 'html' => [ 'type' => 'EntityRef', + 'label' => ts("Current Employer"), ], 'add' => '2.1', ], diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 849daf7a4e5d..1bc7aa5b401a 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -847,8 +847,6 @@ public static function &exportableFields($checkPermission = TRUE) { CRM_Core_BAO_CustomField::getFieldsForImport('Contribution', FALSE, FALSE, FALSE, $checkPermission) ); - $fields['financial_type_id']['title'] = ts('Financial Type ID'); - self::$_exportableFields = $fields; } diff --git a/CRM/Contribute/DAO/Contribution.php b/CRM/Contribute/DAO/Contribution.php index d40a175b96d7..45f51b756cad 100644 --- a/CRM/Contribute/DAO/Contribution.php +++ b/CRM/Contribute/DAO/Contribution.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/Contribution.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:534963bc67ddc36a182ff4767f223531) + * (GenCodeChecksum:cc3bcdbce84066823084f71e30f6990b) */ /** @@ -327,7 +327,7 @@ public static function &fields() { 'financial_type_id' => [ 'name' => 'financial_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Financial Type'), + 'title' => ts('Financial Type ID'), 'description' => ts('FK to Financial Type for (total_amount - non_deductible_amount).'), 'where' => 'civicrm_contribution.financial_type_id', 'export' => TRUE, @@ -338,6 +338,7 @@ public static function &fields() { 'FKClassName' => 'CRM_Financial_DAO_FinancialType', 'html' => [ 'type' => 'Select', + 'label' => ts("Financial Type"), ], 'pseudoconstant' => [ 'table' => 'civicrm_financial_type', diff --git a/xml/schema/Activity/Activity.xml b/xml/schema/Activity/Activity.xml index 8a37ea3d7615..47fa64230103 100644 --- a/xml/schema/Activity/Activity.xml +++ b/xml/schema/Activity/Activity.xml @@ -59,7 +59,7 @@
activity_type_id - Activity Type + Activity Type ID true int unsigned true @@ -73,6 +73,7 @@ Select + @@ -191,11 +192,12 @@ phone_id int unsigned - Phone (called) + Phone (called) ID Phone ID of the number called (optional - used if an existing phone number is selected). 2.0 EntityRef + diff --git a/xml/schema/Contact/Contact.xml b/xml/schema/Contact/Contact.xml index f904c09cff1e..58456425fda7 100644 --- a/xml/schema/Contact/Contact.xml +++ b/xml/schema/Contact/Contact.xml @@ -826,7 +826,7 @@
employer_id - Current Employer + Current Employer ID current_employer_id int unsigned OPTIONAL FK to civicrm_contact record. @@ -834,6 +834,7 @@ 2.1 EntityRef + Individual diff --git a/xml/schema/Contribute/Contribution.xml b/xml/schema/Contribute/Contribution.xml index e98363baa93b..c7ed79e140cc 100644 --- a/xml/schema/Contribute/Contribution.xml +++ b/xml/schema/Contribute/Contribution.xml @@ -81,7 +81,7 @@ financial_type_id - Financial Type + Financial Type ID int unsigned FK to Financial Type for (total_amount - non_deductible_amount). @@ -92,6 +92,7 @@ true Select + 4.3 From c74976533ef772af916059fd1d8f7a2365b06ac0 Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 26 Aug 2020 08:48:12 +1200 Subject: [PATCH 024/834] Remove last pass-by-reference in completeOrder signature ids is now only referred to once in the function & never altered. I have assigned that value to a param & unset ids after that to make that clear --- CRM/Contribute/BAO/Contribution.php | 12 +++++++++--- CRM/Core/Payment/BaseIPN.php | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index a74d99bc5736..3494b5ee3d70 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -4452,9 +4452,15 @@ public static function isSingleLineItem($id) { * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public static function completeOrder($input, &$ids, $objects, $isPostPaymentCreate = FALSE) { + public static function completeOrder($input, $ids, $objects, $isPostPaymentCreate = FALSE) { $transaction = new CRM_Core_Transaction(); $contribution = $objects['contribution']; + // @todo see if we even need this - it's used further down to create an activity + // but the BAO layer should create that - we just need to add a test to cover it & can + // maybe remove $ids altogether. + $contributionContactID = $ids['related_contact'] ?? NULL; + // Unset ids just to make it clear it's not used again. + unset($ids); // The previous details are used when calculating line items so keep it before any code that 'does something' if (!empty($contribution->id)) { $input['prevContribution'] = CRM_Contribute_BAO_Contribution::getValues(['id' => $contribution->id]); @@ -4565,9 +4571,9 @@ public static function completeOrder($input, &$ids, $objects, $isPostPaymentCrea if ($input['component'] == 'contribute') { //CRM-4027 $targetContactID = NULL; - if (!empty($ids['related_contact'])) { + if ($contributionContactID) { $targetContactID = $contribution->contact_id; - $contribution->contact_id = $ids['related_contact']; + $contribution->contact_id = $contributionContactID; } CRM_Activity_BAO_Activity::addActivity($contribution, NULL, $targetContactID); } diff --git a/CRM/Core/Payment/BaseIPN.php b/CRM/Core/Payment/BaseIPN.php index 930baec01633..e4312a5fd797 100644 --- a/CRM/Core/Payment/BaseIPN.php +++ b/CRM/Core/Payment/BaseIPN.php @@ -467,7 +467,7 @@ private function cancelMembership($membership, $membershipStatusID, $onlyCancelP * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public function completeTransaction(&$input, &$ids, &$objects) { + public function completeTransaction($input, $ids, $objects) { CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects); } From 8283d4a965eceb24724016b5730ae1823da7e1ca Mon Sep 17 00:00:00 2001 From: John Kingsnorth Date: Tue, 25 Aug 2020 22:38:23 +0100 Subject: [PATCH 025/834] dev/core#1971 Fix test breaking subsequent tests when it fails --- tests/phpunit/CRM/Core/OptionGroupTest.php | 33 ++++++++++------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/tests/phpunit/CRM/Core/OptionGroupTest.php b/tests/phpunit/CRM/Core/OptionGroupTest.php index d09af84302c9..81778ef2d7a7 100644 --- a/tests/phpunit/CRM/Core/OptionGroupTest.php +++ b/tests/phpunit/CRM/Core/OptionGroupTest.php @@ -96,38 +96,36 @@ public function testDomainSpecificValueCache() { $domainIDs = []; $optionValues = []; - // Create domains - $domainIDs[] = $this->callAPISuccess('Domain', 'create', [ - 'name' => "Test extra domain 1", - 'domain_version' => CRM_Utils_System::version(), - ])['id']; - $domainIDs[] = $this->callAPISuccess('Domain', 'create', [ - 'name' => "Test extra domain 2", - 'domain_version' => CRM_Utils_System::version(), - ])['id']; - - // Create 'from' email addresses - foreach ($domainIDs as $domainID) { - $result = $this->callAPISuccess('option_value', 'create', [ + // Create domain and from_email_address + for ($i = 1; $i < 3; $i++) { + $domainID = $this->callAPISuccess('Domain', 'create', [ + 'name' => "Test extra domain " . $i, + 'domain_version' => CRM_Utils_System::version(), + ])['id']; + $optionValues[] = $this->callAPISuccess('option_value', 'create', [ 'option_group_id' => 'from_email_address', 'name' => '"Test ' . $domainID . '" ', 'label' => '"Test ' . $domainID . '" ', 'value' => 'test' . $domainID, 'is_active' => 1, 'domain_id' => $domainID, - ]); - $optionValues[] = $result['id']; + ])['id']; + $domainIDs[] = $domainID; } - // Check values are as expected for each domain + // Check expected values for each domain foreach ($domainIDs as $domainID) { \CRM_Core_Config::domainID($domainID); $result = CRM_Core_OptionGroup::values('from_email_address'); + + // Reset the domain, so we don't break future tests if this fails + \CRM_Core_Config::domainID($original_domain); + + // Assert $this->assertEquals(array_keys($result)[0], 'test' . $domainID); } // Clean up - \CRM_Core_Config::domainID($original_domain); foreach ($optionValues as $id) { $this->callAPISuccess('option_value', 'delete', ['id' => $id]); } @@ -136,7 +134,6 @@ public function testDomainSpecificValueCache() { CRM_Core_DAO::executeQuery('DELETE FROM civicrm_domain where id = %1', [1 => [$domainID, 'Int']]); } unset($original_domain, $domainIDs, $optionValues); - } } From 7da8a388e41789ea1b131b81f4f6731702aaa8e8 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Wed, 26 Aug 2020 09:23:41 -0400 Subject: [PATCH 026/834] Take the guesswork out of rendering clientside CRM variables The window.CRM object needs to be either created or updated depending on whether it already exists. Before: Guess whether it exists based on the region and mode. After: Actually check if it exists. --- CRM/Core/Resources.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/CRM/Core/Resources.php b/CRM/Core/Resources.php index 0fdc57cf21de..265112d8f994 100644 --- a/CRM/Core/Resources.php +++ b/CRM/Core/Resources.php @@ -354,14 +354,10 @@ protected function mergeSettings($settings, $additions) { * @return string */ public function renderSetting($region = NULL) { - // On a standard page request we construct the CRM object from scratch - if (($region === 'html-header') || !self::isAjaxMode()) { - $js = 'var CRM = ' . json_encode($this->getSettings()) . ';'; - } - // For an ajax request we append to it - else { - $js = 'CRM.$.extend(true, CRM, ' . json_encode($this->getSettings()) . ');'; - } + $vars = json_encode($this->getSettings(), JSON_UNESCAPED_SLASHES); + $js = "(function(vars) { + if (window.CRM) CRM.$.extend(true, CRM, vars); else window.CRM = vars; +})($vars)"; return sprintf("\n", $js); } From c4c5eb90d76d99d9e7e676802ca47a3a81f78b71 Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 26 Aug 2020 08:00:01 +1200 Subject: [PATCH 027/834] Remove 1 or 2 remaining places where relatedObjects is called in completeOrder If we remove this & the other place we no longer need to call loadRelatedObjects before this function and only message related places call it In this case the lines can go as 1) trxn_id should always be used if provided. Casting to isoDate has not been required for a few years as the DAO now handles --- CRM/Contribute/BAO/Contribution.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index a74d99bc5736..989090a8acb9 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -4545,12 +4545,6 @@ public static function completeOrder($input, &$ids, $objects, $isPostPaymentCrea CRM_Contribute_BAO_ContributionRecur::addrecurSoftCredit($objects['contributionRecur']->id, $contribution->id); } - if (empty($contribution->_relatedObjects['participant']) && !empty($contribution->_relatedObjects['membership'])) { - // @fixme Can we remove this if altogether? - we removed the participant if / else and left relatedObjects['participant'] to ensure behaviour didn't change but it is probably not required. - // @todo - use getRelatedMemberships instead - $contribution->trxn_id = $input['trxn_id'] ?? NULL; - $contribution->receive_date = CRM_Utils_Date::isoToMysql($contribution->receive_date); - } $contribution->contribution_status_id = $contributionParams['contribution_status_id']; CRM_Core_Error::debug_log_message('Contribution record updated successfully'); From 696737b5b83f5986edf19b7208e43cde253f2bdb Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 24 Aug 2020 15:22:19 +1200 Subject: [PATCH 028/834] Fix for ongoing issues with static upsetting the apple cart This is an alternate to https://github.com/civicrm/civicrm-core/pull/18218 I managed to replicate the bug in https://issues.civicrm.org/jira/browse/CRM-4213 that the static was added to cover. This takes an alternate approach of loading the memberships & checking them rather than a static. I think it might mean more queries but they shouldn't be expensive queries & the code is just too hard-going to skip straight from hacked but still intermittantly buggy to also having no additional queries --- CRM/Contribute/BAO/Contribution.php | 5 -- CRM/Member/BAO/Membership.php | 81 ++++++++++++++----- .../CRM/Member/Form/MembershipTest.php | 4 +- tests/phpunit/api/v3/JobTest.php | 5 -- tests/phpunit/api/v3/MembershipTest.php | 74 ++++++++++++++++- 5 files changed, 137 insertions(+), 32 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index a74d99bc5736..9b68859709a2 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -5261,11 +5261,6 @@ public static function updateMembershipBasedOnCompletionOfContribution($contribu //so make status override false. $membershipParams['is_override'] = FALSE; $membershipParams['status_override_end_date'] = 'null'; - - //CRM-17723 - reset static $relatedContactIds array() - // @todo move it to Civi Statics. - $var = TRUE; - CRM_Member_BAO_Membership::createRelatedMemberships($var, $var, TRUE); civicrm_api3('Membership', 'create', $membershipParams); } } diff --git a/CRM/Member/BAO/Membership.php b/CRM/Member/BAO/Membership.php index 1030eaae1c9b..3ccda33cf0a3 100644 --- a/CRM/Member/BAO/Membership.php +++ b/CRM/Member/BAO/Membership.php @@ -1316,25 +1316,10 @@ public static function sortName($id) { * @param CRM_Core_DAO $dao * Membership object. * - * @param bool $reset - * - * @return array|null - * Membership details, if created. - * * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public static function createRelatedMemberships($params, $dao, $reset = FALSE) { - // CRM-4213 check for loops, using static variable to record contacts already processed. - if (!isset(\Civi::$statics[__CLASS__]['related_contacts'])) { - \Civi::$statics[__CLASS__]['related_contacts'] = []; - } - if ($reset) { - // CRM-17723. - unset(\Civi::$statics[__CLASS__]['related_contacts']); - return FALSE; - } - $relatedContactIds = &\Civi::$statics[__CLASS__]['related_contacts']; + public static function createRelatedMemberships($params, $dao) { $membership = new CRM_Member_DAO_Membership(); $membership->id = $dao->id; @@ -1371,9 +1356,9 @@ public static function createRelatedMemberships($params, $dao, $reset = FALSE) { ); // CRM-4213, CRM-19735 check for loops, using static variable to record contacts already processed. - // Remove repeated related contacts, which already inherited membership of this type. - $relatedContactIds[$membership->contact_id][$membership->membership_type_id] = TRUE; + // Remove repeated related contacts, which already inherited membership of this type$relatedContactIds[$membership->contact_id][$membership->membership_type_id] = TRUE; foreach ($allRelatedContacts as $cid => $status) { + // relatedContactIDs is always empty now - will remove next roud because of whitespace readability. if (empty($relatedContactIds[$cid]) || empty($relatedContactIds[$cid][$membership->membership_type_id])) { $relatedContactIds[$cid][$membership->membership_type_id] = TRUE; @@ -1470,7 +1455,9 @@ public static function createRelatedMemberships($params, $dao, $reset = FALSE) { $ids = []; if (($params['status_id'] == $deceasedStatusId) || ($params['status_id'] == $expiredStatusId)) { // related membership is not active so does not count towards maximum - CRM_Member_BAO_Membership::create($params); + if (!self::hasExistingInheritedMembership($params)) { + CRM_Member_BAO_Membership::create($params); + } } else { // related membership already exists, so this is just an update @@ -1495,7 +1482,9 @@ public static function createRelatedMemberships($params, $dao, $reset = FALSE) { if ($numRelatedAvailable <= 0) { break; } - CRM_Member_BAO_Membership::create($params); + if (!self::hasExistingInheritedMembership($params)) { + CRM_Member_BAO_Membership::create($params); + } $numRelatedAvailable--; } } @@ -2092,6 +2081,58 @@ protected static function updateDeceasedMembersStatuses() { return $count; } + /** + * Does the existing membership match the required membership. + * + * Check before updating that the params are not a match - this is part of avoiding + * a loop if we have already updated. + * + * https://issues.civicrm.org/jira/browse/CRM-4213 + * @param array $params + * + * @param array $membership + * + * @return bool + */ + protected static function matchesRequiredMembership($params, $membership) { + foreach (['start_date', 'end_date'] as $date) { + if (strtotime($params[$date]) !== strtotime($membership[$date])) { + return FALSE; + } + if ((int) $params['status_id'] !== (int) $membership['status_id']) { + return FALSE; + } + if ((int) $params['membership_type_id'] !== (int) $membership['membership_type_id']) { + return FALSE; + } + } + return TRUE; + } + + /** + * Params of new membership. + * + * @param array $params + * + * @return bool + * @throws \CiviCRM_API3_Exception + */ + protected static function hasExistingInheritedMembership($params) { + foreach (civicrm_api3('Membership', 'get', ['contact_id' => $params['contact_id']])['values'] as $membership) { + if (!empty($membership['owner_membership_id']) + && $membership['membership_type_id'] === $params['membership_type_id'] + && (int) $params['owner_membership_id'] !== (int) $membership['owner_membership_id'] + ) { + // Inheriting it from another contact, don't update here. + return TRUE; + } + if (self::matchesRequiredMembership($params, $membership)) { + return TRUE; + } + } + return FALSE; + } + /** * Process price set and line items. * diff --git a/tests/phpunit/CRM/Member/Form/MembershipTest.php b/tests/phpunit/CRM/Member/Form/MembershipTest.php index ef0d0e99bda8..79730e5b9210 100644 --- a/tests/phpunit/CRM/Member/Form/MembershipTest.php +++ b/tests/phpunit/CRM/Member/Form/MembershipTest.php @@ -1023,6 +1023,7 @@ public function testFormWithFailedContribution() { * Uses some data from tests/phpunit/CRM/Member/Form/dataset/data.xml . * * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ public function testTwoInheritedMembershipsViaPriceSetInBackend() { // Create an organization and give it a "Member of" relationship to $this->_individualId. @@ -1045,7 +1046,7 @@ public function testTwoInheritedMembershipsViaPriceSetInBackend() { $primaryMembershipIds = []; foreach ($orgMembershipResult['values'] as $membership) { $primaryMembershipIds[] = $membership['id']; - $this->assertTrue(empty($membership['owner_membership_id']), "Membership on the organization has owner_membership_id so is inherited."); + $this->assertTrue(empty($membership['owner_membership_id']), 'Membership on the organization has owner_membership_id so is inherited.'); } // CRM-20955: check that correct inherited memberships were created for the individual, @@ -1100,6 +1101,7 @@ public function testTwoInheritedMembershipsViaPriceSetInBackend() { * checking that the line items have correct amounts. * * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ public function testTwoMembershipsViaPriceSetInBackendWithDiscount() { // Register buildAmount hook to apply discount. diff --git a/tests/phpunit/api/v3/JobTest.php b/tests/phpunit/api/v3/JobTest.php index f042fa9585ec..6e30be943c0c 100644 --- a/tests/phpunit/api/v3/JobTest.php +++ b/tests/phpunit/api/v3/JobTest.php @@ -1990,7 +1990,6 @@ public function testProcessMembershipNoDeceasedStatus() { * and left alone when it shouldn't. * * @throws \CRM_Core_Exception - * @throws \CiviCRM_API3_Exception */ public function testProcessMembershipUpdateStatus() { $this->ids['MembershipType'] = $this->membershipTypeCreate(); @@ -2107,10 +2106,6 @@ public function testProcessMembershipUpdateStatus() { $this->assertEquals($organizationMembershipID, $expiredInheritedRelationship['owner_membership_id']); $this->assertMembershipStatus('Grace', (int) $expiredInheritedRelationship['status_id']); - // Reset static $relatedContactIds array in createRelatedMemberships(), - // to avoid bug where inherited membership gets deleted. - $var = TRUE; - CRM_Member_BAO_Membership::createRelatedMemberships($var, $var, TRUE); // Check that after running process_membership job, statuses are correct. $this->callAPISuccess('Job', 'process_membership', []); diff --git a/tests/phpunit/api/v3/MembershipTest.php b/tests/phpunit/api/v3/MembershipTest.php index 7e03eee9a3e5..8f6171d49c7d 100644 --- a/tests/phpunit/api/v3/MembershipTest.php +++ b/tests/phpunit/api/v3/MembershipTest.php @@ -9,6 +9,10 @@ +--------------------------------------------------------------------+ */ +use Civi\Api4\MembershipType; +use Civi\Api4\Relationship; +use Civi\Api4\RelationshipType; + /** * Test APIv3 civicrm_membership functions * @@ -495,7 +499,6 @@ public function testGetWithRelationship() { * and max_related property for Membership_Type and Membership entities * * @throws \CRM_Core_Exception - * @throws \CiviCRM_API3_Exception */ public function testCreateWithRelationship() { // Create membership type: inherited through employment, max_related = 2 @@ -667,6 +670,75 @@ public function testCreateWithRelationship() { $this->contactDelete($membershipOrgId); } + /** + * Test that loops are not created when adding spouse relationships. + * + * This add a test for https://issues.civicrm.org/jira/browse/CRM-4213 in the hope of removing + * the buggy fix for that without a resurgence. + * + * @throws \API_Exception + * @throws \CRM_Core_Exception + * @throws \Civi\API\Exception\UnauthorizedException + */ + public function testCreateWithSpouseRelationship() { + $relationshipTypeID = RelationshipType::get()->addSelect('id')->addWhere('name_a_b', '=', 'Spouse of')->execute()->first()['id']; + MembershipType::update()->setValues([ + 'relationship_direction' => ['b_a', 'a_b'], + 'relationship_type_id' => [$relationshipTypeID, $relationshipTypeID], + ]) + ->addWhere('name', '=', 'General') + ->execute()->first()['id']; + + $spouse1ID = $this->individualCreate(['first_name' => 'him']); + $spouse2ID = $this->individualCreate(['first_name' => 'her']); + $spouse3ID = $this->individualCreate(['first_name' => 'they']); + $spouse4ID = $this->individualCreate(['first_name' => 'them']); + Relationship::create()->setValues([ + 'contact_id_a' => $spouse1ID, + 'contact_id_b' => $spouse2ID, + 'relationship_type_id' => $relationshipTypeID, + ])->execute(); + + $this->contactMembershipCreate([ + 'contact_id' => $spouse1ID, + 'start_date' => date('Y-m-d'), + 'end_date' => '+1 year', + ]); + + $this->callAPISuccessGetSingle('Membership', [ + 'contact_id' => $spouse2ID, + 'membership_type_id' => 'General', + ]); + + $this->callAPISuccessGetSingle('Membership', [ + 'contact_id' => $spouse1ID, + 'membership_type_id' => 'General', + ]); + // Add another Spouse + Relationship::create()->setValues([ + 'contact_id_a' => $spouse3ID, + 'contact_id_b' => $spouse1ID, + 'relationship_type_id' => $relationshipTypeID, + ])->execute(); + $this->callAPISuccessGetSingle('Membership', [ + 'contact_id' => $spouse3ID, + 'membership_type_id' => 'General', + ]); + $this->callAPISuccessGetCount('Membership', [], 3); + Relationship::create()->setValues([ + 'contact_id_a' => $spouse1ID, + 'contact_id_b' => $spouse4ID, + 'relationship_type_id' => $relationshipTypeID, + ])->execute(); + + $this->callAPISuccessGetSingle('Membership', [ + 'contact_id' => $spouse4ID, + 'membership_type_id' => 'General', + ]); + + $this->callAPISuccessGetCount('Membership', [], 4); + } + /** * We are checking for no e-notices + only id & end_date returned * From ac6b9668d8bcb60e58896d33960fc0876f017629 Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 27 Aug 2020 10:05:04 +1200 Subject: [PATCH 029/834] Use already detemined value for contributionRecurid --- CRM/Contribute/BAO/Contribution.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index d69c94fbac02..0e8ba3d61c1a 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -4545,8 +4545,8 @@ public static function completeOrder($input, $ids, $objects, $isPostPaymentCreat } // Add new soft credit against current $contribution. - if (!empty($objects['contributionRecur']) && $objects['contributionRecur']->id) { - CRM_Contribute_BAO_ContributionRecur::addrecurSoftCredit($objects['contributionRecur']->id, $contribution->id); + if ($recurringContributionID) { + CRM_Contribute_BAO_ContributionRecur::addrecurSoftCredit($recurringContributionID, $contribution->id); } $contribution->contribution_status_id = $contributionParams['contribution_status_id']; From d0b559aa6aee52306c44645605f5b7424822beeb Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Thu, 27 Aug 2020 11:56:25 +0530 Subject: [PATCH 030/834] dev/core#912 update payment instrument of main contribution record --- CRM/Financial/BAO/Payment.php | 1 + api/v3/Contribution.php | 3 ++ tests/phpunit/api/v3/ContributionTest.php | 50 +++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/CRM/Financial/BAO/Payment.php b/CRM/Financial/BAO/Payment.php index f841363f141f..e13837e996c0 100644 --- a/CRM/Financial/BAO/Payment.php +++ b/CRM/Financial/BAO/Payment.php @@ -157,6 +157,7 @@ public static function create($params) { 'is_post_payment_create' => TRUE, 'is_email_receipt' => $params['is_send_contribution_notification'], 'trxn_date' => $params['trxn_date'], + 'payment_instrument_id' => $paymentTrxnParams['payment_instrument_id'], ]); // Get the trxn $trxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC'); diff --git a/api/v3/Contribution.php b/api/v3/Contribution.php index a1b06305e959..7a5e94dcb57c 100644 --- a/api/v3/Contribution.php +++ b/api/v3/Contribution.php @@ -678,6 +678,9 @@ function _ipn_process_transaction(&$params, $contribution, $input, $ids, $firstC } $input['card_type_id'] = $params['card_type_id'] ?? NULL; $input['pan_truncation'] = $params['pan_truncation'] ?? NULL; + if (!empty($params['payment_instrument_id'])) { + $input['payment_instrument_id'] = $params['payment_instrument_id']; + } return CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects, $params['is_post_payment_create'] ?? NULL); } diff --git a/tests/phpunit/api/v3/ContributionTest.php b/tests/phpunit/api/v3/ContributionTest.php index 8de1a75b8960..8b673eb9e370 100644 --- a/tests/phpunit/api/v3/ContributionTest.php +++ b/tests/phpunit/api/v3/ContributionTest.php @@ -4899,4 +4899,54 @@ public function testPaymentDontChangeReceiveDate() { $this->assertEquals('2020-02-02 00:00:00', $contribution['receive_date']); } + /** + * Make sure that recording a payment with Different Payment Instrument update main contribution record payment + * instrument too. If multiple Payment Recorded, last payment record payment (when No more due) instrument set to main + * payment + */ + public function testPaymentVerifyPaymentInstrumentChange() { + // Create Pending contribution with pay later mode, with payment instrument Check + $params = [ + 'contact_id' => $this->_individualId, + 'total_amount' => 100, + 'receive_date' => '2020-02-02', + 'contribution_status_id' => 'Pending', + 'is_pay_later' => 1, + 'payment_instrument_id' => 'Check', + ]; + $contributionID = $this->contributionCreate($params); + + + // Record the the Payment with instrument other than Check, e.g EFT + $paymentParams = [ + 'contribution_id' => $contributionID, + 'total_amount' => 50, + 'trxn_date' => '2020-03-04', + 'payment_instrument_id' => 'EFT' + ]; + $this->callAPISuccess('payment', 'create', $paymentParams); + + $contribution = $this->callAPISuccess('Contribution', 'getSingle', [ + 'id' => $contributionID, + ]); + // payment status should be 'Partially paid' + $this->assertEquals('Partially paid', $contribution['contribution_status']); + + // Record the the Payment with instrument other than Check, e.g Cash (pay all remaining amount) + $paymentParams = [ + 'contribution_id' => $contributionID, + 'total_amount' => 50, + 'trxn_date' => '2020-03-04', + 'payment_instrument_id' => 'Cash' + ]; + $this->callAPISuccess('payment', 'create', $paymentParams); + + //check if contribution Payment Instrument (Payment Method) is is set to "Cash". + $contribution = $this->callAPISuccess('Contribution', 'getSingle', [ + 'id' => $contributionID, + ]); + $this->assertEquals('Cash', $contribution['payment_instrument']); + $this->assertEquals('Completed', $contribution['contribution_status']); + } + } From f7299b67c3991d53b4c4366f54beb07693ae4961 Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Thu, 27 Aug 2020 11:59:54 +0530 Subject: [PATCH 031/834] Remove additional line --- tests/phpunit/api/v3/ContributionTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/api/v3/ContributionTest.php b/tests/phpunit/api/v3/ContributionTest.php index 8b673eb9e370..b865f99e8382 100644 --- a/tests/phpunit/api/v3/ContributionTest.php +++ b/tests/phpunit/api/v3/ContributionTest.php @@ -4916,7 +4916,6 @@ public function testPaymentVerifyPaymentInstrumentChange() { ]; $contributionID = $this->contributionCreate($params); - // Record the the Payment with instrument other than Check, e.g EFT $paymentParams = [ 'contribution_id' => $contributionID, From d0d6dd4087746816aa2eed2a120c33877770b9da Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Thu, 27 Aug 2020 12:27:51 +0530 Subject: [PATCH 032/834] fix test case warning --- tests/phpunit/api/v3/ContributionTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/api/v3/ContributionTest.php b/tests/phpunit/api/v3/ContributionTest.php index b865f99e8382..bf24317b8868 100644 --- a/tests/phpunit/api/v3/ContributionTest.php +++ b/tests/phpunit/api/v3/ContributionTest.php @@ -4921,7 +4921,7 @@ public function testPaymentVerifyPaymentInstrumentChange() { 'contribution_id' => $contributionID, 'total_amount' => 50, 'trxn_date' => '2020-03-04', - 'payment_instrument_id' => 'EFT' + 'payment_instrument_id' => 'EFT', ]; $this->callAPISuccess('payment', 'create', $paymentParams); @@ -4936,7 +4936,7 @@ public function testPaymentVerifyPaymentInstrumentChange() { 'contribution_id' => $contributionID, 'total_amount' => 50, 'trxn_date' => '2020-03-04', - 'payment_instrument_id' => 'Cash' + 'payment_instrument_id' => 'Cash', ]; $this->callAPISuccess('payment', 'create', $paymentParams); From fc2b5ff83b4bc6f19b102d2be0504db51161870f Mon Sep 17 00:00:00 2001 From: demeritcowboy Date: Wed, 26 Aug 2020 17:26:54 -0400 Subject: [PATCH 033/834] autodetect ssl --- setup/plugins/init/Backdrop.civi-setup.php | 2 + setup/plugins/init/Drupal.civi-setup.php | 2 + setup/plugins/init/Drupal8.civi-setup.php | 2 + .../InstallSettingsFile.civi-setup.php | 7 + setup/src/Setup/DrupalUtil.php | 54 +++++++ .../CRM/common/civicrm.settings.php.template | 2 +- tests/phpunit/Civi/Setup/DrupalUtilTest.php | 133 ++++++++++++++++++ 7 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 tests/phpunit/Civi/Setup/DrupalUtilTest.php diff --git a/setup/plugins/init/Backdrop.civi-setup.php b/setup/plugins/init/Backdrop.civi-setup.php index 57e867f90c36..2320b88dace6 100644 --- a/setup/plugins/init/Backdrop.civi-setup.php +++ b/setup/plugins/init/Backdrop.civi-setup.php @@ -39,11 +39,13 @@ // Compute DSN. global $databases; + $ssl_params = \Civi\Setup\DrupalUtil::guessSslParams($databases['default']['default']); $model->db = $model->cmsDb = array( 'server' => \Civi\Setup\DbUtil::encodeHostPort($databases['default']['default']['host'], $databases['default']['default']['port'] ?: NULL), 'username' => $databases['default']['default']['username'], 'password' => $databases['default']['default']['password'], 'database' => $databases['default']['default']['database'], + 'ssl_params' => empty($ssl_params) ? NULL : $ssl_params, ); // Compute URLs diff --git a/setup/plugins/init/Drupal.civi-setup.php b/setup/plugins/init/Drupal.civi-setup.php index b24d7c164cba..d57ff432c21c 100644 --- a/setup/plugins/init/Drupal.civi-setup.php +++ b/setup/plugins/init/Drupal.civi-setup.php @@ -37,11 +37,13 @@ // Compute DSN. global $databases; + $ssl_params = \Civi\Setup\DrupalUtil::guessSslParams($databases['default']['default']); $model->db = $model->cmsDb = array( 'server' => \Civi\Setup\DbUtil::encodeHostPort($databases['default']['default']['host'], $databases['default']['default']['port'] ?: NULL), 'username' => $databases['default']['default']['username'], 'password' => $databases['default']['default']['password'], 'database' => $databases['default']['default']['database'], + 'ssl_params' => empty($ssl_params) ? NULL : $ssl_params, ); // Compute cmsBaseUrl. diff --git a/setup/plugins/init/Drupal8.civi-setup.php b/setup/plugins/init/Drupal8.civi-setup.php index a693cba92c4c..22034e908954 100644 --- a/setup/plugins/init/Drupal8.civi-setup.php +++ b/setup/plugins/init/Drupal8.civi-setup.php @@ -40,11 +40,13 @@ // Compute DSN. $connectionOptions = \Drupal::database()->getConnectionOptions(); + $ssl_params = \Civi\Setup\DrupalUtil::guessSslParams($connectionOptions); $model->db = $model->cmsDb = array( 'server' => \Civi\Setup\DbUtil::encodeHostPort($connectionOptions['host'], $connectionOptions['port'] ?: NULL), 'username' => $connectionOptions['username'], 'password' => $connectionOptions['password'], 'database' => $connectionOptions['database'], + 'ssl_params' => empty($ssl_params) ? NULL : $ssl_params, ); // Compute cmsBaseUrl. diff --git a/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php b/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php index 29aedb2b073b..5422292f32a5 100644 --- a/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php +++ b/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php @@ -71,6 +71,13 @@ $params['CMSdbPass'] = addslashes($m->cmsDb['password']); $params['CMSdbHost'] = addslashes($m->cmsDb['server']); $params['CMSdbName'] = addslashes($m->cmsDb['database']); + // The '&' prefix is awkward, but we don't know what's already in the file. + // At the time of writing, it has ?new_link=true. If that is removed, + // then need to update this. + // The PHP_QUERY_RFC3986 is important because PEAR::DB will interpret plus + // signs as a reference to its old DSN format and mangle the DSN, so we + // need to use %20 for spaces. + $params['CMSdbSSL'] = empty($m->cmsDb['ssl_params']) ? '' : addslashes('&' . http_build_query($m->cmsDb['ssl_params'], '', '&', PHP_QUERY_RFC3986)); $params['siteKey'] = addslashes($m->siteKey); $extraSettings = array(); diff --git a/setup/src/Setup/DrupalUtil.php b/setup/src/Setup/DrupalUtil.php index 0f5ab95205b0..8d1e95bc2e72 100644 --- a/setup/src/Setup/DrupalUtil.php +++ b/setup/src/Setup/DrupalUtil.php @@ -71,4 +71,58 @@ public static function getDrupalSiteDir($cmsPath) { */ } + /** + * Guess if the CMS is using SSL for MySQL and what the corresponding + * parameters should be for PEAR::DB. + * + * Not all combinations will work. See the install docs for a list of known + * configurations that do. We don't enforce that here since we're just + * trying to guess a default based on what they already have. + * + * @param array $cmsDatabaseParams + * The contents of the section from drupal's settings.php where it defines + * the $database array, usually under 'default'. + * @return array + * The corresponding guessed params for PEAR::DB. + */ + public static function guessSslParams(array $cmsDatabaseParams):array { + // If the pdo-mysql extension isn't loaded or they have nothing in drupal + // config for pdo, then we're done. PDO isn't required for Civi, but note + // the references to PDO constants below would fail and they obviously + // wouldn't have them in drupal config then. + if (empty($cmsDatabaseParams['pdo']) || !extension_loaded('pdo_mysql')) { + return []; + } + + $pdo = $cmsDatabaseParams['pdo']; + + $pdo_map = [ + \PDO::MYSQL_ATTR_SSL_CA => 'ca', + \PDO::MYSQL_ATTR_SSL_KEY => 'key', + \PDO::MYSQL_ATTR_SSL_CERT => 'cert', + \PDO::MYSQL_ATTR_SSL_CAPATH => 'capath', + \PDO::MYSQL_ATTR_SSL_CIPHER => 'cipher', + ]; + + $ssl_params = []; + + // If they have one set in drupal config and it's a string, then copy + // it over verbatim. + foreach ($pdo_map as $pdo_name => $ssl_name) { + if (!empty($pdo[$pdo_name]) && is_string($pdo[$pdo_name])) { + $ssl_params[$ssl_name] = $pdo[$pdo_name]; + } + } + + // No client certificate or server verification, but want SSL. Return our + // made-up indicator ssl=1 that isn't a real mysqli option but which we + // recognize. It's possible they have other params set too which we pass + // along from above, but that may not be compatible but it's up to them. + if (($pdo[\PDO::MYSQL_ATTR_SSL_CA] ?? NULL) === TRUE && ($pdo[\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] ?? NULL) === FALSE) { + $ssl_params['ssl'] = 1; + } + + return $ssl_params; + } + } diff --git a/templates/CRM/common/civicrm.settings.php.template b/templates/CRM/common/civicrm.settings.php.template index 3d0575569fb8..d66a63d632ec 100644 --- a/templates/CRM/common/civicrm.settings.php.template +++ b/templates/CRM/common/civicrm.settings.php.template @@ -73,7 +73,7 @@ if (!defined('CIVICRM_UF')) { * define( 'CIVICRM_UF_DSN', 'mysql://cms_db_username:cms_db_password@db_server/cms_database?new_link=true'); */ if (!defined('CIVICRM_UF_DSN') && CIVICRM_UF !== 'UnitTests') { - define( 'CIVICRM_UF_DSN' , 'mysql://%%CMSdbUser%%:%%CMSdbPass%%@%%CMSdbHost%%/%%CMSdbName%%?new_link=true'); + define( 'CIVICRM_UF_DSN' , 'mysql://%%CMSdbUser%%:%%CMSdbPass%%@%%CMSdbHost%%/%%CMSdbName%%?new_link=true%%CMSdbSSL%%'); } // %%extraSettings%% diff --git a/tests/phpunit/Civi/Setup/DrupalUtilTest.php b/tests/phpunit/Civi/Setup/DrupalUtilTest.php new file mode 100644 index 000000000000..adf606b04fc0 --- /dev/null +++ b/tests/phpunit/Civi/Setup/DrupalUtilTest.php @@ -0,0 +1,133 @@ +assertSame($expected, \Civi\Setup\DrupalUtil::guessSslParams($input)); + } + + /** + * Data provider for testGuessSslParams + * @return array + */ + public function pdoParamsProvider():array { + return [ + 'empty' => [[], []], + 'empty2' => [['pdo' => []], []], + 'no client certificate, no server verification' => [ + [ + 'pdo' => [ + \PDO::MYSQL_ATTR_SSL_CA => TRUE, + \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => FALSE, + ], + ], + ['ssl' => 1], + ], + 'typical client certificate with server verification' => [ + [ + 'pdo' => [ + \PDO::MYSQL_ATTR_SSL_CA => '/tmp/cacert.crt', + \PDO::MYSQL_ATTR_SSL_KEY => '/tmp/my.key', + \PDO::MYSQL_ATTR_SSL_CERT => '/tmp/cert.crt', + ], + ], + [ + 'ca' => '/tmp/cacert.crt', + 'key' => '/tmp/my.key', + 'cert' => '/tmp/cert.crt', + ], + ], + 'client certificate, no server verification' => [ + [ + 'pdo' => [ + \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => FALSE, + \PDO::MYSQL_ATTR_SSL_KEY => '/tmp/my.key', + \PDO::MYSQL_ATTR_SSL_CERT => '/tmp/cert.crt', + ], + ], + [ + 'key' => '/tmp/my.key', + 'cert' => '/tmp/cert.crt', + ], + ], + 'self-signed client certificate with server verification' => [ + [ + 'pdo' => [ + \PDO::MYSQL_ATTR_SSL_CA => '/tmp/cert.crt', + \PDO::MYSQL_ATTR_SSL_KEY => '/tmp/my.key', + \PDO::MYSQL_ATTR_SSL_CERT => '/tmp/cert.crt', + ], + ], + [ + 'ca' => '/tmp/cert.crt', + 'key' => '/tmp/my.key', + 'cert' => '/tmp/cert.crt', + ], + ], + 'Not sure what would happen in practice but is all the string params' => [ + [ + 'pdo' => [ + \PDO::MYSQL_ATTR_SSL_CA => '/tmp/cacert.crt', + \PDO::MYSQL_ATTR_SSL_KEY => '/tmp/my.key', + \PDO::MYSQL_ATTR_SSL_CERT => '/tmp/cert.crt', + \PDO::MYSQL_ATTR_SSL_CAPATH => '/tmp/cacert_folder', + \PDO::MYSQL_ATTR_SSL_CIPHER => 'aes', + ], + ], + [ + 'ca' => '/tmp/cacert.crt', + 'key' => '/tmp/my.key', + 'cert' => '/tmp/cert.crt', + 'capath' => '/tmp/cacert_folder', + 'cipher' => 'aes', + ], + ], + 'Ditto, but showing extra ones should get ignored' => [ + [ + 'pdo' => [ + \PDO::MYSQL_ATTR_SSL_CA => '/tmp/cacert.crt', + \PDO::MYSQL_ATTR_SSL_KEY => '/tmp/my.key', + \PDO::MYSQL_ATTR_SSL_CERT => '/tmp/cert.crt', + \PDO::MYSQL_ATTR_SSL_CAPATH => '/tmp/cacert_folder', + \PDO::MYSQL_ATTR_SSL_CIPHER => 'aes', + 'fourteen' => 'the number fourteen', + ], + ], + [ + 'ca' => '/tmp/cacert.crt', + 'key' => '/tmp/my.key', + 'cert' => '/tmp/cert.crt', + 'capath' => '/tmp/cacert_folder', + 'cipher' => 'aes', + ], + ], + "some windows paths shouldn't get mangled" => [ + [ + 'pdo' => [ + \PDO::MYSQL_ATTR_SSL_CA => 'C:/Program Files/MariaDB 10.3/data/cacert.crt', + \PDO::MYSQL_ATTR_SSL_KEY => 'C:/Program Files/MariaDB 10.3/data/my.key', + \PDO::MYSQL_ATTR_SSL_CERT => 'C:\\Program Files\\MariaDB 10.3\\data\\cert.crt', + ], + ], + [ + 'ca' => 'C:/Program Files/MariaDB 10.3/data/cacert.crt', + 'key' => 'C:/Program Files/MariaDB 10.3/data/my.key', + 'cert' => 'C:\\Program Files\\MariaDB 10.3\\data\\cert.crt', + ], + ], + ]; + } + +} From 7adea59a09e5f8ca4f20619ad506b734855e3f74 Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Thu, 27 Aug 2020 22:44:31 +0530 Subject: [PATCH 034/834] report#47 Report Bookkeeping add time field for date filter. --- CRM/Report/Form/Contribute/Bookkeeping.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CRM/Report/Form/Contribute/Bookkeeping.php b/CRM/Report/Form/Contribute/Bookkeeping.php index f70c7d29d190..70c7a4d857ce 100644 --- a/CRM/Report/Form/Contribute/Bookkeeping.php +++ b/CRM/Report/Form/Contribute/Bookkeeping.php @@ -300,8 +300,7 @@ public function __construct() { 'operatorType' => CRM_Report_Form::OP_INT, 'type' => CRM_Utils_Type::T_INT, ], - 'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATE], - 'receipt_date' => ['operatorType' => CRM_Report_Form::OP_DATE], + 'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATETIME], 'contribution_source' => [ 'title' => ts('Source'), 'name' => 'source', @@ -317,6 +316,7 @@ public function __construct() { 'order_bys' => [ 'contribution_id' => ['title' => ts('Contribution #')], 'contribution_status_id' => ['title' => ts('Contribution Status')], + 'receive_date' => ['title' => ts('Date Received')], ], 'grouping' => 'contri-fields', ], @@ -364,7 +364,7 @@ public function __construct() { ], 'trxn_date' => [ 'title' => ts('Transaction Date'), - 'operatorType' => CRM_Report_Form::OP_DATE, + 'operatorType' => CRM_Report_Form::OP_DATETIME, 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, ], 'status_id' => [ @@ -383,6 +383,7 @@ public function __construct() { ], 'order_bys' => [ 'payment_instrument_id' => ['title' => ts('Payment Method')], + 'trxn_date' => ['title' => ts('Transaction Date')], ], ], 'civicrm_entity_financial_trxn' => [ @@ -536,8 +537,10 @@ public function where() { $relative = $this->_params["{$fieldName}_relative"] ?? NULL; $from = $this->_params["{$fieldName}_from"] ?? NULL; $to = $this->_params["{$fieldName}_to"] ?? NULL; + $fromTime = $this->_params["{$fieldName}_from_time"] ?? NULL; + $toTime = $this->_params["{$fieldName}_to_time"] ?? NULL; - $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type']); + $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type'], $fromTime, $toTime); } else { $op = $this->_params["{$fieldName}_op"] ?? NULL; From e7339d592be523cb2a591bc5d0a413b6f1b9836b Mon Sep 17 00:00:00 2001 From: eileen Date: Fri, 28 Aug 2020 10:49:32 +1200 Subject: [PATCH 035/834] Shell Financial ACLs extension This is intended as a hidden extension while we work through the process of migrating core functionality into it. I'm expected there to be a bit of work to unravel this into hook interactions - but the core extension mechanism gives us that space --- .gitignore | 1 + CRM/Upgrade/Incremental/php/FiveThirty.php | 42 ++ bin/regen.sh | 2 +- distmaker/dists/common.sh | 1 + ext/financialacls/LICENSE.txt | 667 +++++++++++++++++++++ ext/financialacls/README.md | 44 ++ ext/financialacls/financialacls.civix.php | 477 +++++++++++++++ ext/financialacls/financialacls.php | 172 ++++++ ext/financialacls/images/screenshot.png | Bin 0 -> 11775 bytes ext/financialacls/info.xml | 33 + xml/templates/civicrm_data.tpl | 1 + 11 files changed, 1439 insertions(+), 1 deletion(-) create mode 100644 ext/financialacls/LICENSE.txt create mode 100644 ext/financialacls/README.md create mode 100644 ext/financialacls/financialacls.civix.php create mode 100644 ext/financialacls/financialacls.php create mode 100644 ext/financialacls/images/screenshot.png create mode 100644 ext/financialacls/info.xml diff --git a/.gitignore b/.gitignore index 390c2a04945c..0070fbb42c66 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ !/ext/flexmailer !/ext/eventcart !/ext/search +!/ext/financialacls backdrop/ bower_components CRM/Case/xml/configuration diff --git a/CRM/Upgrade/Incremental/php/FiveThirty.php b/CRM/Upgrade/Incremental/php/FiveThirty.php index 1f8367d24867..8ccaf073f383 100644 --- a/CRM/Upgrade/Incremental/php/FiveThirty.php +++ b/CRM/Upgrade/Incremental/php/FiveThirty.php @@ -65,8 +65,50 @@ public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) { // // The above is an exception because 'Upgrade DB to %1: SQL' is generic & reusable. // } + /** + * Upgrade function. + * + * @param string $rev + */ + public function upgrade_5_30_alpha1($rev) { + $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev); + $this->addTask('Add core (required) extension Financial ACLs', 'installFinancialAcls'); + } + // public static function taskFoo(CRM_Queue_TaskContext $ctx, ...) { // return TRUE; // } + /** + * Install financialacls extension. + * + * This feature is restructured as a core extension - which is primarily a code cleanup step. + * + * @param \CRM_Queue_TaskContext $ctx + * + * @return bool + * + * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception + */ + public static function installFinancialAcls(CRM_Queue_TaskContext $ctx) { + // Install via direct SQL manipulation. Note that: + // (1) This extension has no activation logic. + // (2) On new installs, the extension is activated purely via default SQL INSERT. + // (3) Caches are flushed at the end of the upgrade. + // ($) Over long term, upgrade steps are more reliable in SQL. API/BAO sometimes don't work mid-upgrade. + $insert = CRM_Utils_SQL_Insert::into('civicrm_extension')->row([ + 'type' => 'module', + 'full_name' => 'financialacls', + 'name' => 'financialacls', + 'label' => 'Financial ACLs', + 'file' => 'financialacls', + 'schema_version' => NULL, + 'is_active' => 1, + ]); + CRM_Core_DAO::executeQuery($insert->usingReplace()->toSQL()); + + return TRUE; + } + } diff --git a/bin/regen.sh b/bin/regen.sh index f7451a23fc02..e7525399ebdc 100755 --- a/bin/regen.sh +++ b/bin/regen.sh @@ -47,7 +47,7 @@ php GenerateData.php ## Prune local data $MYSQLCMD -e "DROP TABLE IF EXISTS civicrm_install_canary; DELETE FROM civicrm_cache; DELETE FROM civicrm_setting;" -$MYSQLCMD -e "DELETE FROM civicrm_extension WHERE full_name NOT IN ('sequentialcreditnotes', 'eventcart', 'search', 'flexmailer');" +$MYSQLCMD -e "DELETE FROM civicrm_extension WHERE full_name NOT IN ('sequentialcreditnotes', 'eventcart', 'search', 'flexmailer', 'financialacls);" TABLENAMES=$( echo "show tables like 'civicrm_%'" | $MYSQLCMD | grep ^civicrm_ | xargs ) cd $CIVISOURCEDIR/sql diff --git a/distmaker/dists/common.sh b/distmaker/dists/common.sh index ad93400ce3e3..522bcc0c2ec2 100644 --- a/distmaker/dists/common.sh +++ b/distmaker/dists/common.sh @@ -109,6 +109,7 @@ function dm_core_exts() { echo ext/sequentialcreditnotes echo ext/flexmailer echo ext/eventcart + echo ext/financialacls } ## Copy all packages diff --git a/ext/financialacls/LICENSE.txt b/ext/financialacls/LICENSE.txt new file mode 100644 index 000000000000..96bc3698ee01 --- /dev/null +++ b/ext/financialacls/LICENSE.txt @@ -0,0 +1,667 @@ +Package: financialacls +Copyright (C) 2020, eileen +Licensed under the GNU Affero Public License 3.0 (below). + +------------------------------------------------------------------------------- + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/ext/financialacls/README.md b/ext/financialacls/README.md new file mode 100644 index 000000000000..531f9bea768b --- /dev/null +++ b/ext/financialacls/README.md @@ -0,0 +1,44 @@ +# financialacls + +![Screenshot](/images/screenshot.png) + +(*FIXME: In one or two paragraphs, describe what the extension does and why one would download it. *) + +The extension is licensed under [AGPL-3.0](LICENSE.txt). + +## Requirements + +* PHP v7.0+ +* CiviCRM (*FIXME: Version number*) + +## Installation (Web UI) + +This extension has not yet been published for installation via the web UI. + +## Installation (CLI, Zip) + +Sysadmins and developers may download the `.zip` file for this extension and +install it with the command-line tool [cv](https://github.com/civicrm/cv). + +```bash +cd +cv dl financialacls@https://github.com/FIXME/financialacls/archive/master.zip +``` + +## Installation (CLI, Git) + +Sysadmins and developers may clone the [Git](https://en.wikipedia.org/wiki/Git) repo for this extension and +install it with the command-line tool [cv](https://github.com/civicrm/cv). + +```bash +git clone https://github.com/FIXME/financialacls.git +cv en financialacls +``` + +## Usage + +(* FIXME: Where would a new user navigate to get started? What changes would they see? *) + +## Known Issues + +(* FIXME *) diff --git a/ext/financialacls/financialacls.civix.php b/ext/financialacls/financialacls.civix.php new file mode 100644 index 000000000000..2f5da6596187 --- /dev/null +++ b/ext/financialacls/financialacls.civix.php @@ -0,0 +1,477 @@ +getUrl(self::LONG_NAME), '/'); + } + return CRM_Core_Resources::singleton()->getUrl(self::LONG_NAME, $file); + } + + /** + * Get the path of a resource file (in this extension). + * + * @param string|NULL $file + * Ex: NULL. + * Ex: 'css/foo.css'. + * @return string + * Ex: '/var/www/example.org/sites/default/ext/org.example.foo'. + * Ex: '/var/www/example.org/sites/default/ext/org.example.foo/css/foo.css'. + */ + public static function path($file = NULL) { + // return CRM_Core_Resources::singleton()->getPath(self::LONG_NAME, $file); + return __DIR__ . ($file === NULL ? '' : (DIRECTORY_SEPARATOR . $file)); + } + + /** + * Get the name of a class within this extension. + * + * @param string $suffix + * Ex: 'Page_HelloWorld' or 'Page\\HelloWorld'. + * @return string + * Ex: 'CRM_Foo_Page_HelloWorld'. + */ + public static function findClass($suffix) { + return self::CLASS_PREFIX . '_' . str_replace('\\', '_', $suffix); + } + +} + +use CRM_Financialacls_ExtensionUtil as E; + +/** + * (Delegated) Implements hook_civicrm_config(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config + */ +function _financialacls_civix_civicrm_config(&$config = NULL) { + static $configured = FALSE; + if ($configured) { + return; + } + $configured = TRUE; + + $template =& CRM_Core_Smarty::singleton(); + + $extRoot = dirname(__FILE__) . DIRECTORY_SEPARATOR; + $extDir = $extRoot . 'templates'; + + if (is_array($template->template_dir)) { + array_unshift($template->template_dir, $extDir); + } + else { + $template->template_dir = [$extDir, $template->template_dir]; + } + + $include_path = $extRoot . PATH_SEPARATOR . get_include_path(); + set_include_path($include_path); +} + +/** + * (Delegated) Implements hook_civicrm_xmlMenu(). + * + * @param $files array(string) + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_xmlMenu + */ +function _financialacls_civix_civicrm_xmlMenu(&$files) { + foreach (_financialacls_civix_glob(__DIR__ . '/xml/Menu/*.xml') as $file) { + $files[] = $file; + } +} + +/** + * Implements hook_civicrm_install(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install + */ +function _financialacls_civix_civicrm_install() { + _financialacls_civix_civicrm_config(); + if ($upgrader = _financialacls_civix_upgrader()) { + $upgrader->onInstall(); + } +} + +/** + * Implements hook_civicrm_postInstall(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall + */ +function _financialacls_civix_civicrm_postInstall() { + _financialacls_civix_civicrm_config(); + if ($upgrader = _financialacls_civix_upgrader()) { + if (is_callable([$upgrader, 'onPostInstall'])) { + $upgrader->onPostInstall(); + } + } +} + +/** + * Implements hook_civicrm_uninstall(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall + */ +function _financialacls_civix_civicrm_uninstall() { + _financialacls_civix_civicrm_config(); + if ($upgrader = _financialacls_civix_upgrader()) { + $upgrader->onUninstall(); + } +} + +/** + * (Delegated) Implements hook_civicrm_enable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable + */ +function _financialacls_civix_civicrm_enable() { + _financialacls_civix_civicrm_config(); + if ($upgrader = _financialacls_civix_upgrader()) { + if (is_callable([$upgrader, 'onEnable'])) { + $upgrader->onEnable(); + } + } +} + +/** + * (Delegated) Implements hook_civicrm_disable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable + * @return mixed + */ +function _financialacls_civix_civicrm_disable() { + _financialacls_civix_civicrm_config(); + if ($upgrader = _financialacls_civix_upgrader()) { + if (is_callable([$upgrader, 'onDisable'])) { + $upgrader->onDisable(); + } + } +} + +/** + * (Delegated) Implements hook_civicrm_upgrade(). + * + * @param $op string, the type of operation being performed; 'check' or 'enqueue' + * @param $queue CRM_Queue_Queue, (for 'enqueue') the modifiable list of pending up upgrade tasks + * + * @return mixed + * based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending) + * for 'enqueue', returns void + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade + */ +function _financialacls_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) { + if ($upgrader = _financialacls_civix_upgrader()) { + return $upgrader->onUpgrade($op, $queue); + } +} + +/** + * @return CRM_Financialacls_Upgrader + */ +function _financialacls_civix_upgrader() { + if (!file_exists(__DIR__ . '/CRM/Financialacls/Upgrader.php')) { + return NULL; + } + else { + return CRM_Financialacls_Upgrader_Base::instance(); + } +} + +/** + * Search directory tree for files which match a glob pattern. + * + * Note: Dot-directories (like "..", ".git", or ".svn") will be ignored. + * Note: In Civi 4.3+, delegate to CRM_Utils_File::findFiles() + * + * @param string $dir base dir + * @param string $pattern , glob pattern, eg "*.txt" + * + * @return array + */ +function _financialacls_civix_find_files($dir, $pattern) { + if (is_callable(['CRM_Utils_File', 'findFiles'])) { + return CRM_Utils_File::findFiles($dir, $pattern); + } + + $todos = [$dir]; + $result = []; + while (!empty($todos)) { + $subdir = array_shift($todos); + foreach (_financialacls_civix_glob("$subdir/$pattern") as $match) { + if (!is_dir($match)) { + $result[] = $match; + } + } + if ($dh = opendir($subdir)) { + while (FALSE !== ($entry = readdir($dh))) { + $path = $subdir . DIRECTORY_SEPARATOR . $entry; + if ($entry[0] == '.') { + } + elseif (is_dir($path)) { + $todos[] = $path; + } + } + closedir($dh); + } + } + return $result; +} + +/** + * (Delegated) Implements hook_civicrm_managed(). + * + * Find any *.mgd.php files, merge their content, and return. + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_managed + */ +function _financialacls_civix_civicrm_managed(&$entities) { + $mgdFiles = _financialacls_civix_find_files(__DIR__, '*.mgd.php'); + sort($mgdFiles); + foreach ($mgdFiles as $file) { + $es = include $file; + foreach ($es as $e) { + if (empty($e['module'])) { + $e['module'] = E::LONG_NAME; + } + if (empty($e['params']['version'])) { + $e['params']['version'] = '3'; + } + $entities[] = $e; + } + } +} + +/** + * (Delegated) Implements hook_civicrm_caseTypes(). + * + * Find any and return any files matching "xml/case/*.xml" + * + * Note: This hook only runs in CiviCRM 4.4+. + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_caseTypes + */ +function _financialacls_civix_civicrm_caseTypes(&$caseTypes) { + if (!is_dir(__DIR__ . '/xml/case')) { + return; + } + + foreach (_financialacls_civix_glob(__DIR__ . '/xml/case/*.xml') as $file) { + $name = preg_replace('/\.xml$/', '', basename($file)); + if ($name != CRM_Case_XMLProcessor::mungeCaseType($name)) { + $errorMessage = sprintf("Case-type file name is malformed (%s vs %s)", $name, CRM_Case_XMLProcessor::mungeCaseType($name)); + throw new CRM_Core_Exception($errorMessage); + } + $caseTypes[$name] = [ + 'module' => E::LONG_NAME, + 'name' => $name, + 'file' => $file, + ]; + } +} + +/** + * (Delegated) Implements hook_civicrm_angularModules(). + * + * Find any and return any files matching "ang/*.ang.php" + * + * Note: This hook only runs in CiviCRM 4.5+. + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_angularModules + */ +function _financialacls_civix_civicrm_angularModules(&$angularModules) { + if (!is_dir(__DIR__ . '/ang')) { + return; + } + + $files = _financialacls_civix_glob(__DIR__ . '/ang/*.ang.php'); + foreach ($files as $file) { + $name = preg_replace(':\.ang\.php$:', '', basename($file)); + $module = include $file; + if (empty($module['ext'])) { + $module['ext'] = E::LONG_NAME; + } + $angularModules[$name] = $module; + } +} + +/** + * (Delegated) Implements hook_civicrm_themes(). + * + * Find any and return any files matching "*.theme.php" + */ +function _financialacls_civix_civicrm_themes(&$themes) { + $files = _financialacls_civix_glob(__DIR__ . '/*.theme.php'); + foreach ($files as $file) { + $themeMeta = include $file; + if (empty($themeMeta['name'])) { + $themeMeta['name'] = preg_replace(':\.theme\.php$:', '', basename($file)); + } + if (empty($themeMeta['ext'])) { + $themeMeta['ext'] = E::LONG_NAME; + } + $themes[$themeMeta['name']] = $themeMeta; + } +} + +/** + * Glob wrapper which is guaranteed to return an array. + * + * The documentation for glob() says, "On some systems it is impossible to + * distinguish between empty match and an error." Anecdotally, the return + * result for an empty match is sometimes array() and sometimes FALSE. + * This wrapper provides consistency. + * + * @link http://php.net/glob + * @param string $pattern + * + * @return array + */ +function _financialacls_civix_glob($pattern) { + $result = glob($pattern); + return is_array($result) ? $result : []; +} + +/** + * Inserts a navigation menu item at a given place in the hierarchy. + * + * @param array $menu - menu hierarchy + * @param string $path - path to parent of this item, e.g. 'my_extension/submenu' + * 'Mailing', or 'Administer/System Settings' + * @param array $item - the item to insert (parent/child attributes will be + * filled for you) + * + * @return bool + */ +function _financialacls_civix_insert_navigation_menu(&$menu, $path, $item) { + // If we are done going down the path, insert menu + if (empty($path)) { + $menu[] = [ + 'attributes' => array_merge([ + 'label' => CRM_Utils_Array::value('name', $item), + 'active' => 1, + ], $item), + ]; + return TRUE; + } + else { + // Find an recurse into the next level down + $found = FALSE; + $path = explode('/', $path); + $first = array_shift($path); + foreach ($menu as $key => &$entry) { + if ($entry['attributes']['name'] == $first) { + if (!isset($entry['child'])) { + $entry['child'] = []; + } + $found = _financialacls_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item); + } + } + return $found; + } +} + +/** + * (Delegated) Implements hook_civicrm_navigationMenu(). + */ +function _financialacls_civix_navigationMenu(&$nodes) { + if (!is_callable(['CRM_Core_BAO_Navigation', 'fixNavigationMenu'])) { + _financialacls_civix_fixNavigationMenu($nodes); + } +} + +/** + * Given a navigation menu, generate navIDs for any items which are + * missing them. + */ +function _financialacls_civix_fixNavigationMenu(&$nodes) { + $maxNavID = 1; + array_walk_recursive($nodes, function($item, $key) use (&$maxNavID) { + if ($key === 'navID') { + $maxNavID = max($maxNavID, $item); + } + }); + _financialacls_civix_fixNavigationMenuItems($nodes, $maxNavID, NULL); +} + +function _financialacls_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID) { + $origKeys = array_keys($nodes); + foreach ($origKeys as $origKey) { + if (!isset($nodes[$origKey]['attributes']['parentID']) && $parentID !== NULL) { + $nodes[$origKey]['attributes']['parentID'] = $parentID; + } + // If no navID, then assign navID and fix key. + if (!isset($nodes[$origKey]['attributes']['navID'])) { + $newKey = ++$maxNavID; + $nodes[$origKey]['attributes']['navID'] = $newKey; + $nodes[$newKey] = $nodes[$origKey]; + unset($nodes[$origKey]); + $origKey = $newKey; + } + if (isset($nodes[$origKey]['child']) && is_array($nodes[$origKey]['child'])) { + _financialacls_civix_fixNavigationMenuItems($nodes[$origKey]['child'], $maxNavID, $nodes[$origKey]['attributes']['navID']); + } + } +} + +/** + * (Delegated) Implements hook_civicrm_alterSettingsFolders(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_alterSettingsFolders + */ +function _financialacls_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) { + $settingsDir = __DIR__ . DIRECTORY_SEPARATOR . 'settings'; + if (!in_array($settingsDir, $metaDataFolders) && is_dir($settingsDir)) { + $metaDataFolders[] = $settingsDir; + } +} + +/** + * (Delegated) Implements hook_civicrm_entityTypes(). + * + * Find any *.entityType.php files, merge their content, and return. + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes + */ +function _financialacls_civix_civicrm_entityTypes(&$entityTypes) { + $entityTypes = array_merge($entityTypes, []); +} diff --git a/ext/financialacls/financialacls.php b/ext/financialacls/financialacls.php new file mode 100644 index 000000000000..e6c39a0ab068 --- /dev/null +++ b/ext/financialacls/financialacls.php @@ -0,0 +1,172 @@ + E::ts('New subliminal message'), +// 'name' => 'mailing_subliminal_message', +// 'url' => 'civicrm/mailing/subliminal', +// 'permission' => 'access CiviMail', +// 'operator' => 'OR', +// 'separator' => 0, +// )); +// _financialacls_civix_navigationMenu($menu); +//} diff --git a/ext/financialacls/images/screenshot.png b/ext/financialacls/images/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..6765b696fa03249ac2cd605d5f0e4aa000ad6dad GIT binary patch literal 11775 zcmcI~2{hF0+y56OQnV|Q9{obnC}fhoMT;zDD-6k&WsqSQyC_cyNfKkwBV?D!GRBt5 zGGmLzHfHR`GDD0#%X^QW=l8znfBxq^@Be?!drqgOx$n8}>%Q*KwcOX|dS;+^ZTC)* zoe%`=zH$A^9SGtfLJ-$;K3?!m^9hqK2s%@D9eVB`m ze0z+oc|*}j>nVYr)!O=^lmDV7CVZoA&aAMLlfE~UxJ+GV44YZJ@XT$!mYBA*L~lj) zU!JMn4BQT+S+^Z82wHdcnxR)ZA(43^qM|u-7s2=QMf=d#u3gKaP$-jW(dyZW&GzS2 zRLlc@e%^724}yY|<<-?KpA#;#gaVfb!zq1z7R9BdrefmarerdiQtaF%dl;;OIQ^~n z0Mf8R_l@p3?{Q;C9~+xABR_F9Un5%Cqc0NN5Y%@5P7>nYy;EH9tJDK-QwY-K0zZb4 zAA`ZjSzlkDTwyat{bwjSre|cqa`6)1x)0bHCh5i~ed{%BWhPTbMr;=bK8H>YM{DGl zl$Pd9OgIYch9J%Q^zpN2FULrgL~a2%=L*wApjxUkSb?AS3Aa5l=&&;v}9 z5Lh>u{aV8LdETk=jO9cL<~_RGI6~u5+V_V6L(R?2bLHaTz>r%`WsL}CMOPsAGGpPJ z1;HcT%*-r1SYvM&0#saLDuzQI_>zU-ibIuS+xi1(p>)x77kx=Zkmz$pt}&cj1nCt9b71zcO}| zR)N7&M0}A*){o9Mu*kgkK{(!BVLu%IC_X-(oZF}8pZ_jmV@&(^Wz9vZ1+JAP;&8~_ z>)?J`83VZNrK-V&g@sN_mh7KS zazJ}C%00x1MHV%_o#^-hd+4OVOG%S^FUti?50T_{fpKqaLV~02XybA!IhmyJBTwM` zxpUtW3tn$0*RE^_mpXpz#EBD>_Vznoo}NY=ZU3zx;*hiG?)PeBM=(tm zVEO<{Q6$m{g~A_@V)s|jkygVmw*%P+a-TGWhK43fUlCjwl5o%>78?ly!03A{q5+J& zzRuT0imJ_e2`#MFrl@Yi;j04w0J@`FCho|L@Bn}1c$A-BGGhPV`6FP#8(FT|^r0obN?$^T$7UHD5I4RUgFDC6S~ zTZ61FudiJuqFH7uS{#u3`T21$-%5`Iio7sW@u{L>Evw>(KG7zecQE{Jf0obM@}m{4 zMKTUJfnazXuOX30On;88Gj!CXME5h=QrGX}(+Tv@IC{9uNzJ{)jg^@UFo-UwDkbGwQBqQM~^-l`Qmrq zHz)RIF4x zO=v6$b`knOMLSS6t4mqH8^Y>=cKJ*A%c8|K^6ecQBJ~fVRpfA3JTmoILlSEnM-HDZ z8P^iYu6s5}q;BUPwA{54Az3-Oc;cBJlb5XR*_Gh%)M*J|JXwhMwXWW@kj9e7_V|0# zHMT^p60y7Bzyo&e;tzo}Qp6uVcu)&Or8&E}hCb$uf<-Pvh^p@BuzW>y!b(qoLXD|<3t z@|to)T{V{}0a!oNVZm_>XU?24HqE#As8s73v{}UXNw7jgkOA{jH|un4Y%JwVxDZ+C zDY#LO3$$_|fSb6D|J=wkn-{gP1+ImoH(LkiSA%_z;gtYb*X5sF4K=fl@4D^Ml; z87}B9ba8&IqBf%QF6+G=uvWp zs3(lZ=qNW;5sBv0heu!f2@=hPfy?T4o>NTp(1kIFVEcfFS#u@d(r;`?^)0o_$hj+O zEzIBnpkV+bH4(=(H|qv?h9O{=FHKKQI_0pbOlEG28wCA)dgo)F)cd1iV#W~>5t8ru zh@PRo8C`U`rI)|IO9t2`>GRjGS2*7K&bQ`-zFXHm!fIg%neb86Lv;%MTJ*8FFY_E7 zZkR|Xrlb@ks|T26q}c8x0q36=S*RnouJ6xi!Fjx;Z+->WQ9e66+dh*4+X?(*$6ihA zc+2?93q8p(n;%i#^hIDVyjry`tG%kg86I8IsTOTUJVC4)2=0Q!ainyS0y9u4x4aEt= z*r^`Hl$)C(04kSsF2XZMeX}bk#*!$4sq$l z#9}UJK=r-bt<`#4hr#J-*NM|Mh&^b6K~Hy+dN8ylNWUh8=JSyfdFv(K3){ms5)z6&n-2|=M`ADuA7s$San4@z*jPBDD5 znd4pVpMC$?2)#CHb~ZAzJ(yH}oY`*LE~9>s?#t(6)|kodf&|vIR6a8>P~#b`scWF2x5%)7CTes<~*jZ_w@B8@&3g}bdS+fy4q0V0U-7h?QzB!Eer_f7BY+b>*Ezfk_a+F_KTVXo|1yz@$7*R67R6NrbwZ_cN&3O}epri-B zzU94iI;`tf&ndXFvLm?Ocfz(WS3pXWHE&&X z+!_0TDUbAKPNQBq|79V1PdE%Qeqy4xFHrI%0*AZ71+CuBf8?>D)TK&TY&`!mS(1-< z7Lw02E!Bcj`$rNbyOaoZjm1%p2f_EL^4DDL?7kgUhEg3knc=92WS2Z4qp?^w|55Jl zTA0Dc^`@2e2e@f(bqxhZAc)1_cY8Q&2F>SDW1XoFMrT2G$^m9y^vlO|PUn4Of5q^gdDp za?jV+)+U{=`~LklCvxVidF+0G)a-sD5W~g7VzGH#tSuvie4Hb-)4Y%$i;ImdOF2uW z(*qE9?>?jjG?Ot+y%6L;JCAmEH^@K96ydQJE^nWo_sp8>WH{YfFNT5W?4|M2LScLd zcP(uGMPH0={nw8D9#_O{xAVIH1Zu#Z&7cV+- zj`4YAI9{c4W0^~Hlh#M4Qc);?fy1}>2`p;jzQ;&sZm9kZ#95D4DB&GE_4FV?0a#wD zU-}<*+S?tk^Vh=Mc^9M~_Z&G*ARvn5F3e_4c#F`5*z8ek(``d0)qzJ&K_qz zCX%3<9&gj;)>c#f6!l`_&M-s`<_~BbIXpZ}SzYzbajR>_!t(?GNbIsywAB3;b1D2D zK0e*OP|XqB5i9LYJBixa*+<3YLC8;mgk0EG=`;Jo~lI`HvJv%~fFw9rC< z`Nv78NxAeUW-}%YIAiRu9l-!P88F6J62LjRG@_!=vvPvXq(Kyia3 zdcQ*H1Amgfj7;E{XC#{U;?C`MD;7?z*1dJ>wdB*Rp|M-j^Yfp)xHwbNF~*5^(-Vvn&C&_R zi5B-np;k7Kw@tVmiC>S}Y-)Pp#Z?%(L$iDPDnr(^-2K?mqp$H66DHq%@ZZ;HgktRMb9Mt^tQs&U8~(fMmHl_Bgm39GZQ~% z=y=Hv39L1hN7!OwZsM^(DjqIUT3V`euq#p<2w4Ux3)`O-({zY>InsUo*WOpE_MGVh zkG-5@UmGPGhVc5}j&_tW8M#JCadrQ@Q4#MGRnDIub=w6F;0Z$WupD^io4%;#m`s6Q|jup zF;RUqTApiP*`V7Fu=4yXqcg7kRfv)Uj0YYV3_+2w4;`W>RM*S@^Pagq`?zV>4D|_Ug;DW0lJo?f^S499SA%+Wh)OcyLdhiaM zSLU^r5B$J6JfW4_1k@r+WEgkrySv|3bAk2FO1B4if|n&F;M4bq086b;X+Q%(n}I7n z%2hEoN9|*HYGF0d)K`le*1}~U3b8gvl#^pby_NH0Y($f(z%YaL6Oxl*#&iosb)TQ9 zMnO*ysnlB6D65gf+OF+l01(4Rlrsh~NB5x5aMw7O^4O2r+Ut+`bG#z`p-L*S5DDg3 z+0}qGiA}ZEVPOETn&8GmpE>!iwwwb^LYm3VIEbFA^UWH_cty>s^WB<3xY1L!IMjOZmuD%)PjUyU|UBO|m+`9d{LCMITQ5 zmE=mosM6w4xwR-cFwgznJ`aU2^6%h-OPzV%?L{pcJqfxOzS!IM3Ec*wF9Y zwiW|h{oZwJx8J*N?FK&nv)k`)|Mw?u?eqJ<|GD)O&e8uF`TcQALBFN`FS`0i>i?p) z|LH_bdjI;gH^sY(jhoD|T6bFT4qEaat>%159uEB^2r2}2Vu34UtK?tx8)U1EA#dF+ znZTQxBo{av;q>s~ht)NPhE>3@X#WSg-%RCPe`~-rYIHVbbB)1+KNPgVP9ZNP)%Ycg z!<~KU;`_Ihxd)ATCZw@e7}i(4`Kx;K*66H$(5hk9oaFqNo%IsSYsf}o>*{J@a!_0x5?JnL`}^uX9lfiwi-pVF@f`G z;>K3XwZdj@OK}DM>DzuTmgZ=f9&7EoUCrvMS}dMis@`1BRHq9CEq~DusPGrkSf9|> z*pIGWXStd7Z*DB3T`aV7;W z$2eE*R}0%hbork%sYVhVFJ!El;+wPLIyM_#`HZ6j8gI8UQe7OBd@(8WroUOSs+;MK zpnrpnc;)f^l9f(sY|zh8@z?d6tp1I>ij8oMwZXU-uNH)Ra z(BcRqyHu#l8m?8c?vLaK7)bQQrHi6|Eg8WVeS1{t{%S0%*uv9)yEkEJsyC`?!EQ5| z~j~HMz5cFp&vLu-|~t9sjkcO z*&o&UGnKz2#_w0RDv4D7Ovs0-WtozS)h?o_lG|NY*VJ3cCFdB(jpcfcKqvD2z0*WY zbu2z?iWjjU{T?4i^UsqKHNo}OB^4h;Kl$y-m-c<~-l>j!bSo-5<{PZJH{#O$b@d)@iyhy?I=2lO1?@MPy1!CX zXdZoxgz52*st73I&OB#KuMXfv(hOv*6;m<;<_-L=E&$cgA0vV)d{t0>9|B_wvC+4q z;Uq@3hUKsEcdJPDx};0y>>Fj`me~at`e}v0hdp|+aLVqaJI~aR5vc5VezyhcSU^Wx z_Qq0JeFyhLG3osm_V1ip(!cAfVQwCT;Qzo%k&?@N&})fnW;o6G_gTiZbQy$`EA;JI zB|#Pb(fl90y2kwrFex47vMGydCkMuy-8OwD7X6EECbRmz!l!(j_Wo`59$I%IZygSx z9HGa?^~R#?`~PLjK;cRPRzJ0(ItwObix$c%&DQfL&$I=G{||!dzvJNlHKKrE^S=~& zKq&t2xBfl46%YSC@xPDN)?IAL-?6?H2Esr12*Ur?$Nw`E_&o}~{r{Sne--*CO#c&% zHM_m`3T^H7dspz0HW4bM{y}|p{DkmqO~S~^1MmQ4OM&4wT)t(c8Lc{~+Q?CTWn};F zVTllLz)_S3u9q_m!Ff|vnwpx7!26t1FCJu}1`Qjl;vkDKw(DM)B_3-P`G7O|DT|BOgIUAiL7p*_i^K zP7IEfrsDr8;+L(sS$3uF9KRBp{r&rQCoiw!i|XpIL2op=B+R26%n9BpFAUj_QR69A9!#jK0W7nl${}dt$X`L zWe0exF`LaEC2)8>3ZNC}lZ2gM{x}NM57YPF%08(q8ZXf1tt=|VKtmU8 zot&~kRn8STsKXN4Q88aLh% zquz>e?2Ff1n2`_M%~85ek4Jo-chBQ^c3ID>_&iJ}?*Q$e2QowX)z+LRE+5UUtmG&! zT`I|kRHwMx5*Sn$q~3U5ikq+O{(Fy|!cyq0y_>?{mmuZqsYY{GH{G-nzu*qjf-WHoew;*1F)0_P5Cd@2Z6?3Q3XZ9~MV0^;oJ9&d+<(**c z4s5eMe^o+G^2%fFfypfhn54(;S!NI3BPJ#y(!vuESgB!1AtCZsL9!P1vmN<>@UY?AWtno=QD_|9(b17k z!is(pI;JX3v6=$jQWNmDw_$=FF)tMegolMNXnyEB)^BM#4pph_i(<5h?LNS%Sm|yo zx3;#HYO9P4)jZq*tzJ!(nVz10qt}us^SDr8S1`!R<%QruYg@cNpsSn606_C;z;hJW zjjCbhzLu#1xdUHq6(~9MH1I|FCZ?u2py&#!JvHlRT$He*Pu*PCR?Yc<&3y631J84_ zhDN2H_Qo4O;yUcfPgsCuhV_Tt4-y9w`Hm#rDUt4Cyynm|P)(+t3d(gG)uwhG&H7kv~M|=eXTxGGBg@ACMHOhs=)#tAXL$C=+_V@2G z*e0+Vu2;`%lynf#1o8KQ6Y%nXh(dMD{sE2+j}s($rM>Vp>6GSy%CEVq$^lleHCgR6 z{5|NoFyAdCv?Xr5@uR7|db{rso*u`@H5~^B&OQC?+SKR^1WtUL#TOP= zDm>j8&=nwN`V?R)BPi}jtNMcs{@>+3VSkvg_7NszHw}Z`S&EbG(SdUZp z9hb@K&hYR9rEwWT*0?=y3LvK}9XvdWj-p7Bb4UFWIi+AwPah@qP@8__Y+kVIO!ne} z=!;_<5<_{zs9&}9qqH+9(^zzuq&0!WHO@wyphoSuW9IC zdNYRg;^OdjNgaUyx+H|`xd);Ef`96a3gF@f+(7e+j$sSP1w1~TZob&@siPOnidW7% za@l^AL!X%fpObzMR5wArc+?Bfnga2%Atu-WDA+qb<9AWhKG|_3VP_$SI}^wq5=j+= z+LRr~?nOUrI%0J~WH0(%=vFba-9<(>v)x}t_XD^DO-4cW;?fc#=nv*&l?c#`lpf$f ztE+p8^Wr1^EOF2V#a!GGK)lLNBouLKsR{BNCmvBKP!OAqr0qUSv7kX*P`RPn?zA44 zk__t?QBh`q-}o@9cYmy6^yu}L(6?+}sLdQ`Q1>$aoAMysJXyCvv)k@IqB^iLX{jN0 zsHH{EYq)MVAm!SIufA|`)>HT{EN5v1nwadUcyJiI`^iv>DrSg(52R@g>dL<6Ep8)6 zuT>VGWT?0xfowsJ@{|*wOF|KxBtb*($VWWt{@*(wS-aIlG0h zq^`Coz)Yc!C?tG;#j=rPzXhA5pB_D`-C{GMhiGvaNdkNTr`sblQzs7I_403T4VdI~ zGA1I4*nl7;eQChFNFo=4#(;_muThXkkqGqF^7d&>z{^_zqP3)?gd|o5=5mJwi#s z88QHwknl-Eb@}r4*Fs?k`n~D5fToFeB@trZu=h_CaY))SKqm1M`78OW`9Jf2;gGLtL!f)+Dl>Lt)_ za>Vg|oVJPAXUX09xe*eQX9&2nJ9~h@%#=2bI;eV3~`Yu)x+IBEm*-v#2V|K#| zpcz1O`u?b%$@Ig~7L#fBd!Rpo_xq0@MpL~$z;hRW1PurMl{>u)K=X)Oz6Ie9rkos z9k^<*q_bVjowsXY4yuP!Bn=lnFR!d%b_{btsRB8b1e4bDel%&NuDMR_?$NM>gai{Z z`-M>Z+))Vfz5I)>&7wn;ONJ>}0kRAErq%KB4;NKc6`Md)iSOO~QQ<>}NW}s9v#7ij z=RnX1A^76&{umVv@bm!T0BW=9S_AGPf8`17a~V07#QOYMYYNsHciI$KJ~aQ)g55<_ zj@`~2JAnSB-9iW1+o#2ue$W;OItyx`i#?eRzk78m)04l%wYW@VS5#EwDu8YtgSRyj z%*CXSA3uVuL!mUWIU~Uw{6y#E>3=EMTDr+!DZ0WT{jE!#1GO9+9CXT><;Fp)$`&OG znz0mgo?d@*0c0SHi;Fkw3@+?P%Y6g=iloI67qtdCo02nBU(ni=bnV=kBPTwap408F zJnMM27#I+Tvx?(20wx7ra06LjUNe7X&m{Bh`$E6zN%UZAiber&W;#T7V+(Nc694jh z|IC;MEp4p-5*gS@oRJJo0bGrFXsVQy6bG&DjzxGyn}`=m2hsiA|Jj-N=5s4X8Bp~A zS?0ThgyO|AckI}4veZ_}3H^R2F;H3kgfVqh@J2QPq;nim#-E90^*l?I#dDZxVKghi zjTO*}>Dz@aSpv->;}k?T$fMKSvAFD>^u(Qog({lu6c>OEPosQd;z|kt*SK&`mf4ax zNw8CELPPPT_0lJdaYEcw&^91~l^w$gUDz3GSA;K4ks|Lh>efxwE!3^iZPOh%j|IJL zhZTFiC*tyY2FpKwyp68dNmRT04Y{dhzXzqUah%hRmvOudxNaG~mgeRK-QrTWFRp&i z)O9mgE3W{*@H79K8c4aPJ}>~h3a5Ua2OUj8hebtCt31FfC=J3@?EGez9jw9-NRTx^ zEvD!?dvg?;RP{S?pU$eQQWHZP7U%7d%uG~i-~sjkT^#+VD~t2ojueH5(e(NdWqwPp zj&V44nU0PQ1we3(8}uD|-bJ@9-aIyyP)LpN2Br+HQW|k3fbXxJ#!l}Inj5*HKMh!1 zXxw|^-Rr6w6DdYZ1|*x>=~L}P7xVI`rmY-9Xc4q(@I&4kaNYWe#6s*@KkyXT=epmc zW3s%9aS_O~>#wBYO79s<%SpK-8j`Cii zV-V=58R2sV!>fE#Pr_&%YeXXBWL(V*8D1K;0Qa9e8^^wi2@n?w2R|k2orE8xxjxhY zmZ<5SRo^nrV&SOWJt!gK{{8#Yzj<2O|d>fLRsDmI@R@Z(`~7UTwETh5l1a;M?QBeDxZt!m!5(tOC&O7yLFU z$%S>CPN%DP2oAvoi7XtR(}!a{r03ZJbP$Z5-~kP~ZIip>;;lMGC!4I{;h|ekf#!FE zM~|6&19%^hw+SDr|#x={YN*N{}`xvl&C-IUigHAZS(ZIU^r_rP=-17(hb4eGT0w$cOM*O9DQm) zuvq+Oq^a0Xuwt;~+1KB-9FR27RR*c;MFVzWbA3N&p48?JJ%4|t45-&1{1>`$Rqsmv IrTdTn0|#irRsaA1 literal 0 HcmV?d00001 diff --git a/ext/financialacls/info.xml b/ext/financialacls/info.xml new file mode 100644 index 000000000000..8539355b3256 --- /dev/null +++ b/ext/financialacls/info.xml @@ -0,0 +1,33 @@ + + + financialacls + Financial ACLs + Implements financial ACLS - may not be disabled without risk of site breakage while in progress + AGPL-3.0 + + CiviCRM + info@civicrm.org + + + http://FIXME + http://FIXME + http://FIXME + http://www.gnu.org/licenses/agpl-3.0.html + + 2020-08-27 + 1.0 + stable + + 5.30 + + + mgmt:hidden + + Financial acls - working towards moving this code to the extension from core + + + + + CRM/Financialacls + + diff --git a/xml/templates/civicrm_data.tpl b/xml/templates/civicrm_data.tpl index dc9967e4fec5..d9891d3e1c96 100644 --- a/xml/templates/civicrm_data.tpl +++ b/xml/templates/civicrm_data.tpl @@ -1781,3 +1781,4 @@ VALUES -- do not try this at home folks. INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'sequentialcreditnotes', 'Sequential credit notes', 'Sequential credit notes', 'sequentialcreditnotes', 1); INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'eventcart', 'Event cart', 'Event cart', 'eventcart', 1); +INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'financialacls', 'Financial ACLs', 'Financial ACLs', 'financialacls', 1); From 58d1e21e44d0392974b0cf596d69e735c7fe9d2b Mon Sep 17 00:00:00 2001 From: Seamus Lee Date: Fri, 28 Aug 2020 09:59:22 +1000 Subject: [PATCH 036/834] [REF] Move Auto DSN Switching into a core function --- CRM/Admin/Form/Setting/UF.php | 3 ++- CRM/Core/BAO/SchemaHandler.php | 6 ++++-- CRM/Core/DAO.php | 1 + CRM/Core/Lock.php | 3 ++- CRM/Logging/Differ.php | 3 ++- CRM/Logging/ReportDetail.php | 3 ++- CRM/Logging/Reverter.php | 3 ++- CRM/Logging/Schema.php | 6 ++++-- CRM/Upgrade/Incremental/php/FourFive.php | 3 ++- CRM/Utils/File.php | 1 + CRM/Utils/SQL.php | 18 ++++++++++++++++++ CRM/Utils/System/Backdrop.php | 5 +++-- CRM/Utils/System/Drupal.php | 5 +++-- CRM/Utils/System/Drupal6.php | 5 +++-- Civi/Test.php | 3 ++- tests/phpunit/CiviTest/CiviUnitTestCase.php | 3 ++- tests/phpunit/api/v3/SyntaxConformanceTest.php | 3 ++- 17 files changed, 55 insertions(+), 19 deletions(-) diff --git a/CRM/Admin/Form/Setting/UF.php b/CRM/Admin/Form/Setting/UF.php index 62c12870f974..208b2f3cf16a 100644 --- a/CRM/Admin/Form/Setting/UF.php +++ b/CRM/Admin/Form/Setting/UF.php @@ -61,7 +61,8 @@ public function buildQuickForm() { $config->dsn != $config->userFrameworkDSN || !empty($drupal_prefix) ) ) { - $dsnArray = DB::parseDSN($config->dsn); + $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn); + $dsnArray = DB::parseDSN($dsn); $tableNames = CRM_Core_DAO::getTableNames(); asort($tableNames); $tablePrefixes = '$databases[\'default\'][\'default\'][\'prefix\']= array('; diff --git a/CRM/Core/BAO/SchemaHandler.php b/CRM/Core/BAO/SchemaHandler.php index 519b152cfd5b..a6c9845a224d 100644 --- a/CRM/Core/BAO/SchemaHandler.php +++ b/CRM/Core/BAO/SchemaHandler.php @@ -592,7 +592,8 @@ public static function checkIfFieldExists($tableName, $columnName, $i18nRewrite */ public static function checkFKExists($table_name, $constraint_name) { $config = CRM_Core_Config::singleton(); - $dbUf = DB::parseDSN($config->dsn); + $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn); + $dbUf = DB::parseDSN($dsn); $query = " SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = %1 @@ -832,7 +833,8 @@ public static function migrateUtf8mb4($revert = FALSE, $patterns = ['civicrm\_%' // If we specified a list of databases assume the user knows what they are doing. // If they specify the database they should also specify the pattern. if (!$databaseList) { - $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN); + $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsn = DB::parseDSN($dsn); $logging_database = $dsn['database']; $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS FROM `{$logging_database}` WHERE Engine <> 'MyISAM' AND (" . implode(' OR ', $logTableNameLikePatterns) . ")"); while ($dao->fetch()) { diff --git a/CRM/Core/DAO.php b/CRM/Core/DAO.php index 2d3ce654cec8..4c33f8002ec3 100644 --- a/CRM/Core/DAO.php +++ b/CRM/Core/DAO.php @@ -161,6 +161,7 @@ public static function getTableName() { public static function init($dsn) { Civi::$statics[__CLASS__]['init'] = 1; $options = &PEAR::getStaticProperty('DB_DataObject', 'options'); + $dsn = CRM_Utils_SQL::autoSwitchDSN($dsn); $options['database'] = $dsn; $options['quote_identifiers'] = TRUE; if (CRM_Utils_SQL::isSSLDSN($dsn)) { diff --git a/CRM/Core/Lock.php b/CRM/Core/Lock.php index da1366b4243a..894b913ab2d6 100644 --- a/CRM/Core/Lock.php +++ b/CRM/Core/Lock.php @@ -111,7 +111,8 @@ public static function createCivimailLock($name) { */ public function __construct($name, $timeout = NULL, $serverWideLock = FALSE) { $config = CRM_Core_Config::singleton(); - $dsnArray = DB::parseDSN($config->dsn); + $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn); + $dsnArray = DB::parseDSN($dsn); $database = $dsnArray['database']; $domainID = CRM_Core_Config::domainID(); if ($serverWideLock) { diff --git a/CRM/Logging/Differ.php b/CRM/Logging/Differ.php index a124a4006956..863322bb2aae 100644 --- a/CRM/Logging/Differ.php +++ b/CRM/Logging/Differ.php @@ -28,7 +28,8 @@ class CRM_Logging_Differ { * @param string $interval */ public function __construct($log_conn_id, $log_date, $interval = '10 SECOND') { - $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN); + $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsn = DB::parseDSN($dsn); $this->db = $dsn['database']; $this->log_conn_id = $log_conn_id; $this->log_date = $log_date; diff --git a/CRM/Logging/ReportDetail.php b/CRM/Logging/ReportDetail.php index d5822b183d51..fdbd92216a89 100644 --- a/CRM/Logging/ReportDetail.php +++ b/CRM/Logging/ReportDetail.php @@ -257,7 +257,8 @@ public function buildQuickForm() { * Store the dsn for the logging database in $this->db. */ protected function storeDB() { - $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN); + $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsn = DB::parseDSN($dsn); $this->db = $dsn['database']; } diff --git a/CRM/Logging/Reverter.php b/CRM/Logging/Reverter.php index 72d729d03af6..eb1c9dedc16c 100644 --- a/CRM/Logging/Reverter.php +++ b/CRM/Logging/Reverter.php @@ -33,7 +33,8 @@ class CRM_Logging_Reverter { * @param $log_date */ public function __construct($log_conn_id, $log_date) { - $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN); + $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsn = DB::parseDSN($dsn); $this->db = $dsn['database']; $this->log_conn_id = $log_conn_id; $this->log_date = $log_date; diff --git a/CRM/Logging/Schema.php b/CRM/Logging/Schema.php index e9160bbdefe1..25b63413d254 100644 --- a/CRM/Logging/Schema.php +++ b/CRM/Logging/Schema.php @@ -158,11 +158,13 @@ public function __construct() { $nonStandardTableNameString = $this->getNonStandardTableNameFilterString(); if (defined('CIVICRM_LOGGING_DSN')) { - $dsn = DB::parseDSN(CIVICRM_LOGGING_DSN); + $dsn = CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN); + $dsn = DB::parseDSN($dsn); $this->useDBPrefix = (CIVICRM_LOGGING_DSN != CIVICRM_DSN); } else { - $dsn = DB::parseDSN(CIVICRM_DSN); + $dsn = CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsn = DB::parseDSN($dsn); $this->useDBPrefix = FALSE; } $this->db = $dsn['database']; diff --git a/CRM/Upgrade/Incremental/php/FourFive.php b/CRM/Upgrade/Incremental/php/FourFive.php index d905ea536b06..0f27a0be48ed 100644 --- a/CRM/Upgrade/Incremental/php/FourFive.php +++ b/CRM/Upgrade/Incremental/php/FourFive.php @@ -52,7 +52,8 @@ public function upgrade_4_5_alpha1($rev) { // if DB is been into upgrade for 3.4.2 version, it would have pdf_format_id name for FK // else FK_civicrm_msg_template_pdf_format_id $config = CRM_Core_Config::singleton(); - $dbUf = DB::parseDSN($config->dsn); + $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn); + $dbUf = DB::parseDSN($dsn); $query = " SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'civicrm_msg_template' diff --git a/CRM/Utils/File.php b/CRM/Utils/File.php index 67da4b10a665..c196b3dd7126 100644 --- a/CRM/Utils/File.php +++ b/CRM/Utils/File.php @@ -325,6 +325,7 @@ public static function runSqlQuery($dsn, $queryString, $prefix = NULL, $dieOnErr } else { require_once 'DB.php'; + $dsn = CRM_Utils_SQL::autoSwitchDSN($dsn); $db = DB::connect($dsn); } diff --git a/CRM/Utils/SQL.php b/CRM/Utils/SQL.php index 1cd1dc02b6e7..3b9167e6265f 100644 --- a/CRM/Utils/SQL.php +++ b/CRM/Utils/SQL.php @@ -185,4 +185,22 @@ public static function isSSLDSN(string $dsn):bool { return (bool) preg_match('/[\?&](key|cert|ca|capath|cipher|ssl)=/', $dsn); } + /** + * If DB_DSN_MODE is auto then we should replace mysql with mysqli if mysqli is available or the other way around as appropriate + * @param string $dsn + * + * @return string + */ + public static function autoSwitchDSN($dsn) { + if (defined('DB_DSN_MODE') && DB_DSN_MODE === 'auto') { + if (extension_loaded('mysqli')) { + $dsn = preg_replace('/^mysql:/', 'mysqli:', $dsn); + } + else { + $dsn = preg_replace('/^mysqli:/', 'mysql:', $dsn); + } + } + return $dsn; + } + } diff --git a/CRM/Utils/System/Backdrop.php b/CRM/Utils/System/Backdrop.php index d46e56146ce8..83b66078cd1c 100644 --- a/CRM/Utils/System/Backdrop.php +++ b/CRM/Utils/System/Backdrop.php @@ -291,9 +291,10 @@ protected function getUsersTableName() { public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) { $config = CRM_Core_Config::singleton(); - $dbBackdrop = DB::connect($config->userFrameworkDSN); + $ufDSN = CRM_Utils_SQL::autoSwitchDSN($config->userFrameworkDSN); + $dbBackdrop = DB::connect($ufDSN); if (DB::isError($dbBackdrop)) { - throw new CRM_Core_Exception("Cannot connect to Backdrop database via $config->userFrameworkDSN, " . $dbBackdrop->getMessage()); + throw new CRM_Core_Exception("Cannot connect to Backdrop database via $ufDSN, " . $dbBackdrop->getMessage()); } $account = $userUid = $userMail = NULL; diff --git a/CRM/Utils/System/Drupal.php b/CRM/Utils/System/Drupal.php index bf4f8693cf5c..710e17835585 100644 --- a/CRM/Utils/System/Drupal.php +++ b/CRM/Utils/System/Drupal.php @@ -317,9 +317,10 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP $config = CRM_Core_Config::singleton(); - $dbDrupal = DB::connect($config->userFrameworkDSN); + $ufDSN = CRM_Utils_SQL::autoSwitchDSN($config->userFrameworkDSN); + $dbDrupal = DB::connect($ufDSN); if (DB::isError($dbDrupal)) { - throw new CRM_Core_Exception("Cannot connect to drupal db via $config->userFrameworkDSN, " . $dbDrupal->getMessage()); + throw new CRM_Core_Exception("Cannot connect to drupal db via $ufDSN, " . $dbDrupal->getMessage()); } $account = $userUid = $userMail = NULL; diff --git a/CRM/Utils/System/Drupal6.php b/CRM/Utils/System/Drupal6.php index 0ac291164ef0..343ec7fe74e3 100644 --- a/CRM/Utils/System/Drupal6.php +++ b/CRM/Utils/System/Drupal6.php @@ -300,9 +300,10 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP $config = CRM_Core_Config::singleton(); - $dbDrupal = DB::connect($config->userFrameworkDSN); + $ufDSN = CRM_Utils_SQL::autoSwitchDSN($config->userFrameworkDSN); + $dbDrupal = DB::connect($ufDSN); if (DB::isError($dbDrupal)) { - throw new CRM_Core_Exception("Cannot connect to drupal db via $config->userFrameworkDSN, " . $dbDrupal->getMessage()); + throw new CRM_Core_Exception("Cannot connect to drupal db via $ufDSN, " . $dbDrupal->getMessage()); } $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; diff --git a/Civi/Test.php b/Civi/Test.php index 1d9db1207952..cf9c841ab798 100644 --- a/Civi/Test.php +++ b/Civi/Test.php @@ -59,7 +59,8 @@ public static function asPreInstall($callback) { public static function dsn($part = NULL) { if (!isset(self::$singletons['dsn'])) { require_once "DB.php"; - self::$singletons['dsn'] = \DB::parseDSN(CIVICRM_DSN); + $dsn = \CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + self::$singletons['dsn'] = \DB::parseDSN($dsn); } if ($part === NULL) { diff --git a/tests/phpunit/CiviTest/CiviUnitTestCase.php b/tests/phpunit/CiviTest/CiviUnitTestCase.php index 7d05958487b2..8f4550ec4401 100644 --- a/tests/phpunit/CiviTest/CiviUnitTestCase.php +++ b/tests/phpunit/CiviTest/CiviUnitTestCase.php @@ -228,7 +228,8 @@ public static function getDBName() { static $dbName = NULL; if ($dbName === NULL) { require_once "DB.php"; - $dsninfo = DB::parseDSN(CIVICRM_DSN); + $dsn = CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsninfo = DB::parseDSN($dsn); $dbName = $dsninfo['database']; } return $dbName; diff --git a/tests/phpunit/api/v3/SyntaxConformanceTest.php b/tests/phpunit/api/v3/SyntaxConformanceTest.php index 05819b0d2ff9..0f0e4003ac0c 100644 --- a/tests/phpunit/api/v3/SyntaxConformanceTest.php +++ b/tests/phpunit/api/v3/SyntaxConformanceTest.php @@ -578,7 +578,8 @@ public static function toBeSkipped_getSqlOperators() { // Re:^^^ => the failure was probably correct behavior, and test is now fixed, but yeah 5.5 is deprecated, and don't care enough to verify. // Test data providers should be able to run in pre-boot environment, so we connect directly to SQL server. require_once 'DB.php'; - $db = DB::connect(CIVICRM_DSN); + $dsn = CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $db = DB::connect($dsn); if ($db->connection instanceof mysqli && $db->connection->server_version < 50600) { $entitiesWithout[] = 'Dedupe'; } From dd118b15122acb052f4edd1a5b104aaba20f154c Mon Sep 17 00:00:00 2001 From: eileen Date: Fri, 28 Aug 2020 12:52:42 +1200 Subject: [PATCH 037/834] dev/core#1972 Fix tax_amount calclation on renewal form --- CRM/Financial/BAO/Order.php | 20 ++++++++++++++++--- .../CRM/Member/Form/MembershipRenewalTest.php | 4 ++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CRM/Financial/BAO/Order.php b/CRM/Financial/BAO/Order.php index 528cdcefb143..43fa9cd2d72a 100644 --- a/CRM/Financial/BAO/Order.php +++ b/CRM/Financial/BAO/Order.php @@ -236,18 +236,17 @@ protected function calculateLineItems(): array { $lineItems[$valueID] = CRM_Price_BAO_PriceSet::getLine($params, $throwAwayArray, $this->getPriceSetID(), $this->getPriceFieldSpec($fieldID), $fieldID, 0)[1][$valueID]; } - $taxRates = CRM_Core_PseudoConstant::getTaxRates(); foreach ($lineItems as &$lineItem) { // Set any pre-calculation to zero as we will calculate. $lineItem['tax_amount'] = 0; if ($this->getOverrideFinancialTypeID() !== FALSE) { $lineItem['financial_type_id'] = $this->getOverrideFinancialTypeID(); } - $taxRate = $taxRates[$lineItem['financial_type_id']] ?? 0; + $taxRate = $this->getTaxRate((int) $lineItem['financial_type_id']); if ($this->getOverrideTotalAmount() !== FALSE) { if ($taxRate) { // Total is tax inclusive. - $lineItem['tax_amount'] = ($taxRate / 100) * $this->getOverrideTotalAmount(); + $lineItem['tax_amount'] = ($taxRate / 100) * $this->getOverrideTotalAmount() / (1 + ($taxRate / 100)); $lineItem['line_total'] = $lineItem['unit_price'] = $this->getOverrideTotalAmount() - $lineItem['tax_amount']; } else { @@ -276,4 +275,19 @@ public function getTotalTaxAmount() :float { return $amount; } + /** + * Get the tax rate for the given financial type. + * + * @param int $financialTypeID + * + * @return float + */ + public function getTaxRate(int $financialTypeID) { + $taxRates = CRM_Core_PseudoConstant::getTaxRates(); + if (!isset($taxRates[$financialTypeID])) { + return 0; + } + return $taxRates[$financialTypeID]; + } + } diff --git a/tests/phpunit/CRM/Member/Form/MembershipRenewalTest.php b/tests/phpunit/CRM/Member/Form/MembershipRenewalTest.php index c990c6318f90..d70f497c2f1d 100644 --- a/tests/phpunit/CRM/Member/Form/MembershipRenewalTest.php +++ b/tests/phpunit/CRM/Member/Form/MembershipRenewalTest.php @@ -241,7 +241,7 @@ public function testSubmitWithTax() { ], 'credit_card_type' => 'Visa', 'billing_first_name' => 'Test', - 'billing_middlename' => 'Last', + 'billing_middle_name' => 'Last', 'billing_street_address-5' => '10 Test St', 'billing_city-5' => 'Test', 'billing_state_province_id-5' => '1003', @@ -250,7 +250,7 @@ public function testSubmitWithTax() { ]); $contribution = $this->callAPISuccessGetSingle('Contribution', ['contact_id' => $this->_individualId, 'is_test' => TRUE, 'return' => ['total_amount', 'tax_amount']]); $this->assertEquals(50, $contribution['total_amount']); - $this->assertEquals(5, $contribution['tax_amount']); + $this->assertEquals(4.55, $contribution['tax_amount']); } /** From 7e59b8450ef51a53f5487e7bb967fb77c1c9d05e Mon Sep 17 00:00:00 2001 From: eileen Date: Fri, 28 Aug 2020 14:06:57 +1200 Subject: [PATCH 038/834] dev/core#1974 Fix incorrect handling of serialize key when changing custom field type See https://lab.civicrm.org/dev/core/-/issues/1974 --- CRM/Custom/Form/ChangeFieldType.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CRM/Custom/Form/ChangeFieldType.php b/CRM/Custom/Form/ChangeFieldType.php index a47e75c4cdf7..24650919ae6c 100644 --- a/CRM/Custom/Form/ChangeFieldType.php +++ b/CRM/Custom/Form/ChangeFieldType.php @@ -146,13 +146,14 @@ public function postProcess() { $customField = new CRM_Core_DAO_CustomField(); $customField->id = $this->_id; $customField->find(TRUE); + $customField->serialize = in_array($dstHtmlType, $mutliValueOps, TRUE); if ($dstHtmlType == 'Text' && in_array($srcHtmlType, [ 'Select', 'Radio', 'Autocomplete-Select', ])) { - $customField->option_group_id = "NULL"; + $customField->option_group_id = 'NULL'; CRM_Core_BAO_CustomField::checkOptionGroup($this->_values['option_group_id']); } @@ -165,7 +166,7 @@ public function postProcess() { $this->firstValueToFlatten($tableName, $this->_values['column_name']); } - $customField->html_type = $dstHtmlType; + $customField->html_type = ($dstHtmlType === 'Multi-Select') ? 'Select' : $dstHtmlType; $customField->save(); // Reset cache for custom fields From 7b06299006eca9e130391362ef0e393a550aee34 Mon Sep 17 00:00:00 2001 From: Seamus Lee Date: Fri, 28 Aug 2020 12:44:15 +1000 Subject: [PATCH 039/834] Fix regen and update civicrm_generated --- bin/regen.sh | 2 +- sql/civicrm_generated.mysql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/regen.sh b/bin/regen.sh index e7525399ebdc..6cf3eb54c643 100755 --- a/bin/regen.sh +++ b/bin/regen.sh @@ -47,7 +47,7 @@ php GenerateData.php ## Prune local data $MYSQLCMD -e "DROP TABLE IF EXISTS civicrm_install_canary; DELETE FROM civicrm_cache; DELETE FROM civicrm_setting;" -$MYSQLCMD -e "DELETE FROM civicrm_extension WHERE full_name NOT IN ('sequentialcreditnotes', 'eventcart', 'search', 'flexmailer', 'financialacls);" +$MYSQLCMD -e "DELETE FROM civicrm_extension WHERE full_name NOT IN ('sequentialcreditnotes', 'eventcart', 'search', 'flexmailer', 'financialacls');" TABLENAMES=$( echo "show tables like 'civicrm_%'" | $MYSQLCMD | grep ^civicrm_ | xargs ) cd $CIVISOURCEDIR/sql diff --git a/sql/civicrm_generated.mysql b/sql/civicrm_generated.mysql index 111014800d61..cdb7b1a269d9 100644 --- a/sql/civicrm_generated.mysql +++ b/sql/civicrm_generated.mysql @@ -495,7 +495,7 @@ UNLOCK TABLES; LOCK TABLES `civicrm_extension` WRITE; /*!40000 ALTER TABLE `civicrm_extension` DISABLE KEYS */; -INSERT INTO `civicrm_extension` (`id`, `type`, `full_name`, `name`, `label`, `file`, `schema_version`, `is_active`) VALUES (1,'module','sequentialcreditnotes','Sequential credit notes','Sequential credit notes','sequentialcreditnotes',NULL,1),(2,'module','eventcart','Event cart','Event cart','eventcart',NULL,1); +INSERT INTO `civicrm_extension` (`id`, `type`, `full_name`, `name`, `label`, `file`, `schema_version`, `is_active`) VALUES (1,'module','sequentialcreditnotes','Sequential credit notes','Sequential credit notes','sequentialcreditnotes',NULL,1),(2,'module','eventcart','Event cart','Event cart','eventcart',NULL,1),(3,'module','financialacls','Financial ACLs','Financial ACLs','financialacls',NULL,1); /*!40000 ALTER TABLE `civicrm_extension` ENABLE KEYS */; UNLOCK TABLES; From 2aaedeaae3b0b7b0c99d7ed04bcf83ae837a30b3 Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Fri, 28 Aug 2020 09:38:52 +0530 Subject: [PATCH 040/834] added custom clause under whereClause and removed where function from bookkeeping, re-added receipt_date --- CRM/Report/Form.php | 4 +- CRM/Report/Form/Contribute/Bookkeeping.php | 64 +++++++--------------- 2 files changed, 24 insertions(+), 44 deletions(-) diff --git a/CRM/Report/Form.php b/CRM/Report/Form.php index d202b845611e..aaf8bb15fd2f 100644 --- a/CRM/Report/Form.php +++ b/CRM/Report/Form.php @@ -5937,7 +5937,9 @@ protected function generateFilterClause($field, $fieldName) { $relative = $this->_params["{$fieldName}_relative"] ?? NULL; $from = $this->_params["{$fieldName}_from"] ?? NULL; $to = $this->_params["{$fieldName}_to"] ?? NULL; - return $this->dateClause($field['dbAlias'], $relative, $from, $to, $field['type']); + $fromTime = $this->_params["{$fieldName}_from_time"] ?? NULL; + $toTime = $this->_params["{$fieldName}_to_time"] ?? NULL; + return $this->dateClause($field['dbAlias'], $relative, $from, $to, $field['type'], $fromTime, $toTime); } } else { diff --git a/CRM/Report/Form/Contribute/Bookkeeping.php b/CRM/Report/Form/Contribute/Bookkeeping.php index 70c7a4d857ce..06f89ab8c959 100644 --- a/CRM/Report/Form/Contribute/Bookkeeping.php +++ b/CRM/Report/Form/Contribute/Bookkeeping.php @@ -301,6 +301,7 @@ public function __construct() { 'type' => CRM_Utils_Type::T_INT, ], 'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATETIME], + 'receipt_date' => ['operatorType' => CRM_Report_Form::OP_DATETIME], 'contribution_source' => [ 'title' => ts('Source'), 'name' => 'source', @@ -517,54 +518,31 @@ public function orderBy() { } } - public function where() { - foreach ($this->_columns as $tableName => $table) { - if (array_key_exists('filters', $table)) { - foreach ($table['filters'] as $fieldName => $field) { - $clause = NULL; - if (in_array($fieldName, [ - 'credit_accounting_code', - 'credit_name', - 'credit_contact_id', - ])) { - $field['dbAlias'] = "CASE + /** + * overriding to modify dbAlias for few fields. + * + * @param array $field Field specifications + * @param string $op Query operator (not an exact match to sql) + * @param mixed $value + * @param float $min + * @param float $max + * + * @return null|string + */ + public function whereClause(&$field, $op, $value, $min, $max) { + if ($field['alias'] == 'financial_account_civireport_credit' && + in_array($field['name'], ['accounting_code', 'id', 'contact_id']) + ) { + $field['dbAlias'] = "CASE WHEN financial_trxn_civireport.from_financial_account_id IS NOT NULL THEN financial_account_civireport_credit_1.{$field['name']} ELSE financial_account_civireport_credit_2.{$field['name']} END"; - } - if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE) { - $relative = $this->_params["{$fieldName}_relative"] ?? NULL; - $from = $this->_params["{$fieldName}_from"] ?? NULL; - $to = $this->_params["{$fieldName}_to"] ?? NULL; - $fromTime = $this->_params["{$fieldName}_from_time"] ?? NULL; - $toTime = $this->_params["{$fieldName}_to_time"] ?? NULL; - - $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type'], $fromTime, $toTime); - } - else { - $op = $this->_params["{$fieldName}_op"] ?? NULL; - if ($op) { - $clause = $this->whereClause($field, - $op, - CRM_Utils_Array::value("{$fieldName}_value", $this->_params), - CRM_Utils_Array::value("{$fieldName}_min", $this->_params), - CRM_Utils_Array::value("{$fieldName}_max", $this->_params) - ); - } - } - if (!empty($clause)) { - $clauses[] = $clause; - } - } - } - } - if (empty($clauses)) { - $this->_where = 'WHERE ( 1 )'; - } - else { - $this->_where = 'WHERE ' . implode(' AND ', $clauses); } + + $clause = parent::whereClause($field, $op, $value, $min, $max); + + return $clause; } public function postProcess() { From 38019b927b0ee8ebfba11165e860146853c1161d Mon Sep 17 00:00:00 2001 From: eileen Date: Fri, 28 Aug 2020 15:49:22 +1200 Subject: [PATCH 041/834] Add line item acl tests --- ext/financialacls/phpunit.xml.dist | 18 +++ .../tests/phpunit/LineItemTest.php | 114 ++++++++++++++++++ ext/financialacls/tests/phpunit/bootstrap.php | 63 ++++++++++ 3 files changed, 195 insertions(+) create mode 100644 ext/financialacls/phpunit.xml.dist create mode 100644 ext/financialacls/tests/phpunit/LineItemTest.php create mode 100644 ext/financialacls/tests/phpunit/bootstrap.php diff --git a/ext/financialacls/phpunit.xml.dist b/ext/financialacls/phpunit.xml.dist new file mode 100644 index 000000000000..fc8f870b723b --- /dev/null +++ b/ext/financialacls/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + ./tests/phpunit + + + + + ./ + + + + + + + + diff --git a/ext/financialacls/tests/phpunit/LineItemTest.php b/ext/financialacls/tests/phpunit/LineItemTest.php new file mode 100644 index 000000000000..71eb6380c4d0 --- /dev/null +++ b/ext/financialacls/tests/phpunit/LineItemTest.php @@ -0,0 +1,114 @@ +installMe(__DIR__) + ->apply(); + } + + /** + * Test api applies permissions on line item actions (delete & get). + */ + public function testLineItemApiPermissions() { + $contact1 = $this->individualCreate(); + $defaultPriceFieldID = $this->getDefaultPriceFieldID(); + $this->callAPISuccess('Order', 'create', [ + 'financial_type_id' => 'Donation', + 'contact_id' => $contact1, + 'line_items' => [ + [ + 'line_item' => [ + [ + 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation'), + 'line_total' => 40, + 'price_field_id' => $defaultPriceFieldID, + 'qty' => 1, + ], + [ + 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Member Dues'), + 'line_total' => 50, + 'price_field_id' => $defaultPriceFieldID, + 'qty' => 1, + ], + ], + ], + ], + ]); + + $this->setPermissions([ + 'access CiviCRM', + 'access CiviContribute', + 'edit contributions', + 'delete in CiviContribute', + 'view contributions of type Donation', + 'delete contributions of type Donation', + ]); + Civi::settings()->set('acl_financial_type', TRUE); + $this->createLoggedInUser(); + + $lineItems = $this->callAPISuccess('LineItem', 'get', ['sequential' => TRUE])['values']; + $this->assertCount(2, $lineItems); + $this->callAPISuccessGetCount('LineItem', ['check_permissions' => TRUE], 1); + + $this->callAPISuccess('LineItem', 'Delete', ['check_permissions' => TRUE, 'id' => $lineItems[0]['id']]); + $this->callAPIFailure('LineItem', 'Delete', ['check_permissions' => TRUE, 'id' => $lineItems[1]['id']]); + } + + /** + * Set ACL permissions, overwriting any existing ones. + * + * @param array $permissions + * Array of permissions e.g ['access CiviCRM','access CiviContribute'], + */ + protected function setPermissions($permissions) { + CRM_Core_Config::singleton()->userPermissionClass->permissions = $permissions; + if (isset(\Civi::$statics['CRM_Financial_BAO_FinancialType'])) { + unset(\Civi::$statics['CRM_Financial_BAO_FinancialType']); + } + } + + /** + * @return mixed + * @throws \API_Exception + * @throws \Civi\API\Exception\UnauthorizedException + */ + protected function getDefaultPriceFieldID(): int { + return PriceField::get() + ->addWhere('price_set_id:name', '=', 'default_contribution_amount') + ->addWhere('name', '=', 'contribution_amount') + ->addWhere('html_type', '=', 'Text') + ->addSelect('id')->execute()->first()['id']; + } + +} diff --git a/ext/financialacls/tests/phpunit/bootstrap.php b/ext/financialacls/tests/phpunit/bootstrap.php new file mode 100644 index 000000000000..a5b49253c819 --- /dev/null +++ b/ext/financialacls/tests/phpunit/bootstrap.php @@ -0,0 +1,63 @@ +add('CRM_', __DIR__); +$loader->add('Civi\\', __DIR__); +$loader->add('api_', __DIR__); +$loader->add('api\\', __DIR__); +$loader->register(); + +/** + * Call the "cv" command. + * + * @param string $cmd + * The rest of the command to send. + * @param string $decode + * Ex: 'json' or 'phpcode'. + * @return string + * Response output (if the command executed normally). + * @throws \RuntimeException + * If the command terminates abnormally. + */ +function cv($cmd, $decode = 'json') { + $cmd = 'cv ' . $cmd; + $descriptorSpec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => STDERR); + $oldOutput = getenv('CV_OUTPUT'); + putenv("CV_OUTPUT=json"); + + // Execute `cv` in the original folder. This is a work-around for + // phpunit/codeception, which seem to manipulate PWD. + $cmd = sprintf('cd %s; %s', escapeshellarg(getenv('PWD')), $cmd); + + $process = proc_open($cmd, $descriptorSpec, $pipes, __DIR__); + putenv("CV_OUTPUT=$oldOutput"); + fclose($pipes[0]); + $result = stream_get_contents($pipes[1]); + fclose($pipes[1]); + if (proc_close($process) !== 0) { + throw new RuntimeException("Command failed ($cmd):\n$result"); + } + switch ($decode) { + case 'raw': + return $result; + + case 'phpcode': + // If the last output is /*PHPCODE*/, then we managed to complete execution. + if (substr(trim($result), 0, 12) !== "/*BEGINPHP*/" || substr(trim($result), -10) !== "/*ENDPHP*/") { + throw new \RuntimeException("Command failed ($cmd):\n$result"); + } + return $result; + + case 'json': + return json_decode($result, 1); + + default: + throw new RuntimeException("Bad decoder format ($decode)"); + } +} From caa0781633b1d5969dad176d0682b8e735589690 Mon Sep 17 00:00:00 2001 From: yashodha Date: Fri, 28 Aug 2020 17:13:56 +0530 Subject: [PATCH 042/834] remove dropped tables for activity target and assignment --- xml/schema/Activity/ActivityAssignment.xml | 62 ---------------------- xml/schema/Activity/ActivityTarget.xml | 61 --------------------- 2 files changed, 123 deletions(-) delete mode 100644 xml/schema/Activity/ActivityAssignment.xml delete mode 100644 xml/schema/Activity/ActivityTarget.xml diff --git a/xml/schema/Activity/ActivityAssignment.xml b/xml/schema/Activity/ActivityAssignment.xml deleted file mode 100644 index 192af7dc8aa7..000000000000 --- a/xml/schema/Activity/ActivityAssignment.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - CRM/Activity - ActivityAssignment - civicrm_activity_assignment - Activity assignments - 1.8 - true - - id - int unsigned - Activity Assignment ID - true - Activity assignment id - 1.8 - - - id - true - - - activity_id - int unsigned - Activity ID - true - Foreign key to the activity for this assignment. - 2.0 - - - activity_id -
civicrm_activity
- id - 2.0 - CASCADE - - - - assignee_contact_id - int unsigned - Assignee Contact ID - true - Foreign key to the contact for this assignment. - 2.0 - - - assignee_contact_id - civicrm_contact
- id - 2.0 - CASCADE -
- - - UI_activity_assignee_contact_id - assignee_contact_id - activity_id - true - 2.0 - - - - diff --git a/xml/schema/Activity/ActivityTarget.xml b/xml/schema/Activity/ActivityTarget.xml deleted file mode 100644 index 6b5cba7867d1..000000000000 --- a/xml/schema/Activity/ActivityTarget.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - CRM/Activity - ActivityTarget - civicrm_activity_target - Activity targets - 2.0 - true - - id - int unsigned - Activity Target ID - true - Activity target id - 2.0 - - - id - true - - - activity_id - int unsigned - Activity ID - true - Foreign key to the activity for this target. - 2.0 - - - activity_id -
civicrm_activity
- id - 2.0 - CASCADE - - - - target_contact_id - Contact ID (match to contact) - true - int unsigned - true - Foreign key to the contact for this target. - 2.0 - - - target_contact_id - civicrm_contact
- id - 2.0 - CASCADE -
- - - UI_activity_target_contact_id - target_contact_id - activity_id - true - 2.0 - - From e7afe635e1779db6038e9bcb4ba6806f2bca2554 Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Fri, 28 Aug 2020 19:59:56 +0530 Subject: [PATCH 043/834] report#48 View Payment owned by Different contact on Membership and Participant View --- CRM/Contribute/Form/Search.php | 8 +++++--- CRM/Event/Page/Tab.php | 2 +- CRM/Member/Page/Tab.php | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CRM/Contribute/Form/Search.php b/CRM/Contribute/Form/Search.php index 4f1009b41a07..341c54c868d4 100644 --- a/CRM/Contribute/Form/Search.php +++ b/CRM/Contribute/Form/Search.php @@ -386,12 +386,14 @@ public function fixFormValues() { $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this); - if ($cid) { + // skip cid (contact id of membership/participant record) to get associated payments for membership/participant record, + // contribution record may be on different contact id. + $skip_cid = CRM_Utils_Request::retrieve('skip_cid', 'Boolean', $this, FALSE, FALSE); + + if ($cid && !$skip_cid) { $cid = CRM_Utils_Type::escape($cid, 'Integer'); if ($cid > 0) { $this->_formValues['contact_id'] = $cid; - // @todo - why do we retrieve these when they are not used? - list($display, $image) = CRM_Contact_BAO_Contact::getDisplayAndImage($cid); $this->_defaults['sort_name'] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $cid, 'sort_name' ); diff --git a/CRM/Event/Page/Tab.php b/CRM/Event/Page/Tab.php index ab29c9ce3577..0ecd0f80631e 100644 --- a/CRM/Event/Page/Tab.php +++ b/CRM/Event/Page/Tab.php @@ -287,7 +287,7 @@ public function associatedContribution() { ); $controller->setEmbedded(TRUE); $controller->set('force', 1); - $controller->set('cid', $this->_contactId); + $controller->set('skip_cid', TRUE); $controller->set('participantId', $this->_id); $controller->set('context', 'contribution'); $controller->process(); diff --git a/CRM/Member/Page/Tab.php b/CRM/Member/Page/Tab.php index ec2300d0ba5d..123f1ce953c4 100644 --- a/CRM/Member/Page/Tab.php +++ b/CRM/Member/Page/Tab.php @@ -590,7 +590,7 @@ public static function associatedContribution($contactId = NULL, $membershipId = $controller->setEmbedded(TRUE); $controller->reset(); $controller->set('force', 1); - $controller->set('cid', $contactId); + $controller->set('skip_cid', TRUE); $controller->set('memberId', $membershipId); $controller->set('context', 'contribution'); $controller->process(); From 36cf2d7eecbc466b5725e7de164397a012107af2 Mon Sep 17 00:00:00 2001 From: Aidan Saunders Date: Fri, 28 Aug 2020 15:45:59 +0100 Subject: [PATCH 044/834] Use !empty() instead of isset() in constructor so that empty strings don't confuse the logic Update array syntax in comments Add test for class.api.php --- api/class.api.php | 24 +++++++------- tests/phpunit/api/v3/ClassAPITest.php | 48 +++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 tests/phpunit/api/v3/ClassAPITest.php diff --git a/api/class.api.php b/api/class.api.php index 4ba61dd30bde..df6ef1283f4c 100644 --- a/api/class.api.php +++ b/api/class.api.php @@ -14,15 +14,15 @@ * ``` * require_once('/your/civi/folder/api/class.api.php'); * // the path to civicrm.settings.php - * $api = new civicrm_api3 (array('conf_path'=> '/your/path/to/your/civicrm/or/joomla/site)); + * $api = new civicrm_api3 (['conf_path'=> '/your/path/to/your/civicrm/or/joomla/site']); * ``` * * or to query a remote server via the rest api * * ``` - * $api = new civicrm_api3 (array ('server' => 'http://example.org', - * 'api_key'=>'theusersecretkey', - * 'key'=>'thesitesecretkey')); + * $api = new civicrm_api3 (['server' => 'http://example.org', + * 'api_key'=>'theusersecretkey', + * 'key'=>'thesitesecretkey']); * ``` * * No matter how initialised and if civicrm is local or remote, you use the class the same way. @@ -34,7 +34,7 @@ * So, to get the individual contacts: * * ``` - * if ($api->Contact->Get(array('contact_type'=>'Individual','return'=>'sort_name,current_employer')) { + * if ($api->Contact->Get(['contact_type'=>'Individual','return'=>'sort_name,current_employer']) { * // each key of the result array is an attribute of the api * echo "\n contacts found " . $api->count; * foreach ($api->values as $c) { @@ -49,7 +49,7 @@ * Or, to create an event: * * ``` - * if ($api->Event->Create(array('title'=>'Test','event_type_id' => 1,'is_public' => 1,'start_date' => 19430429))) { + * if ($api->Event->Create(['title'=>'Test','event_type_id' => 1,'is_public' => 1,'start_date' => 19430429])) { * echo "created event id:". $api->id; * } else { * echo $api->errorMsg(); @@ -62,7 +62,7 @@ * * ``` * $api->Activity->Get (42); - * $api->Activity->Get (array('id'=>42)); + * $api->Activity->Get (['id'=>42]); * ``` * * @@ -87,23 +87,23 @@ public function __construct($config = NULL) { $this->local = TRUE; $this->input = []; $this->lastResult = []; - if (isset($config) && isset($config['server'])) { + if (!empty($config) && !empty($config['server'])) { // we are calling a remote server via REST $this->local = FALSE; $this->uri = $config['server']; - if (isset($config['path'])) { + if (!empty($config['path'])) { $this->uri .= "/" . $config['path']; } else { $this->uri .= '/sites/all/modules/civicrm/extern/rest.php'; } - if (isset($config['key'])) { + if (!empty($config['key'])) { $this->key = $config['key']; } else { die("\nFATAL:param['key] missing\n"); } - if (isset($config['api_key'])) { + if (!empty($config['api_key'])) { $this->api_key = $config['api_key']; } else { @@ -111,7 +111,7 @@ public function __construct($config = NULL) { } return; } - if (isset($config) && isset($config['conf_path'])) { + if (!empty($config) && !empty($config['conf_path'])) { if (!defined('CIVICRM_SETTINGS_PATH')) { define('CIVICRM_SETTINGS_PATH', $config['conf_path'] . '/civicrm.settings.php'); } diff --git a/tests/phpunit/api/v3/ClassAPITest.php b/tests/phpunit/api/v3/ClassAPITest.php new file mode 100644 index 000000000000..0bd35bb7661d --- /dev/null +++ b/tests/phpunit/api/v3/ClassAPITest.php @@ -0,0 +1,48 @@ +assertEquals(TRUE, $api->local); + + // Be sure to include keys otherwise these calls die() + $keys = ['key' => 'my_site_key', 'api_key' => 'my_api_key']; + + // Check empty server string is local + $api = new civicrm_api3(['server' => ''] + $keys); + $this->assertEquals(TRUE, $api->local); + + // Check non-empty server string is remote, check default uri + $api = new civicrm_api3(['server' => 'http://my_server'] + $keys); + $this->assertEquals(FALSE, $api->local); + $this->assertEquals('http://my_server/sites/all/modules/civicrm/extern/rest.php', $api->uri); + + // Check path in uri + $api = new civicrm_api3(['server' => 'http://my_server', 'path' => 'wibble'] + $keys); + $this->assertEquals('http://my_server/wibble', $api->uri); + + } + +} From 9d2afe25e2566c1fede83ba9e61fd74f49cd86dc Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Fri, 28 Aug 2020 13:07:08 -0400 Subject: [PATCH 045/834] APIv4 - Move list of accepted query operators to CoreUtil --- CRM/Api4/Page/Api4Explorer.php | 3 ++- CRM/Core/DAO.php | 2 +- Civi/Api4/Generic/AbstractQueryAction.php | 6 ++++-- Civi/Api4/Generic/DAOGetAction.php | 3 ++- Civi/Api4/Query/Api4SelectQuery.php | 2 +- Civi/Api4/Utils/CoreUtil.php | 7 +++++++ ext/search/CRM/Search/Page/Ang.php | 2 +- 7 files changed, 18 insertions(+), 7 deletions(-) diff --git a/CRM/Api4/Page/Api4Explorer.php b/CRM/Api4/Page/Api4Explorer.php index 3daba0733836..6de079bb8f15 100644 --- a/CRM/Api4/Page/Api4Explorer.php +++ b/CRM/Api4/Page/Api4Explorer.php @@ -11,6 +11,7 @@ */ use Civi\Api4\Service\Schema\Joinable\Joinable; +use Civi\Api4\Utils\CoreUtil; /** * @@ -30,7 +31,7 @@ public function run() { }); } $vars = [ - 'operators' => \CRM_Core_DAO::acceptedSQLOperators(), + 'operators' => CoreUtil::getOperators(), 'basePath' => Civi::resources()->getUrl('civicrm'), 'schema' => (array) \Civi\Api4\Entity::get()->setChain(['fields' => ['$name', 'getFields']])->execute(), 'links' => $entityLinks, diff --git a/CRM/Core/DAO.php b/CRM/Core/DAO.php index 4c33f8002ec3..d62fba1401e7 100644 --- a/CRM/Core/DAO.php +++ b/CRM/Core/DAO.php @@ -2830,7 +2830,7 @@ public static function createSQLFilter($fieldName, $filter, $type = NULL, $alias /** * @see http://issues.civicrm.org/jira/browse/CRM-9150 * support for other syntaxes is discussed in ticket but being put off for now - * @return array + * @return string[] */ public static function acceptedSQLOperators() { return [ diff --git a/Civi/Api4/Generic/AbstractQueryAction.php b/Civi/Api4/Generic/AbstractQueryAction.php index ed7ac9bf374d..85ebcfb5edd5 100644 --- a/Civi/Api4/Generic/AbstractQueryAction.php +++ b/Civi/Api4/Generic/AbstractQueryAction.php @@ -19,6 +19,8 @@ namespace Civi\Api4\Generic; +use Civi\Api4\Utils\CoreUtil; + /** * Base class for all actions that need to fetch records (`Get`, `Update`, `Delete`, etc.). * @@ -86,7 +88,7 @@ abstract class AbstractQueryAction extends AbstractAction { * @throws \API_Exception */ public function addWhere(string $fieldName, string $op, $value = NULL) { - if (!in_array($op, \CRM_Core_DAO::acceptedSQLOperators())) { + if (!in_array($op, CoreUtil::getOperators())) { throw new \API_Exception('Unsupported operator'); } $this->where[] = [$fieldName, $op, $value]; @@ -145,7 +147,7 @@ protected function whereClauseToString($whereClause = NULL, $op = 'AND') { } return $output . '(' . $this->whereClauseToString($whereClause, $op) . ')'; } - elseif (isset($whereClause[1]) && in_array($whereClause[1], \CRM_Core_DAO::acceptedSQLOperators())) { + elseif (isset($whereClause[1]) && in_array($whereClause[1], CoreUtil::getOperators())) { $output = $whereClause[0] . ' ' . $whereClause[1] . ' '; if (isset($whereClause[2])) { $output .= is_array($whereClause[2]) ? '[' . implode(', ', $whereClause[2]) . ']' : $whereClause[2]; diff --git a/Civi/Api4/Generic/DAOGetAction.php b/Civi/Api4/Generic/DAOGetAction.php index d6a3f10eea2a..852a333ccff0 100644 --- a/Civi/Api4/Generic/DAOGetAction.php +++ b/Civi/Api4/Generic/DAOGetAction.php @@ -20,6 +20,7 @@ namespace Civi\Api4\Generic; use Civi\Api4\Query\Api4SelectQuery; +use Civi\Api4\Utils\CoreUtil; /** * Retrieve $ENTITIES based on criteria specified in the `where` parameter. @@ -154,7 +155,7 @@ public function addGroupBy(string $field) { * @throws \API_Exception */ public function addHaving(string $expr, string $op, $value = NULL) { - if (!in_array($op, \CRM_Core_DAO::acceptedSQLOperators())) { + if (!in_array($op, CoreUtil::getOperators())) { throw new \API_Exception('Unsupported operator'); } $this->having[] = [$expr, $op, $value]; diff --git a/Civi/Api4/Query/Api4SelectQuery.php b/Civi/Api4/Query/Api4SelectQuery.php index 5721e919688e..6198b93603c2 100644 --- a/Civi/Api4/Query/Api4SelectQuery.php +++ b/Civi/Api4/Query/Api4SelectQuery.php @@ -362,7 +362,7 @@ protected function treeWalkClauses($clause, $type) { protected function composeClause(array $clause, string $type) { // Pad array for unary operators list($expr, $operator, $value) = array_pad($clause, 3, NULL); - if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { + if (!in_array($operator, CoreUtil::getOperators(), TRUE)) { throw new \API_Exception('Illegal operator'); } diff --git a/Civi/Api4/Utils/CoreUtil.php b/Civi/Api4/Utils/CoreUtil.php index 8d06ce96789b..9bc7ec9b6a42 100644 --- a/Civi/Api4/Utils/CoreUtil.php +++ b/Civi/Api4/Utils/CoreUtil.php @@ -71,4 +71,11 @@ public static function getApiNameFromTableName($tableName) { return $entityName; } + /** + * @return string[] + */ + public static function getOperators() { + return \CRM_Core_DAO::acceptedSQLOperators(); + } + } diff --git a/ext/search/CRM/Search/Page/Ang.php b/ext/search/CRM/Search/Page/Ang.php index 069dc0ee973f..936f92edd619 100644 --- a/ext/search/CRM/Search/Page/Ang.php +++ b/ext/search/CRM/Search/Page/Ang.php @@ -32,7 +32,7 @@ public function run() { // Add client-side vars for the search UI $vars = [ - 'operators' => \CRM_Core_DAO::acceptedSQLOperators(), + 'operators' => \Civi\Api4\Utils\CoreUtil::getOperators(), 'schema' => $this->schema, 'links' => $this->getLinks(), 'loadOptions' => $this->loadOptions, From b82fb202e3d9e75563e45e22df7429fbf6793eae Mon Sep 17 00:00:00 2001 From: eileen Date: Fri, 28 Aug 2020 16:08:56 +1200 Subject: [PATCH 046/834] Move (now tested) delete acl check from v3 api to pre delete hook --- api/v3/LineItem.php | 11 ++--------- ext/financialacls/financialacls.php | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/api/v3/LineItem.php b/api/v3/LineItem.php index a5e5f34ff37d..f5b47a5e86f9 100644 --- a/api/v3/LineItem.php +++ b/api/v3/LineItem.php @@ -75,18 +75,11 @@ function civicrm_api3_line_item_get($params) { * * @param array $params * Array containing id of the group to be deleted. + * * @return array API result array * @throws API_Exception + * @throws \CiviCRM_API3_Exception */ function civicrm_api3_line_item_delete($params) { - if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus() && !empty($params['check_permissions'])) { - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types, CRM_Core_Action::DELETE); - if (empty($params['financial_type_id'])) { - $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_LineItem', $params['id'], 'financial_type_id'); - } - if (!in_array($params['financial_type_id'], array_keys($types))) { - throw new API_Exception('You do not have permission to delete this line item'); - } - } return _civicrm_api3_basic_delete(_civicrm_api3_get_BAO(__FUNCTION__), $params); } diff --git a/ext/financialacls/financialacls.php b/ext/financialacls/financialacls.php index e6c39a0ab068..dda05b28f659 100644 --- a/ext/financialacls/financialacls.php +++ b/ext/financialacls/financialacls.php @@ -143,6 +143,31 @@ function financialacls_civicrm_themes(&$themes) { _financialacls_civix_civicrm_themes($themes); } +/** + * Intervene to prevent deletion, where permissions block it. + * + * @param \CRM_Core_DAO $op + * @param string $objectName + * @param int|null $id + * @param array $params + * + * @throws \API_Exception + * @throws \CRM_Core_Exception + */ +function financialacls_civicrm_pre($op, $objectName, $id, &$params) { + if ($objectName === 'LineItem' && $op === 'delete' && !empty($params['check_permissions'])) { + if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) { + CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types, CRM_Core_Action::DELETE); + if (empty($params['financial_type_id'])) { + $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_LineItem', $params['id'], 'financial_type_id'); + } + if (!in_array($params['financial_type_id'], array_keys($types))) { + throw new API_Exception('You do not have permission to delete this line item'); + } + } + } +} + // --- Functions below this ship commented out. Uncomment as required. --- /** From 39deabd60329563c9808673f3c30d25883960f2e Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Fri, 28 Aug 2020 20:56:16 -0400 Subject: [PATCH 047/834] APIv4 - Add CONTAINS operator and add to search extension --- .../Generic/Traits/ArrayQueryActionTrait.php | 9 ++++ Civi/Api4/Query/Api4SelectQuery.php | 34 ++++++++++++--- Civi/Api4/Utils/CoreUtil.php | 4 +- ext/search/ang/search/crmSearch.component.js | 3 ++ .../ang/search/crmSearchValue.directive.js | 2 +- .../api/v4/Action/BasicActionsTest.php | 43 +++++++++++++++++++ .../api/v4/Action/PseudoconstantTest.php | 26 +++++++++++ 7 files changed, 114 insertions(+), 7 deletions(-) diff --git a/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php b/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php index 4ae6096c5039..fd515b9a4b71 100644 --- a/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php +++ b/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php @@ -158,6 +158,15 @@ private function filterCompare($row, $condition) { case 'NOT IN': return !in_array($value, $expected); + case 'CONTAINS': + if (is_array($value)) { + return in_array($expected, $value); + } + elseif (is_string($value) || is_numeric($value)) { + return strpos((string) $value, (string) $expected) !== FALSE; + } + return $value == $expected; + default: throw new NotImplementedException("Unsupported operator: '$operator' cannot be used with array data"); } diff --git a/Civi/Api4/Query/Api4SelectQuery.php b/Civi/Api4/Query/Api4SelectQuery.php index 6198b93603c2..e4f8ceb187fc 100644 --- a/Civi/Api4/Query/Api4SelectQuery.php +++ b/Civi/Api4/Query/Api4SelectQuery.php @@ -26,8 +26,8 @@ * Leaf operators are one of: * * * '=', '<=', '>=', '>', '<', 'LIKE', "<>", "!=", - * * "NOT LIKE", 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', - * * 'IS NOT NULL', or 'IS NULL'. + * * 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', + * * 'IS NOT NULL', or 'IS NULL', 'CONTAINS'. */ class Api4SelectQuery { @@ -379,7 +379,8 @@ protected function composeClause(array $clause, string $type) { $fieldAlias = $expr; // Attempt to format if this is a real field if (isset($this->apiFieldSpec[$expr])) { - FormattingUtil::formatInputValue($value, $expr, $this->apiFieldSpec[$expr]); + $field = $this->getField($expr); + FormattingUtil::formatInputValue($value, $expr, $field); } } // Expr references a non-field expression like a function; convert to alias @@ -392,7 +393,8 @@ protected function composeClause(array $clause, string $type) { foreach ($this->selectAliases as $selectAlias => $selectExpr) { list($selectField) = explode(':', $selectAlias); if ($selectAlias === $selectExpr && $fieldName === $selectField && isset($this->apiFieldSpec[$fieldName])) { - FormattingUtil::formatInputValue($value, $expr, $this->apiFieldSpec[$fieldName]); + $field = $this->getField($fieldName); + FormattingUtil::formatInputValue($value, $expr, $field); $fieldAlias = $selectAlias; break; } @@ -415,7 +417,29 @@ protected function composeClause(array $clause, string $type) { return sprintf('%s %s %s', $fieldAlias, $operator, $valExpr->render($this->apiFieldSpec)); } elseif ($fieldName) { - FormattingUtil::formatInputValue($value, $fieldName, $this->apiFieldSpec[$fieldName]); + $field = $this->getField($fieldName); + FormattingUtil::formatInputValue($value, $fieldName, $field); + } + } + + if ($operator === 'CONTAINS') { + switch ($field['serialize'] ?? NULL) { + case \CRM_Core_DAO::SERIALIZE_JSON: + $operator = 'LIKE'; + $value = '%"' . $value . '"%'; + // FIXME: Use this instead of the above hack once MIN_INSTALL_MYSQL_VER is bumped to 5.7. + // return sprintf('JSON_SEARCH(%s, "one", "%s") IS NOT NULL', $fieldAlias, \CRM_Core_DAO::escapeString($value)); + break; + + case \CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND: + $operator = 'LIKE'; + $value = '%' . \CRM_Core_DAO::VALUE_SEPARATOR . $value . \CRM_Core_DAO::VALUE_SEPARATOR . '%'; + break; + + default: + $operator = 'LIKE'; + $value = '%' . $value . '%'; + break; } } diff --git a/Civi/Api4/Utils/CoreUtil.php b/Civi/Api4/Utils/CoreUtil.php index 9bc7ec9b6a42..b9090c97dec2 100644 --- a/Civi/Api4/Utils/CoreUtil.php +++ b/Civi/Api4/Utils/CoreUtil.php @@ -75,7 +75,9 @@ public static function getApiNameFromTableName($tableName) { * @return string[] */ public static function getOperators() { - return \CRM_Core_DAO::acceptedSQLOperators(); + $operators = \CRM_Core_DAO::acceptedSQLOperators(); + $operators[] = 'CONTAINS'; + return $operators; } } diff --git a/ext/search/ang/search/crmSearch.component.js b/ext/search/ang/search/crmSearch.component.js index e2d8c564d15e..51f2532bc49a 100644 --- a/ext/search/ang/search/crmSearch.component.js +++ b/ext/search/ang/search/crmSearch.component.js @@ -340,6 +340,9 @@ else if (type === 'Money') { return CRM.formatMoney(value); } + if (_.isArray(value)) { + return value.join(', '); + } return value; } diff --git a/ext/search/ang/search/crmSearchValue.directive.js b/ext/search/ang/search/crmSearchValue.directive.js index 0fb160088402..ad93d2b8788c 100644 --- a/ext/search/ang/search/crmSearchValue.directive.js +++ b/ext/search/ang/search/crmSearchValue.directive.js @@ -39,7 +39,7 @@ if (_.includes(['=', '!=', '<>', '>', '>=', '<', '<='], op)) { $el.crmDatepicker({time: (field.input_attrs && field.input_attrs.time) || false}); } - } else if (_.includes(['=', '!=', '<>', 'IN', 'NOT IN'], op) && (field.fk_entity || field.options || dataType === 'Boolean')) { + } else if (_.includes(['=', '!=', '<>', 'IN', 'NOT IN', 'CONTAINS'], op) && (field.fk_entity || field.options || dataType === 'Boolean')) { if (field.options) { if (field.options === true) { $el.addClass('loading'); diff --git a/tests/phpunit/api/v4/Action/BasicActionsTest.php b/tests/phpunit/api/v4/Action/BasicActionsTest.php index 99d704a18f99..8df90eb87782 100644 --- a/tests/phpunit/api/v4/Action/BasicActionsTest.php +++ b/tests/phpunit/api/v4/Action/BasicActionsTest.php @@ -255,6 +255,49 @@ public function testWildcardSelect() { $this->assertEquals(['shape', 'size', 'weight'], array_keys($result)); } + public function testContainsOperator() { + MockBasicEntity::delete()->addWhere('id', '>', 0)->execute(); + + $records = [ + ['group' => 'one', 'fruit:name' => ['apple', 'pear'], 'weight' => 11], + ['group' => 'two', 'fruit:name' => ['pear', 'banana'], 'weight' => 12], + ]; + MockBasicEntity::save()->setRecords($records)->execute(); + + $result = MockBasicEntity::get() + ->addWhere('fruit:name', 'CONTAINS', 'apple') + ->execute(); + $this->assertCount(1, $result); + $this->assertEquals('one', $result->first()['group']); + + $result = MockBasicEntity::get() + ->addWhere('fruit:name', 'CONTAINS', 'pear') + ->execute(); + $this->assertCount(2, $result); + + $result = MockBasicEntity::get() + ->addWhere('group', 'CONTAINS', 'o') + ->execute(); + $this->assertCount(2, $result); + + $result = MockBasicEntity::get() + ->addWhere('weight', 'CONTAINS', 1) + ->execute(); + $this->assertCount(2, $result); + + $result = MockBasicEntity::get() + ->addWhere('fruit:label', 'CONTAINS', 'Banana') + ->execute(); + $this->assertCount(1, $result); + $this->assertEquals('two', $result->first()['group']); + + $result = MockBasicEntity::get() + ->addWhere('weight', 'CONTAINS', 2) + ->execute(); + $this->assertCount(1, $result); + $this->assertEquals('two', $result->first()['group']); + } + public function testPseudoconstantMatch() { MockBasicEntity::delete()->addWhere('id', '>', 0)->execute(); diff --git a/tests/phpunit/api/v4/Action/PseudoconstantTest.php b/tests/phpunit/api/v4/Action/PseudoconstantTest.php index 9f7e363941de..248073ab8d42 100644 --- a/tests/phpunit/api/v4/Action/PseudoconstantTest.php +++ b/tests/phpunit/api/v4/Action/PseudoconstantTest.php @@ -27,6 +27,7 @@ use Civi\Api4\Email; use Civi\Api4\EntityTag; use Civi\Api4\OptionValue; +use Civi\Api4\Participant; use Civi\Api4\Tag; /** @@ -273,4 +274,29 @@ public function testTagOptions() { $this->assertEquals($tag, $options[$tag]['label']); } + public function testParticipantRole() { + $event = $this->createEntity(['type' => 'Event']); + $contact = $this->createEntity(['type' => 'Individual']); + $participant = Participant::create() + ->addValue('contact_id', $contact['id']) + ->addValue('event_id', $event['id']) + ->addValue('role_id:label', ['Attendee', 'Volunteer']) + ->execute()->first(); + + $search1 = Participant::get() + ->addSelect('role_id', 'role_id:label') + ->addWhere('role_id:label', 'CONTAINS', 'Volunteer') + ->addOrderBy('id') + ->execute()->last(); + + $this->assertEquals(['Attendee', 'Volunteer'], $search1['role_id:label']); + $this->assertEquals(['1', '2'], $search1['role_id']); + + $search2 = Participant::get() + ->addWhere('role_id:label', 'CONTAINS', 'Host') + ->execute()->indexBy('id'); + + $this->assertArrayNotHasKey($participant['id'], (array) $search2); + } + } From c8025280ade6c1dd6d6f78380c992d3621311a31 Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Sat, 29 Aug 2020 08:22:30 +0530 Subject: [PATCH 048/834] user-interface#30 Ability to Send Invoice with modified subject and CC --- CRM/Contribute/Form/Task/Invoice.php | 11 +++++++++++ CRM/Core/BAO/MessageTemplate.php | 5 +++++ templates/CRM/Contribute/Form/Task/Invoice.tpl | 12 ++++++++++++ 3 files changed, 28 insertions(+) diff --git a/CRM/Contribute/Form/Task/Invoice.php b/CRM/Contribute/Form/Task/Invoice.php index b2087a647462..419cf483eb05 100644 --- a/CRM/Contribute/Form/Task/Invoice.php +++ b/CRM/Contribute/Form/Task/Invoice.php @@ -143,6 +143,9 @@ public function buildQuickForm() { $this->addRule('from_email_address', ts('From Email Address is required'), 'required'); } + $attributes = ['class' => 'huge']; + $this->add('text', 'cc_id', ts('CC'), $attributes); + $this->add('text', 'subject', ts('Subject'), $attributes + ['placeholder' => ts('Optional')]); $this->add('wysiwyg', 'email_comment', ts('If you would like to add personal message to email please add it here. (If sending to more then one receipient the same message will be sent to each contact.)'), [ 'rows' => 2, 'cols' => 40, @@ -413,6 +416,14 @@ public static function printPDF($contribIDs, &$params, $contactIds) { // from email address $fromEmailAddress = $params['from_email_address'] ?? NULL; + if (!empty($params['cc_id'])) { + $values['cc_receipt'] = $params['cc_id']; + } + + // get subject from UI + if (!empty($params['subject'])) { + $sendTemplateParams['subject'] = $values['subject'] = $params['subject']; + } // condition to check for download PDF Invoice or email Invoice if ($invoiceElements['createPdf']) { diff --git a/CRM/Core/BAO/MessageTemplate.php b/CRM/Core/BAO/MessageTemplate.php index 4dedb8c66e7d..bc683a8ec047 100644 --- a/CRM/Core/BAO/MessageTemplate.php +++ b/CRM/Core/BAO/MessageTemplate.php @@ -464,6 +464,11 @@ public static function sendTemplate($params) { $mailContent['html'] = preg_replace('/html}", $mailContent['html']); } + // Overwrite subject from form field + if (!empty($params['subject'])) { + $mailContent['subject'] = $params['subject']; + } + // replace tokens in the three elements (in subject as if it was the text body) $domain = CRM_Core_BAO_Domain::getDomain(); $hookTokens = []; diff --git a/templates/CRM/Contribute/Form/Task/Invoice.tpl b/templates/CRM/Contribute/Form/Task/Invoice.tpl index 9f3dde88890e..777354f29f06 100644 --- a/templates/CRM/Contribute/Form/Task/Invoice.tpl +++ b/templates/CRM/Contribute/Form/Task/Invoice.tpl @@ -28,6 +28,18 @@ {$form.from_email_address.label} {$form.from_email_address.html} {help id="id-from_email" file="CRM/Contact/Form/Task/Email.hlp" isAdmin=$isAdmin} + + {$form.cc_id.label} + + {$form.cc_id.html} + + + + {$form.subject.label} + + {$form.subject.html} + + {$form.email_comment.label} {$form.email_comment.html} From 5d4c1098fef46f9515c1f583974b69071cacf465 Mon Sep 17 00:00:00 2001 From: eileen Date: Fri, 28 Aug 2020 17:18:00 +1200 Subject: [PATCH 049/834] Fix test set up to call Order.create to create the correct line items This test is not creating valid line items - so when we expect them to be correct - they aren't --- tests/phpunit/api/v3/ContributionTest.php | 49 +++++++++++++---------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/tests/phpunit/api/v3/ContributionTest.php b/tests/phpunit/api/v3/ContributionTest.php index bf24317b8868..7b2f64bf7b24 100644 --- a/tests/phpunit/api/v3/ContributionTest.php +++ b/tests/phpunit/api/v3/ContributionTest.php @@ -179,7 +179,7 @@ public function testGetContribution() { $contribution2 = $this->callAPISuccess('contribution', 'create', $p); // Now we have 2 - test getcount. - $contribution = $this->callAPISuccess('contribution', 'getcount', []); + $contribution = $this->callAPISuccess('contribution', 'getcount'); $this->assertEquals(2, $contribution); // Test id only format. $contribution = $this->callAPISuccess('contribution', 'get', [ @@ -3562,17 +3562,12 @@ public function cleanUpAfterPriceSets() { * * @param int $priceFieldValueID * @param array $contriParams + * + * @throws \CRM_Core_Exception */ public function setUpPendingContribution($priceFieldValueID, $contriParams = []) { $contactID = $this->individualCreate(); - $membership = $this->callAPISuccess('membership', 'create', [ - 'contact_id' => $contactID, - 'membership_type_id' => $this->_ids['membership_type'], - 'start_date' => 'yesterday - 1 year', - 'end_date' => 'yesterday', - 'join_date' => 'yesterday - 1 year', - ]); - $contribution = $this->callAPISuccess('contribution', 'create', array_merge([ + $contribution = $this->callAPISuccess('Order', 'create', array_merge([ 'domain_id' => 1, 'contact_id' => $contactID, 'receive_date' => date('Ymd'), @@ -3585,23 +3580,33 @@ public function setUpPendingContribution($priceFieldValueID, $contriParams = []) 'source' => 'SSF', 'contribution_status_id' => 2, 'contribution_page_id' => $this->_ids['contribution_page'], - 'api.membership_payment.create' => ['membership_id' => $membership['id']], + 'line_items' => [ + [ + 'line_item' => [ + [ + 'price_field_id' => $this->_ids['price_field'][0], + 'qty' => 1, + 'entity_table' => 'civicrm_membership', + 'unit_price' => 20, + 'line_total' => 20, + 'financial_type_id' => 1, + 'price_field_value_id' => $priceFieldValueID, + ], + ], + 'params' => [ + 'contact_id' => $contactID, + 'membership_type_id' => $this->_ids['membership_type'], + 'start_date' => 'yesterday - 1 year', + 'end_date' => 'yesterday', + 'join_date' => 'yesterday - 1 year', + ], + ], + ], ], $contriParams)); - $this->callAPISuccess('line_item', 'create', [ - 'entity_id' => $contribution['id'], - 'entity_table' => 'civicrm_contribution', - 'contribution_id' => $contribution['id'], - 'price_field_id' => $this->_ids['price_field'][0], - 'qty' => 1, - 'unit_price' => 20, - 'line_total' => 20, - 'financial_type_id' => 1, - 'price_field_value_id' => $priceFieldValueID, - ]); $this->_ids['contact'] = $contactID; $this->_ids['contribution'] = $contribution['id']; - $this->_ids['membership'] = $membership['id']; + $this->_ids['membership'] = $this->callAPISuccessGetValue('MembershipPayment', ['return' => 'membership_id', 'contribution_id' => $contribution['id']]); } /** From 7156e4b0e8ca27491dce7784b55c0dc39a4f1d25 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sat, 29 Aug 2020 19:19:06 -0400 Subject: [PATCH 050/834] Search ext - Show translated labels for operators --- ext/search/CRM/Search/Page/Ang.php | 25 ++++++++++++++++++- ext/search/ang/search/crmSearchClause.html | 2 +- .../ang/search/crmSearchValue.directive.js | 4 +-- ext/search/css/search.css | 2 +- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/ext/search/CRM/Search/Page/Ang.php b/ext/search/CRM/Search/Page/Ang.php index 936f92edd619..b6a03e2bbf76 100644 --- a/ext/search/CRM/Search/Page/Ang.php +++ b/ext/search/CRM/Search/Page/Ang.php @@ -32,7 +32,7 @@ public function run() { // Add client-side vars for the search UI $vars = [ - 'operators' => \Civi\Api4\Utils\CoreUtil::getOperators(), + 'operators' => CRM_Utils_Array::makeNonAssociative($this->getOperators()), 'schema' => $this->schema, 'links' => $this->getLinks(), 'loadOptions' => $this->loadOptions, @@ -55,6 +55,29 @@ public function run() { parent::run(); } + /** + * @return string[] + */ + private function getOperators() { + return [ + '=' => '=', + '!=' => '≠', + '>' => '>', + '<' => '<', + '>=' => '≥', + '<=' => '≤', + 'CONTAINS' => ts('Contains'), + 'IN' => ts('Is In'), + 'NOT IN' => ts('Not In'), + 'LIKE' => ts('Is Like'), + 'NOT LIKE' => ts('Not Like'), + 'BETWEEN' => ts('Is Between'), + 'NOT BETWEEN' => ts('Not Between'), + 'IS NULL' => ts('Is Null'), + 'IS NOT NULL' => ts('Not Null'), + ]; + } + /** * Populates $this->schema & $this->allowedEntities */ diff --git a/ext/search/ang/search/crmSearchClause.html b/ext/search/ang/search/crmSearchClause.html index 1a0567f9cf76..97ef49621fad 100644 --- a/ext/search/ang/search/crmSearchClause.html +++ b/ext/search/ang/search/crmSearchClause.html @@ -16,7 +16,7 @@
- +
diff --git a/ext/search/ang/search/crmSearchValue.directive.js b/ext/search/ang/search/crmSearchValue.directive.js index ad93d2b8788c..9e39cf1585b5 100644 --- a/ext/search/ang/search/crmSearchValue.directive.js +++ b/ext/search/ang/search/crmSearchValue.directive.js @@ -36,10 +36,10 @@ return; } if (inputType === 'Date') { - if (_.includes(['=', '!=', '<>', '>', '>=', '<', '<='], op)) { + if (_.includes(['=', '!=', '>', '>=', '<', '<='], op)) { $el.crmDatepicker({time: (field.input_attrs && field.input_attrs.time) || false}); } - } else if (_.includes(['=', '!=', '<>', 'IN', 'NOT IN', 'CONTAINS'], op) && (field.fk_entity || field.options || dataType === 'Boolean')) { + } else if (_.includes(['=', '!=', 'IN', 'NOT IN', 'CONTAINS'], op) && (field.fk_entity || field.options || dataType === 'Boolean')) { if (field.options) { if (field.options === true) { $el.addClass('loading'); diff --git a/ext/search/css/search.css b/ext/search/css/search.css index 70b329b56324..554f1f724b6e 100644 --- a/ext/search/css/search.css +++ b/ext/search/css/search.css @@ -133,7 +133,7 @@ } #bootstrap-theme.crm-search .api4-operator { - width: 90px; + width: 110px; } #bootstrap-theme.crm-search .api4-add-where-group-menu { From a54cf42b6d32866d1a13219079d4748c9f5317b0 Mon Sep 17 00:00:00 2001 From: Jon Goldberg Date: Thu, 27 Aug 2020 22:00:09 -0400 Subject: [PATCH 051/834] allow negative self-service hours event #34 message improvement fixes to message template fixes --- CRM/Event/BAO/Event.php | 2 ++ CRM/Event/BAO/Participant.php | 8 ++++++-- CRM/Upgrade/Incremental/MessageTemplates.php | 10 ++++++++++ CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl | 3 +++ templates/CRM/Event/Form/ManageEvent/Registration.hlp | 2 +- tests/phpunit/CRM/Event/BAO/ParticipantTest.php | 9 +++++++++ xml/schema/Event/Event.xml | 2 +- .../message_templates/event_online_receipt_html.tpl | 2 +- .../message_templates/event_online_receipt_text.tpl | 2 +- .../message_templates/participant_confirm_html.tpl | 2 +- .../message_templates/participant_confirm_text.tpl | 2 +- 11 files changed, 36 insertions(+), 8 deletions(-) diff --git a/CRM/Event/BAO/Event.php b/CRM/Event/BAO/Event.php index 794e5d14e5a4..b583fdd09926 100644 --- a/CRM/Event/BAO/Event.php +++ b/CRM/Event/BAO/Event.php @@ -1158,6 +1158,8 @@ public static function sendMail($contactID, $values, $participantId, $isTest = F 'conference_sessions' => $sessions, 'credit_card_number' => CRM_Utils_System::mungeCreditCard(CRM_Utils_Array::value('credit_card_number', $participantParams)), 'credit_card_exp_date' => CRM_Utils_Date::mysqlToIso(CRM_Utils_Date::format(CRM_Utils_Array::value('credit_card_exp_date', $participantParams))), + 'selfcancelxfer_time' => abs($values['event']['selfcancelxfer_time']), + 'selfservice_preposition' => $values['event']['selfcancelxfer_time'] < 0 ? 'after' : 'before', ]); // CRM-13890 : NOTE wait list condition need to be given so that diff --git a/CRM/Event/BAO/Participant.php b/CRM/Event/BAO/Participant.php index 1f19c32f74b4..f18198b20961 100644 --- a/CRM/Event/BAO/Participant.php +++ b/CRM/Event/BAO/Participant.php @@ -1877,7 +1877,6 @@ public static function formatFieldsAndSetProfileDefaults($contactId, &$form) { * Evaluate whether a participant record is eligible for self-service transfer/cancellation. If so, * return additional participant/event details. * - * TODO: This function fails when the "hours until self-service" is less than zero. * @param int $participantId * @param string $url * @param bool $isBackOffice @@ -1930,7 +1929,12 @@ public static function getSelfServiceEligibility(int $participantId, string $url $cancelDeadline = (new Datetime($start_date))->sub($cancelInterval); if ($timenow > $cancelDeadline) { $details['eligible'] = FALSE; - $details['ineligible_message'] = ts("Registration for this event cannot be cancelled or transferred less than %1 hours prior to the event's start time. Contact the event organizer if you have questions.", [1 => $time_limit]); + // Change the language of the status message based on whether the waitlist time limit is positive or negative. + $afterOrPrior = $time_limit < 0 ? 'after' : 'prior'; + $moreOrLess = $time_limit < 0 ? 'more' : 'less'; + $details['ineligible_message'] = ts("Registration for this event cannot be cancelled or transferred %1 than %2 hours %3 to the event's start time. Contact the event organizer if you have questions.", + [1 => $moreOrLess, 2 => $cancelHours, 3 => $afterOrPrior]); + } } return $details; diff --git a/CRM/Upgrade/Incremental/MessageTemplates.php b/CRM/Upgrade/Incremental/MessageTemplates.php index 0b86bc6a6dce..94f932a96e50 100644 --- a/CRM/Upgrade/Incremental/MessageTemplates.php +++ b/CRM/Upgrade/Incremental/MessageTemplates.php @@ -215,6 +215,16 @@ protected function getTemplateUpdates() { ['name' => 'contribution_invoice_receipt', 'type' => 'html'], ], ], + [ + 'version' => '5.30.alpha1', + 'upgrade_descriptor' => ts('Support negative hours for cancellation/transfer'), + 'templates' => [ + ['name' => 'participant_confirm', 'type' => 'html'], + ['name' => 'participant_confirm', 'type' => 'text'], + ['name' => 'event_online_receipt', 'type' => 'html'], + ['name' => 'event_online_receipt', 'type' => 'text'], + ], + ], ]; } diff --git a/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl b/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl index 4f70e19a59d0..3b0804b3c3fc 100644 --- a/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl +++ b/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl @@ -1 +1,4 @@ {* file to handle db changes in 5.30.alpha1 during upgrade *} +-- Allow self-service/transfer to have a negative time. +ALTER TABLE civicrm_event MODIFY COLUMN selfcancelxfer_time INT; + diff --git a/templates/CRM/Event/Form/ManageEvent/Registration.hlp b/templates/CRM/Event/Form/ManageEvent/Registration.hlp index 2aa4f62db7de..8fba9c56a980 100644 --- a/templates/CRM/Event/Form/ManageEvent/Registration.hlp +++ b/templates/CRM/Event/Form/ManageEvent/Registration.hlp @@ -89,5 +89,5 @@ {ts}Cancellation or Transfer Time Limit{/ts} {/htxt} {htxt id="id-selfcancelxfer_time"} - {ts}Number of hours prior to event start date to allow self-service cancellation or transfer. Enter 0 (or leave empty) to allow cancellation or transfer up until the event has started.{/ts} + {ts}Number of hours prior to event start date to allow self-service cancellation or transfer. Enter a negative number of hours to allow cancellation after the event starts. Enter 0 (or leave empty) to allow cancellation or transfer up until the event has started.{/ts} {/htxt} diff --git a/tests/phpunit/CRM/Event/BAO/ParticipantTest.php b/tests/phpunit/CRM/Event/BAO/ParticipantTest.php index eb8dc5700842..4dc15134dc0a 100644 --- a/tests/phpunit/CRM/Event/BAO/ParticipantTest.php +++ b/tests/phpunit/CRM/Event/BAO/ParticipantTest.php @@ -493,6 +493,15 @@ public function selfServiceScenarios() { 'isBackOffice' => FALSE, 'successExpected' => FALSE, ]; + // Cancellation deadline is < 0 hours + $scenarios[] = [ + 'selfSvcEnabled' => 1, + 'selfSvcHours' => -12, + 'hoursToEvent' => 4, + 'participantStatusId' => 1, + 'isBackOffice' => FALSE, + 'successExpected' => TRUE, + ]; return $scenarios; } diff --git a/xml/schema/Event/Event.xml b/xml/schema/Event/Event.xml index 4593c92d4e56..979a84df4de4 100644 --- a/xml/schema/Event/Event.xml +++ b/xml/schema/Event/Event.xml @@ -659,7 +659,7 @@ selfcancelxfer_time - int unsigned + int 0 Self-service Cancellation or Transfer Time Number of hours prior to event start date to allow self-service cancellation or transfer. diff --git a/xml/templates/message_templates/event_online_receipt_html.tpl b/xml/templates/message_templates/event_online_receipt_html.tpl index c9e080625675..b9fdec15b443 100644 --- a/xml/templates/message_templates/event_online_receipt_html.tpl +++ b/xml/templates/message_templates/event_online_receipt_html.tpl @@ -497,7 +497,7 @@ {if $event.allow_selfcancelxfer } - {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
+ {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
{capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} {ts}Click here to transfer or cancel your registration.{/ts} diff --git a/xml/templates/message_templates/event_online_receipt_text.tpl b/xml/templates/message_templates/event_online_receipt_text.tpl index dbd5f1ba8803..05be6dff34a7 100644 --- a/xml/templates/message_templates/event_online_receipt_text.tpl +++ b/xml/templates/message_templates/event_online_receipt_text.tpl @@ -291,7 +291,7 @@ You were registered by: {$payer.name} {/if} {if $event.allow_selfcancelxfer } -{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} +{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} {capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} {ts}Transfer or cancel your registration:{/ts} {$selfService} {/if} diff --git a/xml/templates/message_templates/participant_confirm_html.tpl b/xml/templates/message_templates/participant_confirm_html.tpl index ed1d2b7b0db9..c4075a04bbb6 100644 --- a/xml/templates/message_templates/participant_confirm_html.tpl +++ b/xml/templates/message_templates/participant_confirm_html.tpl @@ -165,7 +165,7 @@ {if $event.allow_selfcancelxfer } - {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
+ {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
{capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} {ts}Click here to transfer or cancel your registration.{/ts} diff --git a/xml/templates/message_templates/participant_confirm_text.tpl b/xml/templates/message_templates/participant_confirm_text.tpl index 6296b4594d76..2c459e66e29f 100644 --- a/xml/templates/message_templates/participant_confirm_text.tpl +++ b/xml/templates/message_templates/participant_confirm_text.tpl @@ -13,7 +13,7 @@ Click this link to go to a web page where you can confirm your registration onli {$confirmUrl} {/if} {if $event.allow_selfcancelxfer } -{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} +{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} {capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} {ts}Transfer or cancel your registration:{/ts} {$selfService} {/if} From d5851cf27fba01ba700f8804996979188586f6cb Mon Sep 17 00:00:00 2001 From: Jon Goldberg Date: Sun, 30 Aug 2020 16:58:32 -0400 Subject: [PATCH 052/834] add serialize data to actionschedule --- CRM/Core/DAO/ActionSchedule.php | 5 ++++- xml/schema/Core/ActionSchedule.xml | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CRM/Core/DAO/ActionSchedule.php b/CRM/Core/DAO/ActionSchedule.php index 2762cb3f8885..666f773ba859 100644 --- a/CRM/Core/DAO/ActionSchedule.php +++ b/CRM/Core/DAO/ActionSchedule.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/ActionSchedule.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:caf8b2cabbe06570dcf6cb93144004d2) + * (GenCodeChecksum:d05639de89f460efbb3474dcaf5acd27) */ /** @@ -409,6 +409,7 @@ public static function &fields() { 'entity' => 'ActionSchedule', 'bao' => 'CRM_Core_BAO_ActionSchedule', 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED, 'add' => '3.4', ], 'entity_status' => [ @@ -423,6 +424,7 @@ public static function &fields() { 'entity' => 'ActionSchedule', 'bao' => 'CRM_Core_BAO_ActionSchedule', 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED, 'add' => '3.4', ], 'start_action_offset' => [ @@ -617,6 +619,7 @@ public static function &fields() { 'entity' => 'ActionSchedule', 'bao' => 'CRM_Core_BAO_ActionSchedule', 'localizable' => 0, + 'serialize' => self::SERIALIZE_COMMA, 'add' => '3.4', ], 'recipient_listing' => [ diff --git a/xml/schema/Core/ActionSchedule.xml b/xml/schema/Core/ActionSchedule.xml index 4c7d78c219bf..536f174cfbc6 100644 --- a/xml/schema/Core/ActionSchedule.xml +++ b/xml/schema/Core/ActionSchedule.xml @@ -51,6 +51,7 @@ varchar 255 Entity value + SEPARATOR_TRIMMED 3.4
@@ -58,6 +59,7 @@ varchar 64 Entity status + SEPARATOR_TRIMMED 3.4 @@ -169,6 +171,7 @@ varchar 128 Contact IDs to which reminder should be sent. + COMMA 3.4 From dbb17d48689b6033d365d1b9297456a3e0668ab1 Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 31 Aug 2020 11:21:51 +1200 Subject: [PATCH 053/834] Slight increase in test cover & make functions more re-usable Just preliminary to increasing cover in another test class --- .../CRM/Core/Payment/AuthorizeNetTrait.php | 28 +++++++++++++++++++ .../CRM/Financial/Form/PaymentFormsTest.php | 8 ++---- tests/phpunit/CiviTest/CiviUnitTestCase.php | 2 +- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/tests/phpunit/CRM/Core/Payment/AuthorizeNetTrait.php b/tests/phpunit/CRM/Core/Payment/AuthorizeNetTrait.php index 87ff897d413d..38871fa041a5 100644 --- a/tests/phpunit/CRM/Core/Payment/AuthorizeNetTrait.php +++ b/tests/phpunit/CRM/Core/Payment/AuthorizeNetTrait.php @@ -72,4 +72,32 @@ public function getExpectedRecurResponse() { return '8d468ca1b1dd5c2b56c7OkI00001Successful.663205215120232801512027350'; } + /** + * Create an AuthorizeNet processors with a configured mock handler. + * + * @throws \CiviCRM_API3_Exception + */ + protected function createAuthorizeNetProcessor() { + $processorID = $this->paymentProcessorAuthorizeNetCreate(['is_test' => FALSE]); + $this->setupMockHandler($processorID); + $this->ids['PaymentProcessor']['anet'] = $processorID; + } + + /** + * Assert the request sent to Authorize.net contains the expected values. + * + * @param array $expected + */ + protected function assertRequestValid($expected = []) { + $expected = array_merge([ + 'x_card_num' => '4111111111111111', + 'x_card_code' => 123, + ], $expected); + $request = explode('&', $this->getRequestBodies()[0]); + // This is stand in for now just to check a request happened. We can improve later. + foreach ($expected as $key => $value) { + $this->assertContains($key . '=' . $value, $request); + } + } + } diff --git a/tests/phpunit/CRM/Financial/Form/PaymentFormsTest.php b/tests/phpunit/CRM/Financial/Form/PaymentFormsTest.php index 29b271cadbb3..f012856aa8fc 100644 --- a/tests/phpunit/CRM/Financial/Form/PaymentFormsTest.php +++ b/tests/phpunit/CRM/Financial/Form/PaymentFormsTest.php @@ -38,8 +38,8 @@ class CRM_Financial_Form_PaymentFormsTest extends CiviUnitTestCase { * @throws \CiviCRM_API3_Exception */ public function testEventPaymentForms() { - $processors = [$this->paymentProcessorAuthorizeNetCreate(['is_test' => FALSE])]; - $this->setupMockHandler($processors[0]); + $this->createAuthorizeNetProcessor(); + $processors = [$this->ids['PaymentProcessor']['anet']]; $eventID = $this->eventCreatePaid([ 'end_date' => '+ 1 month', 'registration_end_date' => '+ 1 month', @@ -93,9 +93,7 @@ public function testEventPaymentForms() { $qfKey = $form->controller->_key; } $this->callAPISuccessGetSingle('Participant', ['participant_status_id' => 'Registered']); - $request = explode('&', $this->getRequestBodies()[0]); - // This is stand in for now just to check a request happened. We can improve later. - $this->assertContains('x_card_num=4111111111111111', $request); + $this->assertRequestValid(['x_city' => 'The+Shire', 'x_state' => 'IL', 'x_amount' => 1.0]); } } diff --git a/tests/phpunit/CiviTest/CiviUnitTestCase.php b/tests/phpunit/CiviTest/CiviUnitTestCase.php index 8f4550ec4401..229650a46bc4 100644 --- a/tests/phpunit/CiviTest/CiviUnitTestCase.php +++ b/tests/phpunit/CiviTest/CiviUnitTestCase.php @@ -749,7 +749,7 @@ public function paymentProcessorAuthorizeNetCreate($params = []) { ], $params); $result = $this->callAPISuccess('PaymentProcessor', 'create', $params); - return $result['id']; + return (int) $result['id']; } /** From 67dd58e4152f6f04e458872cf1fb20f19832098c Mon Sep 17 00:00:00 2001 From: KarinG Date: Sun, 30 Aug 2020 19:01:41 -0600 Subject: [PATCH 054/834] extend_testSubmitContributionPageWithPriceSetQuantity --- tests/phpunit/api/v3/ContributionPageTest.php | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/tests/phpunit/api/v3/ContributionPageTest.php b/tests/phpunit/api/v3/ContributionPageTest.php index 25eb05e65f4d..689694a72a46 100644 --- a/tests/phpunit/api/v3/ContributionPageTest.php +++ b/tests/phpunit/api/v3/ContributionPageTest.php @@ -1957,6 +1957,7 @@ public function testSubmitContributionPageWithPriceSetQuantity($thousandSeparato 'label' => 'Printing Rights', 'html_type' => 'Text', ]); + $this->callAPISuccess('price_field_value', 'create', [ 'price_set_id' => $priceSetID, 'price_field_id' => $priceField['id'], @@ -1969,10 +1970,28 @@ public function testSubmitContributionPageWithPriceSetQuantity($thousandSeparato // Set quantity for our test $submitParams['price_' . $priceFieldId] = 180; + $priceField = $this->callAPISuccess('price_field', 'create', [ + 'price_set_id' => $priceSetID, + 'label' => 'Another Line Item', + 'html_type' => 'Text', + ]); + + $this->callAPISuccess('price_field_value', 'create', [ + 'price_set_id' => $priceSetID, + 'price_field_id' => $priceField['id'], + 'label' => 'Another Line Item', + 'financial_type_id' => $financialTypeId, + 'amount' => '2.95', + ]); + $priceFieldId = $priceField['id']; + + // Set quantity for our test + $submitParams['price_' . $priceFieldId] = 110; + // contribution_page submit requires amount and tax_amount - and that's ok we're not testing that - we're testing at the LineItem level - $submitParams['amount'] = $this->formatMoneyInput(180 * 16.95); + $submitParams['amount'] = $this->formatMoneyInput(180 * 16.95 + 110 * 2.95); // This is the correct Tax Amount - use it later to compare to what the CiviCRM Core came up with at the LineItem level - $submitParams['tax_amount'] = $this->formatMoneyInput(180 * 16.95 * 0.10); + $submitParams['tax_amount'] = (180 * 16.95 * 0.10 + 110 * 2.95 * 0.10); $this->callAPISuccess('contribution_page', 'submit', $submitParams); $contribution = $this->callAPISuccessGetSingle('contribution', [ @@ -1980,15 +1999,25 @@ public function testSubmitContributionPageWithPriceSetQuantity($thousandSeparato ]); // Retrieve the lineItem that belongs to the Printing Rights and check the tax_amount CiviCRM Core calculated for it - $lineItem = $this->callAPISuccessGetSingle('LineItem', [ + $lineItem1 = $this->callAPISuccessGetSingle('LineItem', [ 'contribution_id' => $contribution['id'], 'label' => 'Printing Rights', ]); - $lineItem_TaxAmount = round($lineItem['tax_amount'], 2); + // Retrieve the lineItem that belongs to the Another Line Item and check the tax_amount CiviCRM Core calculated for it + $lineItem2 = $this->callAPISuccessGetSingle('LineItem', [ + 'contribution_id' => $contribution['id'], + 'label' => 'Another Line Item', + ]); + + $finalContribution = $this->callAPISuccess('Contribution', 'getsingle', ['id' => $contribution['id'], 'return' => ['tax_amount', 'total_amount']]); + + $this->assertEquals($lineItem1['line_total'] + $lineItem2['line_total'], round(180 * 16.95 + 110 * 2.95, 2), 'Line Item Total is incorrect.'); + $this->assertEquals($lineItem1['line_total'] + $lineItem2['line_total'], $finalContribution['total_amount'], 'Contribution total should match line item totals'); + + $this->assertEquals(round($lineItem1['tax_amount'] + $lineItem2['tax_amount'], 2), round(180 * 16.95 * 0.10 + 110 * 2.95 * 0.10, 2), 'Wrong Sales Tax Amount is calculated and stored.'); + $this->assertEquals(round($lineItem1['tax_amount'] + $lineItem2['tax_amount'], 2), $finalContribution['tax_amount'], 'Sales Tax Amount on Contribution does not match sum of Sales Tax Amount across the Line Items.'); - $this->assertEquals($lineItem['line_total'], $contribution['total_amount'], 'Contribution total should match line total'); - $this->assertEquals($lineItem_TaxAmount, round(180 * 16.95 * 0.10, 2), 'Wrong Sales Tax Amount is calculated and stored.'); } /** From dfdee38776797b83b6d970fd4042740bde1186eb Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 31 Aug 2020 10:10:35 +1200 Subject: [PATCH 055/834] dev/core#1983 Fix to tax calculation on multi-line-item This is similar to https://github.com/civicrm/civicrm-core/pull/18284 - it differs in that the totals are calculated by iterating through the line item array afterwards, rather than expecting the 'getLine' function to calculate totals. Some obvious follow ups suggest themselves but I will look against master. This is difficult to test (Karin gave it a really good shot) because of the weird way it's calculated in Main and thenn used in Confirm. Cleanup should resolve the testability issue too --- CRM/Price/BAO/LineItem.php | 3 ++- CRM/Price/BAO/PriceSet.php | 6 ++++-- tests/phpunit/CRM/Event/BAO/ChangeFeeSelectionTest.php | 2 +- tests/phpunit/CRM/Member/Form/MembershipTest.php | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CRM/Price/BAO/LineItem.php b/CRM/Price/BAO/LineItem.php index f381e971fd64..b94b20755af5 100644 --- a/CRM/Price/BAO/LineItem.php +++ b/CRM/Price/BAO/LineItem.php @@ -654,8 +654,9 @@ public static function changeFeeSelections( $lineItems ) { $entityTable = "civicrm_" . $entity; + $newLineItems = []; CRM_Price_BAO_PriceSet::processAmount($feeBlock, - $params, $lineItems + $params, $newLineItems ); // initialize empty Lineitem instance to call protected helper functions $lineItemObj = new CRM_Price_BAO_LineItem(); diff --git a/CRM/Price/BAO/PriceSet.php b/CRM/Price/BAO/PriceSet.php index bc87b6e9fe53..d2c1643071a2 100644 --- a/CRM/Price/BAO/PriceSet.php +++ b/CRM/Price/BAO/PriceSet.php @@ -673,13 +673,15 @@ public static function processAmount($fields, &$params, &$lineItem, $priceSetID continue; } - list($params, $lineItem, $totalTax, $totalPrice) = self::getLine($params, $lineItem, $priceSetID, $field, $id, $totalPrice); + list($params, $lineItem) = self::getLine($params, $lineItem, $priceSetID, $field, $id, $totalPrice); } $amount_level = []; $totalParticipant = 0; if (is_array($lineItem)) { foreach ($lineItem as $values) { + $totalPrice += $values['line_total'] + $values['tax_amount']; + $totalTax += $values['tax_amount']; $totalParticipant += $values['participant_count']; // This is a bit nasty. The logic of 'quick config' was because price set configuration was // (and still is) too difficult to replace the 'quick config' price set configuration on the contribution @@ -1767,7 +1769,7 @@ public static function getLine(&$params, &$lineItem, $priceSetID, $field, $id, $ } break; } - return [$params, $lineItem, $totalTax, $totalPrice]; + return [$params, $lineItem]; } } diff --git a/tests/phpunit/CRM/Event/BAO/ChangeFeeSelectionTest.php b/tests/phpunit/CRM/Event/BAO/ChangeFeeSelectionTest.php index 3a618e512ed7..329850de106d 100644 --- a/tests/phpunit/CRM/Event/BAO/ChangeFeeSelectionTest.php +++ b/tests/phpunit/CRM/Event/BAO/ChangeFeeSelectionTest.php @@ -490,7 +490,7 @@ public function testCRM17151() { $lineItem = CRM_Price_BAO_LineItem::getLineItems($this->_participantId, 'participant'); $this->assertEquals($this->_expensiveFee, $lineItem[1]['line_total']); CRM_Price_BAO_LineItem::changeFeeSelections($priceSetParams, $this->_participantId, 'participant', $this->_contributionId, $this->_feeBlock, $lineItem); - $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId, 'total_amount', 'id', $this->_cheapFee, "Total Amount equals " . $this->_expensiveFee); + $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId, 'total_amount', 'id', $this->_cheapFee, 'Total Amount equals ' . $this->_expensiveFee); $this->assertDBCompareValue('CRM_Contribute_BAO_Contribution', $this->_contributionId, 'contribution_status_id', 'id', $pendingRefundStatusId, 'Contribution must be refunding'); $priceSetParams['price_' . $this->priceSetFieldID] = $this->expensiveFeeValueID; $lineItem = CRM_Price_BAO_LineItem::getLineItems($this->_participantId, 'participant'); diff --git a/tests/phpunit/CRM/Member/Form/MembershipTest.php b/tests/phpunit/CRM/Member/Form/MembershipTest.php index ef0d0e99bda8..6ac5753209f4 100644 --- a/tests/phpunit/CRM/Member/Form/MembershipTest.php +++ b/tests/phpunit/CRM/Member/Form/MembershipTest.php @@ -536,6 +536,7 @@ public function testSubmit($thousandSeparator) { * Check if the related contribuion is also updated if the minimum_fee didn't match * * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ public function testContributionUpdateOnMembershipTypeChange() { // Step 1: Create a Membership via backoffice whose with 50.00 payment From 28188a1ed392891faadb0176385571538622edb9 Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 31 Aug 2020 13:51:03 +1200 Subject: [PATCH 056/834] Move LineItem acl handling from v3 api to financialacls extension This makes it available for the v4 api --- CRM/Price/BAO/LineItem.php | 28 ---------------------------- api/v3/LineItem.php | 3 --- ext/financialacls/financialacls.php | 21 +++++++++++++++++++++ 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/CRM/Price/BAO/LineItem.php b/CRM/Price/BAO/LineItem.php index b299fc7ad58c..286c12679d3d 100644 --- a/CRM/Price/BAO/LineItem.php +++ b/CRM/Price/BAO/LineItem.php @@ -116,34 +116,6 @@ public static function retrieve(&$params = [], &$defaults = []) { return NULL; } - /** - * Modifies $params array for filtering financial types. - * - * @param array $params - * (reference ) an assoc array of name/value pairs. - * - */ - public static function getAPILineItemParams(&$params) { - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types); - if ($types && empty($params['financial_type_id'])) { - $params['financial_type_id'] = ['IN' => array_keys($types)]; - } - elseif ($types) { - if (is_array($params['financial_type_id'])) { - $invalidFts = array_diff($params['financial_type_id'], array_keys($types)); - } - elseif (!in_array($params['financial_type_id'], array_keys($types))) { - $invalidFts = $params['financial_type_id']; - } - if ($invalidFts) { - $params['financial_type_id'] = ['NOT IN' => $invalidFts]; - } - } - else { - $params['financial_type_id'] = 0; - } - } - /** * @param int $contributionId * diff --git a/api/v3/LineItem.php b/api/v3/LineItem.php index f5b47a5e86f9..35de20acd3ee 100644 --- a/api/v3/LineItem.php +++ b/api/v3/LineItem.php @@ -62,9 +62,6 @@ function _civicrm_api3_line_item_create_spec(&$params) { * Array of matching line_items */ function civicrm_api3_line_item_get($params) { - if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus() && !empty($params['check_permissions'])) { - CRM_Price_BAO_LineItem::getAPILineItemParams($params); - } return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params); } diff --git a/ext/financialacls/financialacls.php b/ext/financialacls/financialacls.php index dda05b28f659..b079114d9f6f 100644 --- a/ext/financialacls/financialacls.php +++ b/ext/financialacls/financialacls.php @@ -168,6 +168,27 @@ function financialacls_civicrm_pre($op, $objectName, $id, &$params) { } } +/** + * Implements hook_civicrm_selectWhereClause(). + * + * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_selectWhereClause + */ +function financialacls_civicrm_selectWhereClause($entity, &$clauses) { + if ($entity === 'LineItem') { + if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) { + $types = []; + CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types); + if ($types) { + $clauses['financial_type_id'] = 'IN (' . implode(',', array_keys($types)) . ')'; + } + else { + $clauses['financial_type_id'] = '= 0'; + } + } + } + +} + // --- Functions below this ship commented out. Uncomment as required. --- /** From a5bf8a53ba596071e21d1069504edd22e055445a Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 31 Aug 2020 14:14:32 +1200 Subject: [PATCH 057/834] [REF] Separate and move line-item specific portion of checkTaxAmount to LineItem This is not intended to be the final place but making sense of checkTaxAmount is hard so I wanted to keep this simple and iterate. checkTaxAmount is called from 2 places - once from v3 LineItem.create with isLineItem = TRUE and once from Contribution.create with isLineItem = FALSE Most of the function is accessible dependent on isLineItem - meaning it's 2 separate functions. However, the first chuck is called 'whenever id is set'. BUT it makes no sense for line items. It uses the 'id' - regardless of whether it is a line item id or a contribution id - to get the previous contribution. It then sets tax amount based on a comparison - but that really makes zero sense. I'm pretty sure it was never intended to be used for LineItems --- CRM/Contribute/BAO/Contribution.php | 9 +-------- api/v3/LineItem.php | 10 +++++++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 0e8ba3d61c1a..de2271b3b0fb 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -4284,14 +4284,7 @@ public static function checkTaxAmount($params, $isLineItem = FALSE) { $params['tax_amount'] = array_sum($taxAmountArray); $params['total_amount'] = $params['total_amount'] + $params['tax_amount']; } - else { - // update line item of contrbution - if (isset($params['financial_type_id']) && array_key_exists($params['financial_type_id'], $taxRates) && $isLineItem) { - $taxRate = $taxRates[$params['financial_type_id']]; - $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($params['line_total'], $taxRate); - $params['tax_amount'] = round($taxAmount['tax_amount'], 2); - } - } + return $params; } diff --git a/api/v3/LineItem.php b/api/v3/LineItem.php index f5b47a5e86f9..4b9bf30bfc7b 100644 --- a/api/v3/LineItem.php +++ b/api/v3/LineItem.php @@ -29,9 +29,13 @@ */ function civicrm_api3_line_item_create($params) { // @todo the following line is not really appropriate for the api. The BAO should - // do the work, and it should be in a tighter function. The below function is not really - // readable because it is handling contribution and line item together. - $params = CRM_Contribute_BAO_Contribution::checkTaxAmount($params, TRUE); + // do the work. + $taxRates = CRM_Core_PseudoConstant::getTaxRates(); + if (isset($params['financial_type_id']) && array_key_exists($params['financial_type_id'], $taxRates)) { + $taxRate = $taxRates[$params['financial_type_id']]; + $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($params['line_total'], $taxRate); + $params['tax_amount'] = round($taxAmount['tax_amount'], 2); + } return _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $params, 'LineItem'); } From f4d22986701224ea83e0f667ea892704b8858761 Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 31 Aug 2020 16:15:37 +1200 Subject: [PATCH 058/834] Test & remove handling for max_related in renewal form max_related is not a field on the renewal form - the handling appears to exist purely because at some stage the BAO was not able to calculate it - however, as the test demonstrates that is no longer the case --- CRM/Member/Form/MembershipRenewal.php | 2 -- .../CRM/Member/Form/MembershipRenewalTest.php | 12 ++++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/CRM/Member/Form/MembershipRenewal.php b/CRM/Member/Form/MembershipRenewal.php index 17b3dfc6f66a..65105c13666d 100644 --- a/CRM/Member/Form/MembershipRenewal.php +++ b/CRM/Member/Form/MembershipRenewal.php @@ -748,7 +748,6 @@ protected function sendReceipt($membership) { */ public function processMembership($contactID, $membershipTypeID, $is_test, $changeToday, $customFieldsFormatted, $numRenewTerms, $membershipID, $pending, $contributionRecurID, $isPayLater) { $allStatus = CRM_Member_PseudoConstant::membershipStatus(); - $membershipTypeDetails = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($membershipTypeID); $ids = []; // CRM-7297 - allow membership type to be be changed during renewal so long as the parent org of new membershipType @@ -773,7 +772,6 @@ public function processMembership($contactID, $membershipTypeID, $is_test, $chan 'end_date' => $currentMembership['end_date'], 'join_date' => $currentMembership['join_date'], 'membership_type_id' => $membershipTypeID, - 'max_related' => !empty($membershipTypeDetails['max_related']) ? $membershipTypeDetails['max_related'] : NULL, 'membership_activity_status' => ($pending || $isPayLater) ? 'Scheduled' : 'Completed', ]; if ($contributionRecurID) { diff --git a/tests/phpunit/CRM/Member/Form/MembershipRenewalTest.php b/tests/phpunit/CRM/Member/Form/MembershipRenewalTest.php index d70f497c2f1d..1b8fe6b3ce3e 100644 --- a/tests/phpunit/CRM/Member/Form/MembershipRenewalTest.php +++ b/tests/phpunit/CRM/Member/Form/MembershipRenewalTest.php @@ -91,6 +91,7 @@ public function setUp() { 'relationship_type_id' => 20, 'min_fee' => 100, 'financial_type_id' => $this->financialTypeID, + 'max_related' => 10, ])['id']; $this->_membershipID = $this->callAPISuccess('Membership', 'create', [ @@ -144,7 +145,6 @@ public function testSubmit() { // This format reflects the 23 being the organisation & the 25 being the type. 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], 'auto_renew' => '0', - 'max_related' => '', 'num_terms' => '1', 'source' => '', 'total_amount' => '50.00', @@ -225,7 +225,6 @@ public function testSubmitWithTax() { // This format reflects the 23 being the organisation & the 25 being the type. 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], 'auto_renew' => '0', - 'max_related' => '', 'num_terms' => '1', 'source' => '', 'total_amount' => '50.00', @@ -281,7 +280,6 @@ public function testSubmitRecur() { 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], 'auto_renew' => '1', 'is_recur' => 1, - 'max_related' => 0, 'num_terms' => '1', 'source' => '', 'total_amount' => '77.00', @@ -493,7 +491,6 @@ public function testSubmitPayLater() { // This format reflects the 23 being the organisation & the 25 being the type. 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], 'auto_renew' => '0', - 'max_related' => '', 'num_terms' => '2', 'total_amount' => '50.00', //Member dues, see data.xml @@ -546,7 +543,6 @@ public function testSubmitPayLaterWithBilling() { // This format reflects the 23 being the organisation & the 25 being the type. 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], 'auto_renew' => '0', - 'max_related' => '', 'num_terms' => '2', 'total_amount' => '50.00', //Member dues, see data.xml @@ -561,7 +557,7 @@ public function testSubmitPayLaterWithBilling() { 'trxn_id' => 777, 'contribution_status_id' => 2, 'billing_first_name' => 'Test', - 'billing_middlename' => 'Last', + 'billing_middle_name' => 'Last', 'billing_street_address-5' => '10 Test St', 'billing_city-5' => 'Test', 'billing_state_province_id-5' => '1003', @@ -573,6 +569,8 @@ public function testSubmitPayLaterWithBilling() { $form->testSubmit($params); $membership = $this->callAPISuccessGetSingle('Membership', ['contact_id' => $this->_individualId]); $this->assertEquals(strtotime($membership['end_date']), strtotime($originalMembership['end_date'])); + $this->assertEquals(10, $membership['max_related']); + $contribution = $this->callAPISuccessGetSingle('Contribution', [ 'contact_id' => $this->_individualId, 'contribution_status_id' => 2, @@ -609,7 +607,6 @@ public function testSubmitComplete() { // This format reflects the 23 being the organisation & the 25 being the type. 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], 'auto_renew' => '0', - 'max_related' => '', 'num_terms' => '2', 'total_amount' => '50.00', //Member dues, see data.xml @@ -685,7 +682,6 @@ protected function getBaseSubmitParams() { 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], 'auto_renew' => '1', 'is_recur' => 1, - 'max_related' => 0, 'num_terms' => '1', 'total_amount' => $this->formatMoneyInput('7800.90'), //Member dues, see data.xml From d7d9f7b6621fefc1f4e53787933d870f4e37c4da Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 31 Aug 2020 16:42:15 +1200 Subject: [PATCH 059/834] [REF] simplify retrieval of existing membership on membership renewal form After stepping through the code I determined that if membershipID is passed into getContactMembership then it either 1) returns the membership id much like membership.get but with is_current_member calculated or 2) returns the owner membership in a similar format However, the renewal form is not visible for a non-owner membership so getting the owner membership is cruft from another form & only 1 applies. Calling the api is much more transparent --- CRM/Member/Form/MembershipRenewal.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/CRM/Member/Form/MembershipRenewal.php b/CRM/Member/Form/MembershipRenewal.php index 17b3dfc6f66a..83bc8106cb51 100644 --- a/CRM/Member/Form/MembershipRenewal.php +++ b/CRM/Member/Form/MembershipRenewal.php @@ -750,12 +750,7 @@ public function processMembership($contactID, $membershipTypeID, $is_test, $chan $allStatus = CRM_Member_PseudoConstant::membershipStatus(); $membershipTypeDetails = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($membershipTypeID); $ids = []; - - // CRM-7297 - allow membership type to be be changed during renewal so long as the parent org of new membershipType - // is the same as the parent org of an existing membership of the contact - $currentMembership = CRM_Member_BAO_Membership::getContactMembership($contactID, $membershipTypeID, - $is_test, $membershipID, TRUE - ); + $currentMembership = civicrm_api3('Membership', 'getsingle', ['id' => $membershipID]); // Do NOT do anything. //1. membership with status : PENDING/CANCELLED (CRM-2395) @@ -765,7 +760,6 @@ public function processMembership($contactID, $membershipTypeID, $is_test, $chan // CRM-15475 array_search('Cancelled', CRM_Member_PseudoConstant::membershipStatus(NULL, " name = 'Cancelled' ", 'name', FALSE, TRUE)), ])) { - $memParams = [ 'id' => $currentMembership['id'], 'status_id' => $currentMembership['status_id'], @@ -785,7 +779,7 @@ public function processMembership($contactID, $membershipTypeID, $is_test, $chan // Check and fix the membership if it is STALE CRM_Member_BAO_Membership::fixMembershipStatusBeforeRenew($currentMembership, $changeToday); - $isMembershipCurrent = $currentMembership['is_current_member']; + $isMembershipCurrent = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipStatus', $currentMembership['status_id'], 'is_current_member'); // CRM-7297 Membership Upsell - calculate dates based on new membership type $dates = CRM_Member_BAO_MembershipType::getRenewalDatesForMembershipType($currentMembership['id'], From f816b68fc866414c929a90d4c67b8df6e3862fec Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 31 Aug 2020 13:00:14 +1200 Subject: [PATCH 060/834] Add test to check, remove unnecessary lines Per the code comment it was likely the removed lines were not needed but tests were required to confirm. This does that --- CRM/Contribute/BAO/Contribution.php | 4 --- .../Event/Form/Registration/ConfirmTest.php | 6 ++-- tests/phpunit/CiviTest/CiviMailUtils.php | 10 ++++++ tests/phpunit/CiviTest/CiviUnitTestCase.php | 1 + tests/phpunit/api/v3/PaymentTest.php | 33 +++++++++++++++---- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 0e8ba3d61c1a..7fa1f8dcb528 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -4527,10 +4527,6 @@ public static function completeOrder($input, $ids, $objects, $isPostPaymentCreat } else { if (empty($input['IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved'])) { - if ($event->is_email_confirm) { - // @todo this should be set by the function that sends the mail after sending. - $contributionParams['receipt_date'] = $changeDate; - } $participantParams['id'] = $participant->id; $participantParams['status_id'] = 'Registered'; civicrm_api3('Participant', 'create', $participantParams); diff --git a/tests/phpunit/CRM/Event/Form/Registration/ConfirmTest.php b/tests/phpunit/CRM/Event/Form/Registration/ConfirmTest.php index f37ae9d2da8a..030228424165 100644 --- a/tests/phpunit/CRM/Event/Form/Registration/ConfirmTest.php +++ b/tests/phpunit/CRM/Event/Form/Registration/ConfirmTest.php @@ -92,9 +92,10 @@ public function testSubmit() { * * @param string $thousandSeparator * - * @dataProvider getThousandSeparators + * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception * - * @throws \Exception + * @dataProvider getThousandSeparators */ public function testPaidSubmit($thousandSeparator) { $this->setCurrencySeparators($thousandSeparator); @@ -165,6 +166,7 @@ public function testPaidSubmit($thousandSeparator) { $this->assertEquals(8000.67, $contribution['total_amount']); $this->assertEquals(1.67, $contribution['fee_amount']); $this->assertEquals(7999, $contribution['net_amount']); + $this->assertNotEmpty($contribution['receipt_date']); $this->assertNotContains(' (multiple participants)', $contribution['amount_level']); $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC'); $financialTrxn = $this->callAPISuccessGetSingle( diff --git a/tests/phpunit/CiviTest/CiviMailUtils.php b/tests/phpunit/CiviTest/CiviMailUtils.php index d7836fc8ab5e..b502273e8373 100644 --- a/tests/phpunit/CiviTest/CiviMailUtils.php +++ b/tests/phpunit/CiviTest/CiviMailUtils.php @@ -62,6 +62,16 @@ public function __construct(&$unit_test, $startImmediately = TRUE) { } } + /** + * Clean up after test. + * + * @throws \CRM_Core_Exception + */ + public function __destruct() { + $this->stop(); + $this->clearMessages(); + } + /** * Start writing emails to db instead of current option. */ diff --git a/tests/phpunit/CiviTest/CiviUnitTestCase.php b/tests/phpunit/CiviTest/CiviUnitTestCase.php index 8f4550ec4401..decd2f21fce1 100644 --- a/tests/phpunit/CiviTest/CiviUnitTestCase.php +++ b/tests/phpunit/CiviTest/CiviUnitTestCase.php @@ -729,6 +729,7 @@ public function paymentProcessorTypeCreate($params = NULL) { * @param array $params * * @return mixed + * @throws \CRM_Core_Exception */ public function paymentProcessorAuthorizeNetCreate($params = []) { $params = array_merge([ diff --git a/tests/phpunit/api/v3/PaymentTest.php b/tests/phpunit/api/v3/PaymentTest.php index 046f1f6058df..4ae47f92003d 100644 --- a/tests/phpunit/api/v3/PaymentTest.php +++ b/tests/phpunit/api/v3/PaymentTest.php @@ -186,6 +186,33 @@ public function testGetPaymentWithTrxnID() { $this->validateAllPayments(); } + /** + * Test contribution receipts triggered by Payment.create with is_send_contribution_notification = TRUE. + * + * @throws \CRM_Core_Exception + */ + public function testPaymentSendContributionReceipt() { + $mut = new CiviMailUtils($this); + $contribution = $this->createPartiallyPaidParticipantOrder(); + $event = $this->callAPISuccess('Event', 'get', []); + $this->addLocationToEvent($event['id']); + $params = [ + 'contribution_id' => $contribution['id'], + 'total_amount' => 150, + 'check_number' => '345', + 'trxn_date' => '2018-08-13 17:57:56', + 'is_send_contribution_notification' => TRUE, + ]; + $this->callAPISuccess('Payment', 'create', $params); + $contribution = $this->callAPISuccessGetSingle('Contribution', ['id' => $contribution['id']]); + $this->assertNotEmpty($contribution['receipt_date']); + $mut->checkMailLog([ + 'Price Field - Price Field 1 1 $ 100.00 $ 100.00', + 'event place', + 'streety street', + ]); + } + /** * Test email receipt for partial payment. * @@ -229,8 +256,6 @@ public function testPaymentEmailReceipt() { 'event place', 'streety street', ]); - $mut->stop(); - $mut->clearMessages(); $this->validateAllPayments(); } @@ -263,8 +288,6 @@ public function testPaymentEmailReceiptFullyPaid() { 'Balance Owed: $ 0.00', 'Thank you for completing this payment.', ]); - $mut->stop(); - $mut->clearMessages(); $this->validateAllPayments(); } @@ -319,8 +342,6 @@ public function testRefundEmailReceipt($thousandSeparator) { 'Transaction Date: November 13th, 2018 12:01 PM', 'Total Paid: $ 170' . $decimalSeparator . '00', ]); - $mut->stop(); - $mut->clearMessages(); $this->validateAllPayments(); } From bb35c5f3477a97b937cf083fa1b66e315ae53507 Mon Sep 17 00:00:00 2001 From: Jon Goldberg Date: Sat, 29 Aug 2020 11:48:11 -0400 Subject: [PATCH 061/834] Clean money for non-deductible amount tests --- api/v3/Contribution.php | 2 +- tests/phpunit/api/v3/ContributionTest.php | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/api/v3/Contribution.php b/api/v3/Contribution.php index 7a5e94dcb57c..63036179825e 100644 --- a/api/v3/Contribution.php +++ b/api/v3/Contribution.php @@ -32,7 +32,7 @@ function civicrm_api3_contribution_create($params) { // The BAO should not clean money - it should be done in the form layer & api wrapper // (although arguably the api should expect pre-cleaned it seems to do some cleaning.) if (empty($params['skipCleanMoney'])) { - foreach (['total_amount', 'net_amount', 'fee_amount'] as $field) { + foreach (['total_amount', 'net_amount', 'fee_amount', 'non_deductible_amount'] as $field) { if (isset($params[$field])) { $params[$field] = CRM_Utils_Rule::cleanMoney($params[$field]); } diff --git a/tests/phpunit/api/v3/ContributionTest.php b/tests/phpunit/api/v3/ContributionTest.php index bf24317b8868..0e088557b036 100644 --- a/tests/phpunit/api/v3/ContributionTest.php +++ b/tests/phpunit/api/v3/ContributionTest.php @@ -4948,4 +4948,26 @@ public function testPaymentVerifyPaymentInstrumentChange() { $this->assertEquals('Completed', $contribution['contribution_status']); } + /** + * Test the "clean money" functionality. + */ + public function testCleanMoney() { + $params = [ + 'contact_id' => $this->_individualId, + 'financial_type_id' => 1, + 'total_amount' => '$100', + 'fee_amount' => '$20', + 'net_amount' => '$80', + 'non_deductible_amount' => '$80', + 'sequential' => 1, + ]; + $id = $this->callAPISuccess('Contribution', 'create', $params)['id']; + // Reading the return values of the API isn't reliable here; get the data from the db. + $contribution = $this->callAPISuccess('Contribution', 'getsingle', ['id' => $id]); + $this->assertEquals('100.00', $contribution['total_amount']); + $this->assertEquals('20.00', $contribution['fee_amount']); + $this->assertEquals('80.00', $contribution['net_amount']); + $this->assertEquals('80.00', $contribution['non_deductible_amount']); + } + } From 3df62a62128fcdfa84957768c8e327af01302063 Mon Sep 17 00:00:00 2001 From: demeritcowboy Date: Mon, 31 Aug 2020 17:58:56 -0400 Subject: [PATCH 062/834] faulty check for simplexml node value - see also PR 18282 --- CRM/Case/XMLProcessor/Process.php | 10 ++++++-- tests/phpunit/api/v3/CaseTest.php | 42 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CRM/Case/XMLProcessor/Process.php b/CRM/Case/XMLProcessor/Process.php index d010a47c6835..3a75649f0bfd 100644 --- a/CRM/Case/XMLProcessor/Process.php +++ b/CRM/Case/XMLProcessor/Process.php @@ -85,7 +85,13 @@ public function process($xml, &$params) { // create relationships for the ones that are required foreach ($xml->CaseRoles as $caseRoleXML) { foreach ($caseRoleXML->RelationshipType as $relationshipTypeXML) { - if ($relationshipTypeXML->creator) { + // simplexml treats node values differently than you'd expect, + // e.g. as an array + // Just using `if ($relationshipTypeXML->creator)` ends up always + // being true, so you have to cast to int or somehow force evaluation + // of the actual value. And casting to (bool) seems to behave + // differently on these objects than casting to (int). + if (!empty($relationshipTypeXML->creator)) { if (!$this->createRelationships($relationshipTypeXML, $params ) @@ -105,7 +111,7 @@ public function process($xml, &$params) { foreach ($xml->ActivitySets as $activitySetsXML) { foreach ($activitySetsXML->ActivitySet as $activitySetXML) { if ($standardTimeline) { - if ($activitySetXML->timeline) { + if (!empty($activitySetXML->timeline)) { return $this->processStandardTimeline($activitySetXML, $params); } } diff --git a/tests/phpunit/api/v3/CaseTest.php b/tests/phpunit/api/v3/CaseTest.php index 470117269217..efd080234293 100644 --- a/tests/phpunit/api/v3/CaseTest.php +++ b/tests/phpunit/api/v3/CaseTest.php @@ -666,6 +666,48 @@ public function testCaseGetWithRoles() { $this->assertTrue($foundManager); } + /** + * Test that case role is not assigned to logged in user if you've unchecked + * the assign to creator in the case type definition. + */ + public function testCaseGetWithRolesNoCreator() { + // Copy and adjust stock case type so that assign role to creator is not checked + $caseType = $this->callAPISuccess('CaseType', 'get', ['id' => $this->caseTypeId]); + $newCaseType = $caseType['values'][$this->caseTypeId]; + // Sanity check that we're changing what we think we're changing. + $this->assertEquals('Homeless Services Coordinator', $newCaseType['definition']['caseRoles'][0]['name']); + // string '0' to match what actually happens when you do it in UI + $newCaseType['definition']['caseRoles'][0]['creator'] = '0'; + unset($newCaseType['id']); + $newCaseType['name'] = 'tree_climbing'; + $newCaseType['title'] = 'Tree Climbing'; + $newCaseType = $this->callAPISuccess('CaseType', 'create', $newCaseType); + + $case1 = $this->callAPISuccess('Case', 'create', [ + 'contact_id' => 17, + 'subject' => "Test case with roles no creator", + 'case_type_id' => $newCaseType['id'], + 'status_id' => "Open", + ]); + $result = $this->callAPISuccessGetSingle('Case', [ + 'id' => $case1['id'], + 'status_id' => "Open", + 'return' => ['contacts'], + ]); + + // There should only be the client role. + $this->assertCount(1, $result['contacts']); + $contact = $result['contacts'][0]; + $this->assertEquals('Client', $contact['role']); + // For good measure + $this->assertNotEquals(1, $contact['creator'] ?? NULL); + $this->assertNotEquals(1, $contact['manager'] ?? NULL); + + // clean up + $this->callAPISuccess('Case', 'create', ['id' => $case1['id'], 'case_type_id' => $this->caseTypeId]); + $this->callAPISuccess('CaseType', 'delete', ['id' => $newCaseType['id']]); + } + public function testCaseGetWithDefinition() { $case1 = $this->callAPISuccess('Case', 'create', [ 'contact_id' => 17, From a0e872f12e7b1bf855a1e4d645ba68d12af4fb83 Mon Sep 17 00:00:00 2001 From: Seamus Lee Date: Mon, 13 Jul 2020 14:38:20 +1000 Subject: [PATCH 063/834] [REF] permit negative payments to be made against completed payments Add in unit test for adding additional refund payment to contribution already refunded and also adding a negative payment to a contribution that is negative already --- CRM/Financial/BAO/Payment.php | 11 ++++++++-- tests/phpunit/api/v3/PaymentTest.php | 32 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CRM/Financial/BAO/Payment.php b/CRM/Financial/BAO/Payment.php index e13837e996c0..2b93c545e1ff 100644 --- a/CRM/Financial/BAO/Payment.php +++ b/CRM/Financial/BAO/Payment.php @@ -487,6 +487,9 @@ protected static function getPayableLineItems($params): array { if ($outstandingBalance !== 0.0) { $ratio = $params['total_amount'] / $outstandingBalance; } + elseif ($params['total_amount'] < 0) { + $ratio = $params['total_amount'] / (float) CRM_Core_BAO_FinancialTrxn::getTotalPayments($params['contribution_id'], TRUE); + } else { // Help we are making a payment but no money is owed. We won't allocate the overpayment to any line item. $ratio = 0; @@ -496,12 +499,16 @@ protected static function getPayableLineItems($params): array { $lineItems[$lineItemID]['id'] = $lineItemID; $lineItems[$lineItemID]['paid'] = self::getAmountOfLineItemPaid($lineItemID); $lineItems[$lineItemID]['balance'] = $lineItem['subTotal'] - $lineItems[$lineItemID]['paid']; - if (!empty($lineItemOverrides)) { $lineItems[$lineItemID]['allocation'] = $lineItemOverrides[$lineItemID] ?? NULL; } else { - $lineItems[$lineItemID]['allocation'] = $lineItems[$lineItemID]['balance'] * $ratio; + if (empty($lineItems[$lineItemID]['balance']) && !empty($ratio) && $params['total_amount'] < 0) { + $lineItems[$lineItemID]['allocation'] = $lineItem['subTotal'] * $ratio; + } + else { + $lineItems[$lineItemID]['allocation'] = $lineItems[$lineItemID]['balance'] * $ratio; + } } } return $lineItems; diff --git a/tests/phpunit/api/v3/PaymentTest.php b/tests/phpunit/api/v3/PaymentTest.php index 4ae47f92003d..5c36bde5d2c9 100644 --- a/tests/phpunit/api/v3/PaymentTest.php +++ b/tests/phpunit/api/v3/PaymentTest.php @@ -183,6 +183,7 @@ public function testGetPaymentWithTrxnID() { ], ]; $this->checkPaymentResult($payment, $expectedResult); + $this->callAPISuccess('Payment', 'create', ['total_amount' => '-20', 'contribution_id' => $contributionID2]); $this->validateAllPayments(); } @@ -213,6 +214,37 @@ public function testPaymentSendContributionReceipt() { ]); } + /** + * Test full refund when no payment has actually been record. + */ + public function testFullRefundWithPaymentAlreadyRefunded() { + $params1 = [ + 'contact_id' => $this->_individualId, + 'trxn_id' => 111111, + 'total_amount' => 10, + ]; + $contributionID1 = $this->contributionCreate($params1); + $paymentParams = ['contribution_id' => $contributionID1]; + $this->callAPISuccess('Payment', 'create', ['total_amount' => '-10', 'contribution_id' => $contributionID1]); + $payment = $this->callAPISuccess('payment', 'get', $paymentParams); + $this->callAPISuccess('Payment', 'create', ['total_amount' => '-10', 'contribution_id' => $contributionID1]); + $payment = $this->callAPISuccess('payment', 'get', $paymentParams); + $this->validateAllPayments(); + } + + public function testNegativePaymentWithNegativeContribution() { + $params1 = [ + 'contact_id' => $this->_individualId, + 'trxn_id' => 111111, + 'total_amount' => -10, + ]; + $contributionID1 = $this->contributionCreate($params1); + $this->callAPISuccess('Payment', 'create', ['total_amount' => '-20', 'contribution_id' => $contributionID1]); + $paymentParams = ['contribution_id' => $contributionID1]; + $payment = $this->callAPISuccess('payment', 'get', $paymentParams); + $this->validateAllPayments(); + } + /** * Test email receipt for partial payment. * From 65b0b7c2d8bcf27a77113d5ee93692ce9b9d39d7 Mon Sep 17 00:00:00 2001 From: eileen Date: Tue, 1 Sep 2020 15:21:50 +1200 Subject: [PATCH 064/834] [REF] Clean up return variables on updateContributionStatus, updatePendingOnlineContribution The return variable from updatePendingOnlineContribution is never used - ergo don't return it. This means the return value from updateContributionStatus is also never used - ergo - you get it. In addition don't call the function with empty variables - I've moved the check as to whether they are empty into the calling function - although likely in a second pass the checks will be removed or change again --- CRM/Event/Form/Task/Batch.php | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/CRM/Event/Form/Task/Batch.php b/CRM/Event/Form/Task/Batch.php index f39d72ac20d7..16d8752bde39 100644 --- a/CRM/Event/Form/Task/Batch.php +++ b/CRM/Event/Form/Task/Batch.php @@ -260,14 +260,10 @@ public function postProcess() { * @param int $participantId * @param int $statusId * - * @return mixed * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ public static function updatePendingOnlineContribution($participantId, $statusId) { - if (!$participantId || !$statusId) { - return NULL; - } $contributionId = CRM_Contribute_BAO_Contribution::checkOnlinePendingContribution($participantId, 'Event' @@ -292,7 +288,7 @@ public static function updatePendingOnlineContribution($participantId, $statusId $contributionStatusId = array_search('Cancelled', $contributionStatuses); } - if (!$contributionStatusId) { + if (!$contributionStatusId || !$participantId || !$contributionId) { return; } @@ -305,9 +301,7 @@ public static function updatePendingOnlineContribution($participantId, $statusId ]; //change related contribution status. - $updatedStatusId = self::updateContributionStatus($params); - - return $updatedStatusId; + self::updateContributionStatus($params); } /** @@ -315,7 +309,6 @@ public static function updatePendingOnlineContribution($participantId, $statusId * * @param array $params * - * @return NULL|int * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception * @throws \Exception @@ -332,10 +325,6 @@ public static function updateContributionStatus($params) { $componentName = $params['componentName'] ?? NULL; $contributionId = $params['contribution_id'] ?? NULL; - if (!$contributionId || !$componentId || !$componentName || !$statusId) { - return NULL; - } - $input = $ids = $objects = []; //get the required ids. @@ -389,13 +378,13 @@ public static function updateContributionStatus($params) { $transaction = new CRM_Core_Transaction(); $baseIPN->cancelled($objects, $transaction, $input); $transaction->commit(); - return $statusId; + return; } - elseif ($statusId == $contributionStatuses['Failed']) { + if ($statusId == $contributionStatuses['Failed']) { $transaction = new CRM_Core_Transaction(); $baseIPN->failed($objects, $transaction, $input); $transaction->commit(); - return $statusId; + return; } // status is not pending @@ -432,8 +421,6 @@ public static function updateContributionStatus($params) { // reset template values before processing next transactions $template->clearTemplateVars(); - - return $statusId; } /** @@ -513,7 +500,7 @@ public function submit($params) { if ($statusChange) { CRM_Event_BAO_Participant::transitionParticipants([$key], $value['status_id'], $fromStatusId); } - if ($relatedStatusChange) { + if ($relatedStatusChange && $key && $value['status_id']) { //update related contribution status, CRM-4395 self::updatePendingOnlineContribution($key, $value['status_id']); } From 8e0eed0307118c6ed1cf19f5da3ba0930cb296e3 Mon Sep 17 00:00:00 2001 From: Noah Miller Date: Mon, 31 Aug 2020 23:38:30 -0700 Subject: [PATCH 065/834] Custom field create/edit form: replace dysfunctional checkbox with static label in edit mode --- templates/CRM/Custom/Form/Field.tpl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/templates/CRM/Custom/Form/Field.tpl b/templates/CRM/Custom/Form/Field.tpl index c50080cef884..fe0d6914825e 100644 --- a/templates/CRM/Custom/Form/Field.tpl +++ b/templates/CRM/Custom/Form/Field.tpl @@ -21,6 +21,9 @@ {$form.data_type.label} {$form.data_type.html} + {if $action neq 1 && $form.data_type.value[1][0] eq "Select" && $form.serialize.value} + ({ts}Multi-Select{/ts}) + {/if} {if $action neq 4 and $action neq 2}
{ts}Select the type of data you want to collect and store for this contact. Then select from the available HTML input field types (choices are based on the type of data being collected).{/ts} {/if} @@ -34,10 +37,12 @@ {/if} - - {$form.serialize.label} - {$form.serialize.html} - + {if $action eq 1} + + {$form.serialize.label} + {$form.serialize.html} + + {/if} {if $form.in_selector} {$form.in_selector.label} From 6f2bdb765f90c1c10a0ee6c123efd557788d6159 Mon Sep 17 00:00:00 2001 From: Andrew Hunt Date: Tue, 1 Sep 2020 11:08:02 -0400 Subject: [PATCH 066/834] 5.29.0 release notes: raw from script --- release-notes/5.29.0.md | 663 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 663 insertions(+) create mode 100644 release-notes/5.29.0.md diff --git a/release-notes/5.29.0.md b/release-notes/5.29.0.md new file mode 100644 index 000000000000..359cb80c51f6 --- /dev/null +++ b/release-notes/5.29.0.md @@ -0,0 +1,663 @@ +# CiviCRM 5.29.0 + +Released September 2, 2020; + +- **[Features](#features)** +- **[Bugs resolved](#bugs)** +- **[Miscellany](#misc)** +- **[Credits](#credits)** + +## Features + +### Core CiviCRM + +- **crm- Missing Summary ([17809](https://github.com/civicrm/civicrm-core/pull/17809))** + +- **CRM- Missing Summary ([17749](https://github.com/civicrm/civicrm-core/pull/17749))** + +## Bugs resolved + +### Core CiviCRM + +- **dev/core#1983 Fix to tax calculation on multi-line-item ([18290](https://github.com/civicrm/civicrm-core/pull/18290))** + +- **dev/core#1982 - 5.29 version of PR 18282 ([18301](https://github.com/civicrm/civicrm-core/pull/18301))** + +- **dev/core#1972 Fix tax_amount calclation on renewal form ([18271](https://github.com/civicrm/civicrm-core/pull/18271))** + +- **Update contributor key for Andrew ([18230](https://github.com/civicrm/civicrm-core/pull/18230))** + +- **5.28.2 & 5.29.3 Release Notes ([18228](https://github.com/civicrm/civicrm-core/pull/18228))** + +- **dev/core#1964 Fix regression bug on deduping contacts with dedupe_exception matches ([18223](https://github.com/civicrm/civicrm-core/pull/18223))** + +- **Fix dedupe regression whereby deleted contacts are found ([18214](https://github.com/civicrm/civicrm-core/pull/18214))** + +- **dev/core#1959 Brick\Math\Exception\RoundingNecessaryException ([18206](https://github.com/civicrm/civicrm-core/pull/18206))** + +- **Fix regression whereby deleted contacts are in quicksearch results ([18213](https://github.com/civicrm/civicrm-core/pull/18213))** + +- **dev/core#1963 - Expanded icon on manage groups appears as unknown icon ([18205](https://github.com/civicrm/civicrm-core/pull/18205))** + +- **dev/core#1961 Fix regression - cancel button not working on recurring contributions ([18204](https://github.com/civicrm/civicrm-core/pull/18204))** + +- **(dev/core#1846) Container, ClassLoader Caches - Separate caches by ve… ([18200](https://github.com/civicrm/civicrm-core/pull/18200))** + +- **dev/core#1945 Fix recur access regression ([18180](https://github.com/civicrm/civicrm-core/pull/18180))** + +- **dev/core#1937 - Upgrade message about needing composer patching turned on and updating mysql in DSN strings ([18174](https://github.com/civicrm/civicrm-core/pull/18174))** + +- **Revert "Swap out button/submit inputs for button elements" ([18185](https://github.com/civicrm/civicrm-core/pull/18185))** + +- **Installation doclinks not getting url-rewritten ([18175](https://github.com/civicrm/civicrm-core/pull/18175))** + +- **cvv required html attribute should depend on backoffice setting ([18166](https://github.com/civicrm/civicrm-core/pull/18166))** + +- **[REF] Remove unnecessary comma ([18163](https://github.com/civicrm/civicrm-core/pull/18163))** + +- **Fix 5.29 (unreleased) regression using temp tables ([18133](https://github.com/civicrm/civicrm-core/pull/18133))** + +- **dev/core#1952 Remove uncessary component checking when exporting all … ([18149](https://github.com/civicrm/civicrm-core/pull/18149))** + +- **dev/core#1953 Ensure that Contribution pages do not fail validation o… ([18144](https://github.com/civicrm/civicrm-core/pull/18144))** + +- **dev/core#1934 fix regression on merging contacts with settings using contact_id ([18126](https://github.com/civicrm/civicrm-core/pull/18126))** + +- **dev/core#1936 Make the price field value label field not required ([18123](https://github.com/civicrm/civicrm-core/pull/18123))** + +- **Swap out button/submit inputs for button elements ([18091](https://github.com/civicrm/civicrm-core/pull/18091))** + +- **dev/event#40 - EventCart - Check legacy setting until extension is public ([18101](https://github.com/civicrm/civicrm-core/pull/18101))** + +- **dev/wordpress#66 Re-instate newer variables but with more support for… ([18068](https://github.com/civicrm/civicrm-core/pull/18068))** + +- **[REF] Fix jquery validation for on behalf of fields when combined wit… ([18092](https://github.com/civicrm/civicrm-core/pull/18092))** + +- **dev/core#1895 fix first/last name adv search ([17950](https://github.com/civicrm/civicrm-core/pull/17950))** + +- **dev/core#1905 force backend links for new "configure" buttons ([18088](https://github.com/civicrm/civicrm-core/pull/18088))** + +- **dev/core#1932 - Make status-checks more polite during upgrade ([18085](https://github.com/civicrm/civicrm-core/pull/18085))** + +- **dev/core#1928 Fix HTML5 error due to required attribute being set swi… ([18080](https://github.com/civicrm/civicrm-core/pull/18080))** + +- **5.28 ([18084](https://github.com/civicrm/civicrm-core/pull/18084))** + +- **5.28 ([18082](https://github.com/civicrm/civicrm-core/pull/18082))** + +- **dev/financial#135 Remove stub function from payflowPro ([18078](https://github.com/civicrm/civicrm-core/pull/18078))** + +- **5.28 ([18077](https://github.com/civicrm/civicrm-core/pull/18077))** + +- **Fix button name on updated form ([18000](https://github.com/civicrm/civicrm-core/pull/18000))** + +- **[NFC] Fix provider unit test on PHP7.4 ([18073](https://github.com/civicrm/civicrm-core/pull/18073))** + +- **[REF] Move handling of form elements back to the Form ([17981](https://github.com/civicrm/civicrm-core/pull/17981))** + +- **Do not pass-by-reference to recur function ([18071](https://github.com/civicrm/civicrm-core/pull/18071))** + +- **dev/financial#135 Remove unreachable doDirectPayment from manual processor ([18072](https://github.com/civicrm/civicrm-core/pull/18072))** + +- **Refactor "applyLocale" and remove references to "language" column in UFMatch table ([18049](https://github.com/civicrm/civicrm-core/pull/18049))** + +- **Show cron warning on Scheduled Jobs admin page ([18065](https://github.com/civicrm/civicrm-core/pull/18065))** + +- **5.28 ([18069](https://github.com/civicrm/civicrm-core/pull/18069))** + +- **Use correct pdf package to generate pdf file on invoice download/email activity ([18056](https://github.com/civicrm/civicrm-core/pull/18056))** + +- **Fix buggy placement of icons on buttons ([18005](https://github.com/civicrm/civicrm-core/pull/18005))** + +- **[REF] Even less variable variables ([18058](https://github.com/civicrm/civicrm-core/pull/18058))** + +- **dev/core#1905 rework #17942 with simpler ts strings ([18064](https://github.com/civicrm/civicrm-core/pull/18064))** + +- **Extract code to set isEmailReceipt in Contribution.completeOrder ([18039](https://github.com/civicrm/civicrm-core/pull/18039))** + +- **[REF] remove first attempt to set currency in repeattransaction flow ([18055](https://github.com/civicrm/civicrm-core/pull/18055))** + +- **5.28 ([18063](https://github.com/civicrm/civicrm-core/pull/18063))** + +- **dev/core#1905 Add configure icons on public pages ([17942](https://github.com/civicrm/civicrm-core/pull/17942))** + +- **CRM_Utils_Hook: deprecation warning and short array syntax ([17995](https://github.com/civicrm/civicrm-core/pull/17995))** + +- **event#35: move statusBounce out of BAO layer; don't allow self-service when dis… ([18040](https://github.com/civicrm/civicrm-core/pull/18040))** + +- **Why not make the buttons flat? ([18054](https://github.com/civicrm/civicrm-core/pull/18054))** + +- **[REF] Simplify location metadata handling in Export class ([17951](https://github.com/civicrm/civicrm-core/pull/17951))** + +- **5.28 ([18059](https://github.com/civicrm/civicrm-core/pull/18059))** + +- **[REF] Do not pass by reference to the recur function ([18057](https://github.com/civicrm/civicrm-core/pull/18057))** + +- **[REF] Simplify getMembershipStatusByDate more ([18051](https://github.com/civicrm/civicrm-core/pull/18051))** + +- **Fix obscure dedupe scenario where 'bad' location data can overwrite good data ([17993](https://github.com/civicrm/civicrm-core/pull/17993))** + +- **dev/drupal#127 - require email for the Useradd task and errors not showing ([17915](https://github.com/civicrm/civicrm-core/pull/17915))** + +- **dev/core#1921 [Ref] remove isoToMysql ([18025](https://github.com/civicrm/civicrm-core/pull/18025))** + +- **dev/core#1665 Remove the having clause as well as having needs a group by ([18052](https://github.com/civicrm/civicrm-core/pull/18052))** + +- **Remove ORDER BY and GROUP BY from alphabetQuery to improve performance ([16877](https://github.com/civicrm/civicrm-core/pull/16877))** + +- **Fix JQuery Validation for radios ([17937](https://github.com/civicrm/civicrm-core/pull/17937))** + +- **[REF] Simplify membership status date handling ([18030](https://github.com/civicrm/civicrm-core/pull/18030))** + +- **Fix PaypalIPN single function to not receive-by-reference ([18044](https://github.com/civicrm/civicrm-core/pull/18044))** + +- **Remove requirement to pass 'contribution_status_id' => Pending from order.create ([18018](https://github.com/civicrm/civicrm-core/pull/18018))** + +- **[REF] Clean up handling of financial_type_id override ([18032](https://github.com/civicrm/civicrm-core/pull/18032))** + +- **Use saved contribution's line items rather than the primaryContributionID ([18033](https://github.com/civicrm/civicrm-core/pull/18033))** + +- **[REF] Remove transaction from completeOrder signature ([18046](https://github.com/civicrm/civicrm-core/pull/18046))** + +- **Wrap multi record custom field inside a div ([17966](https://github.com/civicrm/civicrm-core/pull/17966))** + +- **[Test fix] We might need this to ensure really quick test runs don't fail ([18045](https://github.com/civicrm/civicrm-core/pull/18045))** + +- **dev/core#1137 - Allow ssl connection to mysql by specifying in DSN ([17706](https://github.com/civicrm/civicrm-core/pull/17706))** + +- **Test - attempt to replicate #17852 ([18038](https://github.com/civicrm/civicrm-core/pull/18038))** + +- **[REF] Remove transaction from BaseIPN completeTransaction call ([18042](https://github.com/civicrm/civicrm-core/pull/18042))** + +- **Remove url-tracking in mass sms. dev/core#1843 ([17700](https://github.com/civicrm/civicrm-core/pull/17700))** + +- **Do not overwrite values saved from the repeatContribution routine ([17972](https://github.com/civicrm/civicrm-core/pull/17972))** + +- **dev/core#1916 - Fix naming of case export fields / remove ones that aren't true ([18043](https://github.com/civicrm/civicrm-core/pull/18043))** + +- **SystemCheck: add ability to efficiently run only specified checks ([17824](https://github.com/civicrm/civicrm-core/pull/17824))** + +- **Add testing to Authorize.net and remove the lines that are repeated ([18028](https://github.com/civicrm/civicrm-core/pull/18028))** + +- **Add test on status calculation ([18037](https://github.com/civicrm/civicrm-core/pull/18037))** + +- **Fix qill typo ([18041](https://github.com/civicrm/civicrm-core/pull/18041))** + +- **dev/core#1919 - Missing resubscribe url in text/plain version of unsubscribe confirmation email ([18015](https://github.com/civicrm/civicrm-core/pull/18015))** + +- **[Ref] Simplify is_email_receipt in sendMail ([18029](https://github.com/civicrm/civicrm-core/pull/18029))** + +- **Update flexmailer release information ([17912](https://github.com/civicrm/civicrm-core/pull/17912))** + +- **[REF] [Test] Minor simplification on test ([18019](https://github.com/civicrm/civicrm-core/pull/18019))** + +- **dev/core#1906 - Allow payment create api to record payment on Failed … ([17943](https://github.com/civicrm/civicrm-core/pull/17943))** + +- **Fix for failing test ([18036](https://github.com/civicrm/civicrm-core/pull/18036))** + +- **Remove invalid use of crmMoney formatter ([18031](https://github.com/civicrm/civicrm-core/pull/18031))** + +- **Remove main PaymentExpress class ([18010](https://github.com/civicrm/civicrm-core/pull/18010))** + +- **[Ref] Remove transaction instantiation in PaypalPro ([18026](https://github.com/civicrm/civicrm-core/pull/18026))** + +- **[REF] Stop instantiating transaction in PaypalIPN ([18020](https://github.com/civicrm/civicrm-core/pull/18020))** + +- **Remove unused parameter ids['billing'] ([18021](https://github.com/civicrm/civicrm-core/pull/18021))** + +- **5.28 to master ([18023](https://github.com/civicrm/civicrm-core/pull/18023))** + +- **Cache loader - remove legacy handling, handle null result from setting ([17999](https://github.com/civicrm/civicrm-core/pull/17999))** + +- **Change inform-icon to fa-info-circle ([18001](https://github.com/civicrm/civicrm-core/pull/18001))** + +- **[REF] Remove pass-by-reference & always empty param ([17984](https://github.com/civicrm/civicrm-core/pull/17984))** + +- **CIVICRM_BAO_CACHE_ADAPTER - Remove obsolete option ([17990](https://github.com/civicrm/civicrm-core/pull/17990))** + +- **SQL temp table not using utf8mb4 if server default already set to utf8mb4 ([18012](https://github.com/civicrm/civicrm-core/pull/18012))** + +- **Wrong link to admin page in error message about FROM address on PCP page ([17996](https://github.com/civicrm/civicrm-core/pull/17996))** + +- **[REF] Tighten up function signature for dedupePair ([17923](https://github.com/civicrm/civicrm-core/pull/17923))** + +- **[Ref] Move noisily deprecate BaseIPN->sendMail, call api from it rather than BAO function ([17982](https://github.com/civicrm/civicrm-core/pull/17982))** + +- **[REF] Use CRM_Utils_Mail::send for sending emails for confirming unsu… ([17396](https://github.com/civicrm/civicrm-core/pull/17396))** + +- **CRM_Core_BAO_Cache - Remove functions deprecated a year ago ([17989](https://github.com/civicrm/civicrm-core/pull/17989))** + +- **[Test framework] re-re-fix test and add test for test ([18013](https://github.com/civicrm/civicrm-core/pull/18013))** + +- **Re-fix test ([18009](https://github.com/civicrm/civicrm-core/pull/18009))** + +- **[NFC] Improve docs for APIv4 Save action ([18004](https://github.com/civicrm/civicrm-core/pull/18004))** + +- **dev/core#1909 - E_NOTICE on contribution edit ([18006](https://github.com/civicrm/civicrm-core/pull/18006))** + +- **dev/core#1918 - Remove dubious qfkey checking code that never runs ([18007](https://github.com/civicrm/civicrm-core/pull/18007))** + +- **5.28 ([18008](https://github.com/civicrm/civicrm-core/pull/18008))** + +- **[REF] Reduce calls to CRM_Member_PseudoConstant::membershipType ([17987](https://github.com/civicrm/civicrm-core/pull/17987))** + +- **dev/core#1915 - E_NOTICE when making pcp contribution ([18002](https://github.com/civicrm/civicrm-core/pull/18002))** + +- **[Test framework] - Update failing test ([18003](https://github.com/civicrm/civicrm-core/pull/18003))** + +- **Fix repeattransaction api to use custom data from the template contribution ([17975](https://github.com/civicrm/civicrm-core/pull/17975))** + +- **dev/financial#139 contribution receive_date shouldn't change when payments come in ([17777](https://github.com/civicrm/civicrm-core/pull/17777))** + +- **5.28 ([17997](https://github.com/civicrm/civicrm-core/pull/17997))** + +- **Fix case activity field set to allow long details to be exported ([17970](https://github.com/civicrm/civicrm-core/pull/17970))** + +- **dev/core#1913 Allow for schemas to be added by extensions if they are… ([17986](https://github.com/civicrm/civicrm-core/pull/17986))** + +- **[REF] Use Standard function cacheClause to re-use contact acl cache t… ([17707](https://github.com/civicrm/civicrm-core/pull/17707))** + +- **Fixed filling default values for tagssets in the advanced search form ([17978](https://github.com/civicrm/civicrm-core/pull/17978))** + +- **dev/core#1909 - Avoid E_Notice on SMS provider form when no default url ([17985](https://github.com/civicrm/civicrm-core/pull/17985))** + +- **Remove duplicate cache flush ([17988](https://github.com/civicrm/civicrm-core/pull/17988))** + +- **[REF] Extract setUserContext on contribution form & cleanup on backend add membership form ([17968](https://github.com/civicrm/civicrm-core/pull/17968))** + +- **dev/core#1755 Fix reCaptcha on Mailing Subscribe ([17305](https://github.com/civicrm/civicrm-core/pull/17305))** + +- **[REF] Make explicit what we are doing with 'values' in this code ([17979](https://github.com/civicrm/civicrm-core/pull/17979))** + +- **Simplify caching of status checks ([17817](https://github.com/civicrm/civicrm-core/pull/17817))** + +- **[REF] Reduce interaction between dedupe code and createProfileContact ([17920](https://github.com/civicrm/civicrm-core/pull/17920))** + +- **ensure custom field checkboxes are populated in profiles ([17977](https://github.com/civicrm/civicrm-core/pull/17977))** + +- **dev/core#785 Differentiate smart group from regular group using icon in select2 field ([17927](https://github.com/civicrm/civicrm-core/pull/17927))** + +- **[REF] Minor code clean up ([17974](https://github.com/civicrm/civicrm-core/pull/17974))** + +- **5.28 to master ([17976](https://github.com/civicrm/civicrm-core/pull/17976))** + +- **[REF] Grant cleanup ([17967](https://github.com/civicrm/civicrm-core/pull/17967))** + +- **[NFC] Add in a unit test of calling the contribution page widget endp… ([17965](https://github.com/civicrm/civicrm-core/pull/17965))** + +- **5.28 ([17963](https://github.com/civicrm/civicrm-core/pull/17963))** + +- **dev/core#1909 Fix E-notice when adding a field on a profile ([17962](https://github.com/civicrm/civicrm-core/pull/17962))** + +- **dev/core#1909 Fix e-notice when adding a payment processor ([17964](https://github.com/civicrm/civicrm-core/pull/17964))** + +- **dev/core#1909 - E_NOTICE opening file-on-case ([17959](https://github.com/civicrm/civicrm-core/pull/17959))** + +- **[Ref] Simplify field reference ([17941](https://github.com/civicrm/civicrm-core/pull/17941))** + +- **[Test framework] - Combine triplicate createCase functions ([17957](https://github.com/civicrm/civicrm-core/pull/17957))** + +- **[REF] Refactor to use the standard CRM_Core_Form::addRadio function f… ([17932](https://github.com/civicrm/civicrm-core/pull/17932))** + +- **Upgrade PEAR/mail_mime package to be compliant with PHP7.4 and deploy it using composer ([17948](https://github.com/civicrm/civicrm-core/pull/17948))** + +- **[REF] Fix the default to_financial_account_id for generated transactions ([17938](https://github.com/civicrm/civicrm-core/pull/17938))** + +- **APIv4 - Add keyword to select all custom fields ([17955](https://github.com/civicrm/civicrm-core/pull/17955))** + +- **[REF] Upgrade dompdf version to be more compatible with PHP7.4 ([17946](https://github.com/civicrm/civicrm-core/pull/17946))** + +- **[REF] [Tests] Cleanup test declaration to take advantage of mapping improvements ([17939](https://github.com/civicrm/civicrm-core/pull/17939))** + +- **dev/drupal#127 - CRM_Core_Session::setStatus() gets ignored sometimes ([17914](https://github.com/civicrm/civicrm-core/pull/17914))** + +- **5.28 to master ([17953](https://github.com/civicrm/civicrm-core/pull/17953))** + +- **[REF] Remove unnecessary complexity on im export ([17949](https://github.com/civicrm/civicrm-core/pull/17949))** + +- **NFC - Docblock cleanup ([17945](https://github.com/civicrm/civicrm-core/pull/17945))** + +- **CRM_Utils_Check_Component_Case - Guard against post-upgrade crash ([17944](https://github.com/civicrm/civicrm-core/pull/17944))** + +- **Remove error checking by-pass in tests ([17940](https://github.com/civicrm/civicrm-core/pull/17940))** + +- **Remove extraneous opportunistic cache flush. ([17936](https://github.com/civicrm/civicrm-core/pull/17936))** + +- **dev/core#1902: "Contribution Source" profile field has no effect ([17930](https://github.com/civicrm/civicrm-core/pull/17930))** + +- **[REF] GroupContact BAO - Minor code cleanup ([17928](https://github.com/civicrm/civicrm-core/pull/17928))** + +- **5.28 to master ([17931](https://github.com/civicrm/civicrm-core/pull/17931))** + +- **[REF] Minor function signuture cleanup ([17922](https://github.com/civicrm/civicrm-core/pull/17922))** + +- **[REF] Do not pass variable by reference ([17921](https://github.com/civicrm/civicrm-core/pull/17921))** + +- **5.28 ([17926](https://github.com/civicrm/civicrm-core/pull/17926))** + +- **[NFC] Update a few doc/wiki links in code comments ([17918](https://github.com/civicrm/civicrm-core/pull/17918))** + +- **Improve caching of current domain ([17916](https://github.com/civicrm/civicrm-core/pull/17916))** + +- **Setup UI - Validate that at least one "Component" is enabled ([17778](https://github.com/civicrm/civicrm-core/pull/17778))** + +- **Member detail report: nest "in" options in parentheses ([17911](https://github.com/civicrm/civicrm-core/pull/17911))** + +- **Fix sticky table header on "Find Activities" page ([17917](https://github.com/civicrm/civicrm-core/pull/17917))** + +- **5.28 ([17908](https://github.com/civicrm/civicrm-core/pull/17908))** + +- **[REF] remove unnecessary variable variables ([17903](https://github.com/civicrm/civicrm-core/pull/17903))** + +- **dev/core#1855 - Allow different output formats for CiviReport results and untangle code ([17901](https://github.com/civicrm/civicrm-core/pull/17901))** + +- **[REF] - Add helper function for the repetitive task of fetching multilingual ([17650](https://github.com/civicrm/civicrm-core/pull/17650))** + +- **[unreleased regression] Dummy processor now is ID 7 on buildkit sites ([17905](https://github.com/civicrm/civicrm-core/pull/17905))** + +- **Remove unused "ufUniqID" session variable ([17904](https://github.com/civicrm/civicrm-core/pull/17904))** + +- **Replace a load of references to the wiki with docs links ([17900](https://github.com/civicrm/civicrm-core/pull/17900))** + +- **dev/core#1894 - Make CRM_Activity_Form_SearchTest::testQill less time-sensitive ([17902](https://github.com/civicrm/civicrm-core/pull/17902))** + +- **dev/core#183 Use TempTable builder to generate table for import ([17827](https://github.com/civicrm/civicrm-core/pull/17827))** + +- **Remove check for valid email in synchronizeUFMatch ([17771](https://github.com/civicrm/civicrm-core/pull/17771))** + +- **Call apiv4 from Contribution create rather than fugly addActivity function ([17881](https://github.com/civicrm/civicrm-core/pull/17881))** + +- **APIv4 - Add BasicEntity helper class ([17899](https://github.com/civicrm/civicrm-core/pull/17899))** + +- **mailing#70 Don't create users for test mail if user doesn't have permission ([17867](https://github.com/civicrm/civicrm-core/pull/17867))** + +- **Add APIv4 and pseudoconstants for RelationshipCache ([17879](https://github.com/civicrm/civicrm-core/pull/17879))** + +- **dev/core#1670 copy custom fields from master to shared address ([17580](https://github.com/civicrm/civicrm-core/pull/17580))** + +- **[Ref] Unit test attempt to create reported bugs , minor cleanup ([17560](https://github.com/civicrm/civicrm-core/pull/17560))** + +- **Update version in the test_data_second_domain file and also update th… ([17897](https://github.com/civicrm/civicrm-core/pull/17897))** + +- **dev/core#1888 - Fix one line in PR 17888 ([17898](https://github.com/civicrm/civicrm-core/pull/17898))** + +- **Be a little less supportive to cvs ([17896](https://github.com/civicrm/civicrm-core/pull/17896))** + +- **dev/core#1888 and dev/core#1885 - Fatal error on advanced search and warnings and missing group display on contact form ([17888](https://github.com/civicrm/civicrm-core/pull/17888))** + +- **APIv4 - Specify BridgeEntities to assist with joins ([17808](https://github.com/civicrm/civicrm-core/pull/17808))** + +- **Contribution Summary Report: Taking the currency filtered in the "gen… ([16736](https://github.com/civicrm/civicrm-core/pull/16736))** + +- **Event Cart ext: Move menu entries to extension ([17891](https://github.com/civicrm/civicrm-core/pull/17891))** + +- **5.28 ([17895](https://github.com/civicrm/civicrm-core/pull/17895))** + +- **[Test Framework] - Tests for report downloads ([17892](https://github.com/civicrm/civicrm-core/pull/17892))** + +- **dev/core#1578 - Fix APIv4 chaining with custom fields ([17866](https://github.com/civicrm/civicrm-core/pull/17866))** + +- **Unit test for #17361 ([17882](https://github.com/civicrm/civicrm-core/pull/17882))** + +- **EventCart ext: Cleanup and move form components to ext ([17885](https://github.com/civicrm/civicrm-core/pull/17885))** + +- **EventCart ext: Fix autogenerated code, remove unused hooks, update readme ([17884](https://github.com/civicrm/civicrm-core/pull/17884))** + +- **Load contribution page if live payment processor is disabled but test is available ([17828](https://github.com/civicrm/civicrm-core/pull/17828))** + +- **Search debug ([17887](https://github.com/civicrm/civicrm-core/pull/17887))** + +- **dev/core#1767 Fix phone key parsing in CRM_Dedupe_Finder ([17361](https://github.com/civicrm/civicrm-core/pull/17361))** + +- **[NFC] Fix nonstandard header comments ([17880](https://github.com/civicrm/civicrm-core/pull/17880))** + +- **dev/core#1751: [Create Email] Only Show Update/Save Template when User has Permission to Edit Templates ([17480](https://github.com/civicrm/civicrm-core/pull/17480))** + +- **Use new checkPermissions shorthand in api calls ([17874](https://github.com/civicrm/civicrm-core/pull/17874))** + +- **5.28 ([17878](https://github.com/civicrm/civicrm-core/pull/17878))** + +- **Simplify flushing group contact cache query to reduce table locking and improve performance ([17846](https://github.com/civicrm/civicrm-core/pull/17846))** + +- **dev/core#1869 - Include BOM in attachment when sending CSV CiviReport via mail_report job ([17806](https://github.com/civicrm/civicrm-core/pull/17806))** + +- **dev/core#1280 Fix ContributionPage soft_credit translation ([16838](https://github.com/civicrm/civicrm-core/pull/16838))** + +- **[NFC] Comment block cleanup ([17872](https://github.com/civicrm/civicrm-core/pull/17872))** + +- **REF Extract addToRecentItems from membership create ([17524](https://github.com/civicrm/civicrm-core/pull/17524))** + +- **Disable frequency/interval fields if not required. Mark required if they are so they are validated before submit ([17526](https://github.com/civicrm/civicrm-core/pull/17526))** + +- **dev/membership#18 Enhance parameters for Job.process_membership ([16298](https://github.com/civicrm/civicrm-core/pull/16298))** + +- **Fix currency symbol for Total Amount on contribution page ([17703](https://github.com/civicrm/civicrm-core/pull/17703))** + +- **RelationshipCache - Add a high-level index to facilitate relationship queries (more fields) ([17781](https://github.com/civicrm/civicrm-core/pull/17781))** + +- **[REF] Fix a couple of jQuery errors that have cropped up ([17871](https://github.com/civicrm/civicrm-core/pull/17871))** + +- **Hooks/Dispatcher - Close loopholes that occur around "preboot" hooks ([17831](https://github.com/civicrm/civicrm-core/pull/17831))** + +- **APIv4 - Add shorthand for setCheckPermissions() ([17834](https://github.com/civicrm/civicrm-core/pull/17834))** + +- **Use PrematureExit exception instead of weird hack in tests ([17870](https://github.com/civicrm/civicrm-core/pull/17870))** + +- **dev/core#1113 - Decimal Separator - Invalid value "total_amount" (NaN,N) creating or editing a membership ([16429](https://github.com/civicrm/civicrm-core/pull/16429))** + +- **(REF) regen.sh - Remove unusual handling of `zipcodes.mysql` ([17869](https://github.com/civicrm/civicrm-core/pull/17869))** + +- **dev/report#43 - Icon after saving a civireport instance is misleading ([17863](https://github.com/civicrm/civicrm-core/pull/17863))** + +- **Remove unnecessary try/catch per #17729 ([17823](https://github.com/civicrm/civicrm-core/pull/17823))** + +- **dev/core#1725 Only export primary address fields ([17458](https://github.com/civicrm/civicrm-core/pull/17458))** + +- **api_v3_TaxContributionPageTest fix - remove hard coded processor id ([17860](https://github.com/civicrm/civicrm-core/pull/17860))** + +- **Fixed DB Error: syntax error if line item refers to civicrm_case ([16626](https://github.com/civicrm/civicrm-core/pull/16626))** + +- **Fix potential js error on summary screen when reloading blocks ([17865](https://github.com/civicrm/civicrm-core/pull/17865))** + +- **Search Ext: fix loading options and parsing custom field names ([17864](https://github.com/civicrm/civicrm-core/pull/17864))** + +- **EventCart - Resolve BAO identity and uncommitted DAO changes ([17861](https://github.com/civicrm/civicrm-core/pull/17861))** + +- **[REF] ScheduledJob cleanup, remove unused var ([17862](https://github.com/civicrm/civicrm-core/pull/17862))** + +- **dev/core#1090 Update extendedSerializeData to use the Backbone namesp… ([17855](https://github.com/civicrm/civicrm-core/pull/17855))** + +- **dev/core#1874 - Failing test for new Individual form ([17835](https://github.com/civicrm/civicrm-core/pull/17835))** + +- **Bump lodash from 4.17.15 to 4.17.19 ([17858](https://github.com/civicrm/civicrm-core/pull/17858))** + +- **5.28 ([17859](https://github.com/civicrm/civicrm-core/pull/17859))** + +- **[REF] Migrate Event Cart Setting into the Extension ([17841](https://github.com/civicrm/civicrm-core/pull/17841))** + +- **5.28 ([17856](https://github.com/civicrm/civicrm-core/pull/17856))** + +- **handlePaymentNotification() should not be declared as a static method ([17849](https://github.com/civicrm/civicrm-core/pull/17849))** + +- **[REF] Only printOnly once ([17850](https://github.com/civicrm/civicrm-core/pull/17850))** + +- **API tests - label versions in dataprovider versionThreeAndFour ([17847](https://github.com/civicrm/civicrm-core/pull/17847))** + +- **dev/core#1880 add backticks to custom field insertions ([17848](https://github.com/civicrm/civicrm-core/pull/17848))** + +- **(REF) APIv4 ConformanceTest - Split apart into per-entity sub-tests ([17845](https://github.com/civicrm/civicrm-core/pull/17845))** + +- **dev/core#1872 - Packages and vendor path calculation used in system check is outdated ([17844](https://github.com/civicrm/civicrm-core/pull/17844))** + +- **[NFC] Re run regen after recent merges ([17842](https://github.com/civicrm/civicrm-core/pull/17842))** + +- **5.28 ([17843](https://github.com/civicrm/civicrm-core/pull/17843))** + +- **Move BAO and template files into event cart ([17743](https://github.com/civicrm/civicrm-core/pull/17743))** + +- **Update regen.sh with new & upcoming core extensions ([17839](https://github.com/civicrm/civicrm-core/pull/17839))** + +- **MembershipRenewalTest - Fix failure ([17830](https://github.com/civicrm/civicrm-core/pull/17830))** + +- **Remove hard coded charset. ([17826](https://github.com/civicrm/civicrm-core/pull/17826))** + +- **(REF) WebsiteTest - Mitigate flaky failures ([17833](https://github.com/civicrm/civicrm-core/pull/17833))** + +- **dev/core#1812 Missing view when logging set in a non-US English instance ([17815](https://github.com/civicrm/civicrm-core/pull/17815))** + +- **dev/core#785 Differentiate smart group from regular group using icon in select2 field ([13958](https://github.com/civicrm/civicrm-core/pull/13958))** + +- **APIv4 - Add activity contacts to APIv4 field spec ([17766](https://github.com/civicrm/civicrm-core/pull/17766))** + +- **Adjust mysql SET NAMES in remaining places as we agreed this was the go ([17825](https://github.com/civicrm/civicrm-core/pull/17825))** + +- **core#1805: Autocomplete-select custom field is not searchable ([17569](https://github.com/civicrm/civicrm-core/pull/17569))** + +- **event#38 fix wording on event reg page ([17695](https://github.com/civicrm/civicrm-core/pull/17695))** + +- **core#1795: Searchable Parent tags ([17513](https://github.com/civicrm/civicrm-core/pull/17513))** + +- **dev/core#1768 - Add CiviMail synchronisation frequency setting. ([17709](https://github.com/civicrm/civicrm-core/pull/17709))** + +- **Make new email open and url routes 'public' ([17813](https://github.com/civicrm/civicrm-core/pull/17813))** + +- **dev/core#1871 - require_once's that include "packages/" in the path don't work on drupal 8 ([17822](https://github.com/civicrm/civicrm-core/pull/17822))** + +- **Make api get upgrade-safe ([17729](https://github.com/civicrm/civicrm-core/pull/17729))** + +- **5.28 ([17799](https://github.com/civicrm/civicrm-core/pull/17799))** + +- **CRM_Utils_SQL - Add "onDuplicate()" and "syncInto()" helpers ([17780](https://github.com/civicrm/civicrm-core/pull/17780))** + +- **CheckEnv - Give new installs a grace period before 'Cron Not Running' msg ([17800](https://github.com/civicrm/civicrm-core/pull/17800))** + +- **Fix 'Undefined variable: jsSet in CRM_Core_BAO_Mapping::loadSavedMapping()' ([17816](https://github.com/civicrm/civicrm-core/pull/17816))** + +- **dev/core#1861 fix failure to unset location_type_id when saving uffield ([17812](https://github.com/civicrm/civicrm-core/pull/17812))** + +- **dev/core#1863 Downgrade checkEnvironment level and skip non-prod checks ([17807](https://github.com/civicrm/civicrm-core/pull/17807))** + +- **reporting#21 - don't multiple contribution details when a 1-to-many r… ([15435](https://github.com/civicrm/civicrm-core/pull/15435))** + +- **Add hidden tag to search extension ([17789](https://github.com/civicrm/civicrm-core/pull/17789))** + +- **[REF] Follow up cleanup ([17788](https://github.com/civicrm/civicrm-core/pull/17788))** + +- **[REF] Remove ACL join on temp table creation in Member ContributionDe… ([17723](https://github.com/civicrm/civicrm-core/pull/17723))** + +- **dev/core#1858 Prevent Duplicate contact records being created and har… ([17769](https://github.com/civicrm/civicrm-core/pull/17769))** + +- **REF - Cleanup StatusPreference BAO to be more standard ([17801](https://github.com/civicrm/civicrm-core/pull/17801))** + +- **Sort permittedActivityTypes ([17794](https://github.com/civicrm/civicrm-core/pull/17794))** + +- **[REF] Do or do not - there is no try ([17795](https://github.com/civicrm/civicrm-core/pull/17795))** + +- **Add auto-renew status to membership detail report ([17683](https://github.com/civicrm/civicrm-core/pull/17683))** + +- **APIv4 - Fix saving custom fields with same name ([17791](https://github.com/civicrm/civicrm-core/pull/17791))** + +- **Add system check to ensure WP base page exists ([17698](https://github.com/civicrm/civicrm-core/pull/17698))** + +- **dev/core#1868 - Regression - Description field is always blank on profiles admin page and slew of E_NOTICES ([17786](https://github.com/civicrm/civicrm-core/pull/17786))** + +- **Fixed notice error on Relationships report ([17787](https://github.com/civicrm/civicrm-core/pull/17787))** + +- **getLoggedInContactID() is a static function ([17783](https://github.com/civicrm/civicrm-core/pull/17783))** + +- **5.28 ([17782](https://github.com/civicrm/civicrm-core/pull/17782))** + +- **dev/core#1679: Ensure Paypal IPN always updates the next scheduled payment date ([17744](https://github.com/civicrm/civicrm-core/pull/17744))** + +- **5.28 ([17774](https://github.com/civicrm/civicrm-core/pull/17774))** + +- **dev/core#1853 - Fix validation errors when removing contact subtype ([17765](https://github.com/civicrm/civicrm-core/pull/17765))** + +- **Add search extension ([17775](https://github.com/civicrm/civicrm-core/pull/17775))** + +- **[dev/core#750] Don't check server variables if we're running in CLI ([17636](https://github.com/civicrm/civicrm-core/pull/17636))** + +- **Remove PaymentExpress ipn class ([17763](https://github.com/civicrm/civicrm-core/pull/17763))** + +- **Status Checks - Use more specific label regarding "Domain"/"Organization" check ([17776](https://github.com/civicrm/civicrm-core/pull/17776))** + +- **5.28 to master ([17770](https://github.com/civicrm/civicrm-core/pull/17770))** + +- **dev/drupal#114 and dev/core#1647 - Remove resource url status check ([17754](https://github.com/civicrm/civicrm-core/pull/17754))** + +- **Bump minimum upgradable version to 4.4.7 ([17750](https://github.com/civicrm/civicrm-core/pull/17750))** + +- **[REF] Unused interface CRM_Report_Interface ([17767](https://github.com/civicrm/civicrm-core/pull/17767))** + +- **[TEST] Failing test for PR 16559 ([17256](https://github.com/civicrm/civicrm-core/pull/17256))** + +- **update to pr 16559 ([17764](https://github.com/civicrm/civicrm-core/pull/17764))** + +- **Remove unused, deprecated functions ([17761](https://github.com/civicrm/civicrm-core/pull/17761))** + +- **Improve efficiency of findFiles ([17745](https://github.com/civicrm/civicrm-core/pull/17745))** + +- **APIv4 Explorer: Improve selection of fields for HAVING ([17746](https://github.com/civicrm/civicrm-core/pull/17746))** + +- **More unused functions in GenCode ([17756](https://github.com/civicrm/civicrm-core/pull/17756))** + +- **Fix PHP notice on wordpress permissions form ([17758](https://github.com/civicrm/civicrm-core/pull/17758))** + +- **core#1826: Ignore location_type_id when deduping postal address ([17645](https://github.com/civicrm/civicrm-core/pull/17645))** + +- **[Unit Test] dev/core#1173 and dev/core#1827 - Test for activity tag search - PR 17655 ([17755](https://github.com/civicrm/civicrm-core/pull/17755))** + +- **Convert CRM.utils.formatDate tests to karma ([17757](https://github.com/civicrm/civicrm-core/pull/17757))** + +- **dev/core#1827 activity search - fixing search by tags ([17655](https://github.com/civicrm/civicrm-core/pull/17655))** + +- **Unused functions in GenCode ([17753](https://github.com/civicrm/civicrm-core/pull/17753))** + +- **5.28 ([17751](https://github.com/civicrm/civicrm-core/pull/17751))** + +- **Teach CRM.utils.formatDate to also show time ([17684](https://github.com/civicrm/civicrm-core/pull/17684))** + +- **Fix typo in templates/CRM/Report/Form/Tabs/GroupBy.tpl ([17747](https://github.com/civicrm/civicrm-core/pull/17747))** + +- **Add eventcart shell ([17741](https://github.com/civicrm/civicrm-core/pull/17741))** + +- **dev/core#1966 Fix Output for StateProvince and Country Fields ([620](https://github.com/civicrm/civicrm-drupal/pull/620))** + +- **Fixed for multi-select filter ([615](https://github.com/civicrm/civicrm-drupal/pull/615))** + +- **fix url for file field ([608](https://github.com/civicrm/civicrm-drupal/pull/608))** + +- **[Test] Update hook signature in test ([609](https://github.com/civicrm/civicrm-drupal/pull/609))** + +- **NFC - Docblock cleanup ([610](https://github.com/civicrm/civicrm-drupal/pull/610))** + +- **Installation - Support "activate first" w/setup UI ([606](https://github.com/civicrm/civicrm-drupal/pull/606))** + +- **Slow down the frequency of WordPress "heartbeat" calls in CiviCRM admin ([214](https://github.com/civicrm/civicrm-wordpress/pull/214))** + +- **Remove unused "ufUniqID" session variable ([213](https://github.com/civicrm/civicrm-wordpress/pull/213))** + +- **5.28 to master ([212](https://github.com/civicrm/civicrm-wordpress/pull/212))** + +- **Installation - Support "activate first" w/setup UI ([121](https://github.com/civicrm/civicrm-backdrop/pull/121))** + +- **[NFC] Update versions file to remove reference to Mail_mime and Mail ([301](https://github.com/civicrm/civicrm-packages/pull/301))** + +- **[REF] Remove mail_mime package as now supplied by composer ([300](https://github.com/civicrm/civicrm-packages/pull/300))** + +- **Remove Net packages Net_Curl Net_DIME as they do not appear to be used ([294](https://github.com/civicrm/civicrm-packages/pull/294))** + +- **[REF] Remove some deprecated size function calls replaced with length ([299](https://github.com/civicrm/civicrm-packages/pull/299))** + +## Miscellany + +## Credits + +This release was developed by the following code authors: + +AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman; Christian Wach; Circle Interactive - Pradeep Nayak; CiviCoop - Jaap Jansma, Klaas Eikelboom; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; Coop SymbioTIC - Mathieu Lutfy, Samuel Vanhove; Dave D; dependabot[bot]; Fuzion - Jitendra Purohit; iXiam - César Ramos; JMA Consulting - Monish Deb, Seamus Lee; John Kingsnorth; Joinery - Allen Shaw; Lighthouse Consulting and Design - Brian Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MillerTech - Chamil Wijesooriya; MJCO - Mikey O'Toole; MJW Consulting - Matthew Wire; muniodiego; Progressive Technology Project - Jamie McClelland; Squiffle Consulting - Aidan Saunders; Stephen Palmstrom; Tadpole Collective - Kevin Cristiano; Timbsoft Technologies - Tunbola Ogunwande; Wikimedia Foundation - Eileen McNaughton + +Most authors also reviewed code for this release; in addition, the following +reviewers contributed their comments: + +AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman, Pengyi Zhang; Andrew Thompson; Artful Robot - Rich Lott; Australian Greens - Andrew Cormick-Dockery, John Twyman; Bastien Ho; Blackfly Solutions - Alan Dixon; Christian Wach; Circle Interactive - Pradeep Nayak; civibot[bot]; CiviCoop - Jaap Jansma, Klaas Eikelboom; civicrm-builder; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; CompuCorp - Jamie Novick; Coop SymbioTIC - Mathieu Lutfy, Samuel Vanhove; Dave D; DevApp - Adam Kwiatkowski; elcapo; Francesc Bassas i Bullich; Freeform Solutions - Herb van den Dool; Fuzion - Jitendra Purohit; Greenpeace Central and Eastern Europe - Patrick Figel; irenemeisel; iXiam - César Ramos; JMA Consulting - Joe Murray, Monish Deb, Seamus Lee; Joinery - Allen Shaw; Korlon - Stuart Gaston; Lighthouse Consulting and Design - Brian Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MillerTech - Chamil Wijesooriya; MJCO - Mikey O'Toole; MJW Consulting - Matthew Wire; Nicol Wistreich; Progressive Technology Project - Jamie McClelland; Rar9; Ray Wright; Semper IT - Karin Gerritsen; Squiffle Consulting - Aidan Saunders; Stephen Palmstrom; Tadpole Collective - Kevin Cristiano; Third Sector Design - Michael McAndrew; Wikimedia Foundation - Eileen McNaughton \ No newline at end of file From cb42725f8ea77ae0e6484fdb8340c2de8dc79317 Mon Sep 17 00:00:00 2001 From: Andrew Hunt Date: Tue, 1 Sep 2020 11:18:07 -0400 Subject: [PATCH 067/834] 5.29.0 release notes: added boilerplate --- release-notes.md | 11 +++++++++++ release-notes/5.29.0.md | 24 ++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/release-notes.md b/release-notes.md index 4baa73d92bd1..31038359517f 100644 --- a/release-notes.md +++ b/release-notes.md @@ -15,6 +15,17 @@ Other resources for identifying changes are: * https://github.com/civicrm/civicrm-joomla * https://github.com/civicrm/civicrm-wordpress +## CiviCRM 5.29.0 + +Released September 2, 2020 + +- **[Synopsis](release-notes/5.29.0.md#synopsis)** +- **[Features](release-notes/5.29.0.md#features)** +- **[Bugs resolved](release-notes/5.29.0.md#bugs)** +- **[Miscellany](release-notes/5.29.0.md#misc)** +- **[Credits](release-notes/5.29.0.md#credits)** +- **[Feedback](release-notes/5.29.0.md#feedback)** + ## CiviCRM 5.28.3 Released August 22, 2020 diff --git a/release-notes/5.29.0.md b/release-notes/5.29.0.md index 359cb80c51f6..305ac734d59f 100644 --- a/release-notes/5.29.0.md +++ b/release-notes/5.29.0.md @@ -1,11 +1,25 @@ # CiviCRM 5.29.0 -Released September 2, 2020; +Released September 2, 2020 +- **[Synopsis](#synopsis)** - **[Features](#features)** - **[Bugs resolved](#bugs)** - **[Miscellany](#misc)** - **[Credits](#credits)** +- **[Feedback](#feedback)** + +## Synopsis + +| *Does this version...?* | | +|:--------------------------------------------------------------- |:-------:| +| Fix security vulnerabilities? | | +| Change the database schema? | | +| Alter the API? | | +| Require attention to configuration options? | | +| Fix problems installing or upgrading to a previous version? | | +| Introduce features? | | +| Fix bugs? | | ## Features @@ -660,4 +674,10 @@ AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman; Christia Most authors also reviewed code for this release; in addition, the following reviewers contributed their comments: -AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman, Pengyi Zhang; Andrew Thompson; Artful Robot - Rich Lott; Australian Greens - Andrew Cormick-Dockery, John Twyman; Bastien Ho; Blackfly Solutions - Alan Dixon; Christian Wach; Circle Interactive - Pradeep Nayak; civibot[bot]; CiviCoop - Jaap Jansma, Klaas Eikelboom; civicrm-builder; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; CompuCorp - Jamie Novick; Coop SymbioTIC - Mathieu Lutfy, Samuel Vanhove; Dave D; DevApp - Adam Kwiatkowski; elcapo; Francesc Bassas i Bullich; Freeform Solutions - Herb van den Dool; Fuzion - Jitendra Purohit; Greenpeace Central and Eastern Europe - Patrick Figel; irenemeisel; iXiam - César Ramos; JMA Consulting - Joe Murray, Monish Deb, Seamus Lee; Joinery - Allen Shaw; Korlon - Stuart Gaston; Lighthouse Consulting and Design - Brian Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MillerTech - Chamil Wijesooriya; MJCO - Mikey O'Toole; MJW Consulting - Matthew Wire; Nicol Wistreich; Progressive Technology Project - Jamie McClelland; Rar9; Ray Wright; Semper IT - Karin Gerritsen; Squiffle Consulting - Aidan Saunders; Stephen Palmstrom; Tadpole Collective - Kevin Cristiano; Third Sector Design - Michael McAndrew; Wikimedia Foundation - Eileen McNaughton \ No newline at end of file +AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman, Pengyi Zhang; Andrew Thompson; Artful Robot - Rich Lott; Australian Greens - Andrew Cormick-Dockery, John Twyman; Bastien Ho; Blackfly Solutions - Alan Dixon; Christian Wach; Circle Interactive - Pradeep Nayak; civibot[bot]; CiviCoop - Jaap Jansma, Klaas Eikelboom; civicrm-builder; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; CompuCorp - Jamie Novick; Coop SymbioTIC - Mathieu Lutfy, Samuel Vanhove; Dave D; DevApp - Adam Kwiatkowski; elcapo; Francesc Bassas i Bullich; Freeform Solutions - Herb van den Dool; Fuzion - Jitendra Purohit; Greenpeace Central and Eastern Europe - Patrick Figel; irenemeisel; iXiam - César Ramos; JMA Consulting - Joe Murray, Monish Deb, Seamus Lee; Joinery - Allen Shaw; Korlon - Stuart Gaston; Lighthouse Consulting and Design - Brian Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MillerTech - Chamil Wijesooriya; MJCO - Mikey O'Toole; MJW Consulting - Matthew Wire; Nicol Wistreich; Progressive Technology Project - Jamie McClelland; Rar9; Ray Wright; Semper IT - Karin Gerritsen; Squiffle Consulting - Aidan Saunders; Stephen Palmstrom; Tadpole Collective - Kevin Cristiano; Third Sector Design - Michael McAndrew; Wikimedia Foundation - Eileen McNaughton + +## Feedback + +These release notes are edited by Alice Frumin and Andrew Hunt. If you'd like +to provide feedback on them, please log in to https://chat.civicrm.org/civicrm +and contact `@agh1`. From b1f9364c354720dc9133f42981b0ba00cf032fa5 Mon Sep 17 00:00:00 2001 From: Andrew Hunt Date: Tue, 1 Sep 2020 11:18:38 -0400 Subject: [PATCH 068/834] 5.29.0 release notes: yank changes already in 5.8.x --- release-notes/5.29.0.md | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/release-notes/5.29.0.md b/release-notes/5.29.0.md index 305ac734d59f..edc783189cb3 100644 --- a/release-notes/5.29.0.md +++ b/release-notes/5.29.0.md @@ -43,50 +43,20 @@ Released September 2, 2020 - **5.28.2 & 5.29.3 Release Notes ([18228](https://github.com/civicrm/civicrm-core/pull/18228))** -- **dev/core#1964 Fix regression bug on deduping contacts with dedupe_exception matches ([18223](https://github.com/civicrm/civicrm-core/pull/18223))** - -- **Fix dedupe regression whereby deleted contacts are found ([18214](https://github.com/civicrm/civicrm-core/pull/18214))** - -- **dev/core#1959 Brick\Math\Exception\RoundingNecessaryException ([18206](https://github.com/civicrm/civicrm-core/pull/18206))** - -- **Fix regression whereby deleted contacts are in quicksearch results ([18213](https://github.com/civicrm/civicrm-core/pull/18213))** - -- **dev/core#1963 - Expanded icon on manage groups appears as unknown icon ([18205](https://github.com/civicrm/civicrm-core/pull/18205))** - -- **dev/core#1961 Fix regression - cancel button not working on recurring contributions ([18204](https://github.com/civicrm/civicrm-core/pull/18204))** - -- **(dev/core#1846) Container, ClassLoader Caches - Separate caches by ve… ([18200](https://github.com/civicrm/civicrm-core/pull/18200))** - -- **dev/core#1945 Fix recur access regression ([18180](https://github.com/civicrm/civicrm-core/pull/18180))** - - **dev/core#1937 - Upgrade message about needing composer patching turned on and updating mysql in DSN strings ([18174](https://github.com/civicrm/civicrm-core/pull/18174))** - **Revert "Swap out button/submit inputs for button elements" ([18185](https://github.com/civicrm/civicrm-core/pull/18185))** - **Installation doclinks not getting url-rewritten ([18175](https://github.com/civicrm/civicrm-core/pull/18175))** -- **cvv required html attribute should depend on backoffice setting ([18166](https://github.com/civicrm/civicrm-core/pull/18166))** - -- **[REF] Remove unnecessary comma ([18163](https://github.com/civicrm/civicrm-core/pull/18163))** - - **Fix 5.29 (unreleased) regression using temp tables ([18133](https://github.com/civicrm/civicrm-core/pull/18133))** -- **dev/core#1952 Remove uncessary component checking when exporting all … ([18149](https://github.com/civicrm/civicrm-core/pull/18149))** - -- **dev/core#1953 Ensure that Contribution pages do not fail validation o… ([18144](https://github.com/civicrm/civicrm-core/pull/18144))** - -- **dev/core#1934 fix regression on merging contacts with settings using contact_id ([18126](https://github.com/civicrm/civicrm-core/pull/18126))** - -- **dev/core#1936 Make the price field value label field not required ([18123](https://github.com/civicrm/civicrm-core/pull/18123))** - - **Swap out button/submit inputs for button elements ([18091](https://github.com/civicrm/civicrm-core/pull/18091))** - **dev/event#40 - EventCart - Check legacy setting until extension is public ([18101](https://github.com/civicrm/civicrm-core/pull/18101))** - **dev/wordpress#66 Re-instate newer variables but with more support for… ([18068](https://github.com/civicrm/civicrm-core/pull/18068))** -- **[REF] Fix jquery validation for on behalf of fields when combined wit… ([18092](https://github.com/civicrm/civicrm-core/pull/18092))** - - **dev/core#1895 fix first/last name adv search ([17950](https://github.com/civicrm/civicrm-core/pull/17950))** - **dev/core#1905 force backend links for new "configure" buttons ([18088](https://github.com/civicrm/civicrm-core/pull/18088))** @@ -635,8 +605,6 @@ Released September 2, 2020 - **Add eventcart shell ([17741](https://github.com/civicrm/civicrm-core/pull/17741))** -- **dev/core#1966 Fix Output for StateProvince and Country Fields ([620](https://github.com/civicrm/civicrm-drupal/pull/620))** - - **Fixed for multi-select filter ([615](https://github.com/civicrm/civicrm-drupal/pull/615))** - **fix url for file field ([608](https://github.com/civicrm/civicrm-drupal/pull/608))** From 50a15c9b8c53ed2373290f2e2dec73fb0c9d2df0 Mon Sep 17 00:00:00 2001 From: demeritcowboy Date: Tue, 1 Sep 2020 15:55:16 -0400 Subject: [PATCH 069/834] status_id not available when needed --- CRM/Case/BAO/Case.php | 1 + tests/phpunit/CRM/Case/BAO/CaseTest.php | 79 +++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/CRM/Case/BAO/Case.php b/CRM/Case/BAO/Case.php index 5a3f0b81061b..e78e55923653 100644 --- a/CRM/Case/BAO/Case.php +++ b/CRM/Case/BAO/Case.php @@ -1890,6 +1890,7 @@ public static function getRelatedCases($caseId, $excludeDeleted = TRUE) { 'case_id' => $dao->id, 'case_type' => $dao->case_type, 'client_name' => $clientView, + 'status_id' => $dao->status_id, 'case_status' => $statuses[$dao->status_id], 'links' => $caseView, ]; diff --git a/tests/phpunit/CRM/Case/BAO/CaseTest.php b/tests/phpunit/CRM/Case/BAO/CaseTest.php index 444d1c67c5f6..515257070695 100644 --- a/tests/phpunit/CRM/Case/BAO/CaseTest.php +++ b/tests/phpunit/CRM/Case/BAO/CaseTest.php @@ -723,4 +723,83 @@ public function testCreateCaseWithChangedManagerLabel() { $this->callAPISuccess('RelationshipType', 'create', $changeParams); } + /** + * Test change case status with linked cases choosing the option to + * update the linked cases. + */ + public function testChangeCaseStatusLinkedCases() { + $loggedInUser = $this->createLoggedInUser(); + $clientId1 = $this->individualCreate(); + $clientId2 = $this->individualCreate(); + $case1 = $this->createCase($clientId1, $loggedInUser); + $case2 = $this->createCase($clientId2, $loggedInUser); + $linkActivity = $this->callAPISuccess('Activity', 'create', [ + 'case_id' => $case1->id, + 'source_contact_id' => $loggedInUser, + 'target_contact' => $clientId1, + 'activity_type_id' => 'Link Cases', + 'subject' => 'Test Link Cases', + 'status_id' => 'Completed', + ]); + + // Put it in the format needed for endPostProcess + $activity = new StdClass(); + $activity->id = $linkActivity['id']; + $params = ['link_to_case_id' => $case2->id]; + CRM_Case_Form_Activity_LinkCases::endPostProcess(NULL, $params, $activity); + + // Get the option_value.value for case status Closed + $closedStatusResult = $this->callAPISuccess('OptionValue', 'get', [ + 'option_group_id' => 'case_status', + 'name' => 'Closed', + 'return' => ['value'], + ]); + $closedStatus = $closedStatusResult['values'][$closedStatusResult['id']]['value']; + + // Go thru the motions to change case status + $form = new CRM_Case_Form_Activity_ChangeCaseStatus(); + $form->_caseId = [$case1->id]; + $form->_oldCaseStatus = [$case1->status_id]; + $params = [ + 'id' => $case1->id, + 'case_status_id' => $closedStatus, + 'updateLinkedCases' => '1', + ]; + + CRM_Case_Form_Activity_ChangeCaseStatus::beginPostProcess($form, $params); + // Check that the second case is now also in the form member. + $this->assertEquals([$case1->id, $case2->id], $form->_caseId); + + // We need to pass in an actual activity later + $result = $this->callAPISuccess('Activity', 'create', [ + 'case_id' => $case1->id, + 'source_contact_id' => $loggedInUser, + 'target_contact' => $clientId1, + 'activity_type_id' => 'Change Case Status', + 'subject' => 'Status changed', + 'status_id' => 'Completed', + ]); + $changeStatusActivity = new CRM_Activity_DAO_Activity(); + $changeStatusActivity->id = $result['id']; + $changeStatusActivity->find(TRUE); + + $params = [ + 'case_id' => $case1->id, + 'target_contact_id' => [$clientId1], + 'case_status_id' => $closedStatus, + 'activity_date_time' => $changeStatusActivity->activity_date_time, + ]; + + CRM_Case_Form_Activity_ChangeCaseStatus::endPostProcess($form, $params, $changeStatusActivity); + + // @todo Check other case got closed. + /* + * We can't do this here because it doesn't happen until the parent + * activity does its thing. + $linkedCase = $this->callAPISuccess('Case', 'get', ['id' => $case2->id]); + $this->assertEquals($closedStatus, $linkedCase['values'][$linkedCase['id']]['status_id']); + $this->assertEquals(date('Y-m-d', strtotime($changeStatusActivity->activity_date_time)), $linkedCase['values'][$linkedCase['id']]['end_date']); + */ + } + } From 353282c5b079c879af998149dcdb9e994edaec87 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Tue, 1 Sep 2020 16:48:57 -0400 Subject: [PATCH 070/834] Revert #18005 Fix buggy placement of icons --- CRM/Core/Controller.php | 4 +- CRM/Core/Smarty/plugins/block.crmButton.php | 2 +- ang/crmMailingAB/EditCtrl/report.html | 2 +- ang/crmUi.js | 2 +- css/civicrm.css | 73 ++++++++++++++----- js/crm.ajax.js | 2 +- .../CRM/Admin/Form/Preferences/Display.tpl | 4 +- templates/CRM/Admin/Page/APIExplorer.tpl | 4 +- .../Contact/Form/Search/Custom/FullText.tpl | 14 ++-- .../Event/Form/ParticipantFeeSelection.tpl | 2 +- templates/CRM/Event/Page/DashBoard.tpl | 2 +- templates/CRM/Report/Form/Actions.tpl | 2 +- templates/CRM/Tag/Page/Tag.tpl | 20 ++--- templates/CRM/common/formButtons.tpl | 6 +- 14 files changed, 91 insertions(+), 48 deletions(-) diff --git a/CRM/Core/Controller.php b/CRM/Core/Controller.php index 68c653185855..4a4254c25505 100644 --- a/CRM/Core/Controller.php +++ b/CRM/Core/Controller.php @@ -568,8 +568,8 @@ public function wizardHeader($currentPageName) { public function addWizardStyle(&$wizard) { $wizard['style'] = [ 'barClass' => '', - 'stepPrefixCurrent' => ' ', - 'stepPrefixPast' => ' ', + 'stepPrefixCurrent' => ' ', + 'stepPrefixPast' => ' ', 'stepPrefixFuture' => ' ', 'subStepPrefixCurrent' => '  ', 'subStepPrefixPast' => '  ', diff --git a/CRM/Core/Smarty/plugins/block.crmButton.php b/CRM/Core/Smarty/plugins/block.crmButton.php index 64543d595768..390a8e73030e 100644 --- a/CRM/Core/Smarty/plugins/block.crmButton.php +++ b/CRM/Core/Smarty/plugins/block.crmButton.php @@ -47,7 +47,7 @@ function smarty_block_crmButton($params, $text, &$smarty) { if (strpos($icon, 'fa-') !== 0) { $icon = "fa-$icon"; } - $iconMarkup = " "; + $iconMarkup = "  "; } // All other params are treated as html attributes CRM_Utils_Array::remove($params, 'icon', 'p', 'q', 'a', 'f', 'h', 'fb', 'fe'); diff --git a/ang/crmMailingAB/EditCtrl/report.html b/ang/crmMailingAB/EditCtrl/report.html index 5e97ae964a5b..18380923c1ee 100644 --- a/ang/crmMailingAB/EditCtrl/report.html +++ b/ang/crmMailingAB/EditCtrl/report.html @@ -83,7 +83,7 @@ class="crm-hover-button action-item" ng-href="{{statUrl(am.mailing, statType, 'report')}}" title="{{ts('Reports for \'%1\'', {1: statType.title})}}" - crm-icon="fa-clipboard" + crm-icon="clipboard" > diff --git a/ang/crmUi.js b/ang/crmUi.js index 355939c2dcf7..c6ba58b1464e 100644 --- a/ang/crmUi.js +++ b/ang/crmUi.js @@ -908,7 +908,7 @@ }) // Example for Font Awesome: - // Example for jQuery UI (deprecated): + // Example for jQuery UI (deprecated): .directive('crmIcon', function() { return { restrict: 'EA', diff --git a/css/civicrm.css b/css/civicrm.css index 70e5a0ef01dd..a3264938b0ae 100644 --- a/css/civicrm.css +++ b/css/civicrm.css @@ -1835,6 +1835,8 @@ input.crm-form-entityref { .crm-container .crm-button input { background: none; + _background: #6C6C6C; + /* IE6 only */ border: medium none; color: #FFF; cursor: pointer; @@ -1849,12 +1851,6 @@ input.crm-form-entityref { margin-left: 20px; } -/* Reset WP backend min-height for buttons */ - -.wp-core-ui .crm-container .button { - min-height: 0; -} - .crm-container a.button, .crm-container a.button:link, .crm-container a.button:visited, @@ -1874,19 +1870,11 @@ input.crm-form-entityref { border: 1px solid #3e3e3e; } -.crm-container a.button, -.crm-container a.button:link, -.crm-container a.button:visited, .crm-container span.crm-button { display: block; - float: left; - overflow: hidden; - line-height: 135%; -} - -/* Preserving the important but not sure why */ -.crm-container span.crm-button { float: left !important; + overflow: hidden; + padding: 1px; } .crm-container button.crm-button { @@ -1904,12 +1892,22 @@ input.crm-form-entityref { .crm-container .crm-button input[type=button], .crm-container .crm-button input.crm-form-submit { - padding: 0; + padding: 3px 5px 2px; margin: 0; background: none; + _background: #6C6C6C; + /* IE6 only */ border: none; } +.crm-container a.button, +.crm-container a.button:link, +.crm-container a.button:visited { + display: block; + float: left; + line-height: 135%; +} + .crm-container .crm-button:hover, .crm-container .crm-button:focus, .crm-container input[type=submit]:hover, @@ -2048,6 +2046,31 @@ input.crm-form-entityref { margin-left: 3px; } +.crm-container .crm-button.crm-icon-button { + padding: 2px 2px 1px 4px; +} + +.crm-container .crm-button.crm-icon-button input { + padding-left: 18px; +} + +.crm-container .crm-button.button-crm-i { + padding: 2px 0 1px 5px; +} + +.crm-container .crm-button.button-crm-i input { + padding-left: 0; +} + +.crm-container .crm-button-icon { + background-image: url("../i/icons/jquery-ui-FFFFFF.png"); + height: 16px; + width: 16px; + display: block; + position: absolute; + pointer-events: none; +} + .crm-container .delete-icon { background-position: -176px -96px; } @@ -2094,6 +2117,22 @@ a.crm-i:hover { color: #86c661; } +.crm-i-button { + position: relative; +} + +.crm-i-button>.crm-i { + position: absolute; + pointer-events: none; + top: .4em; + left: .4em; +} + +.crm-container .crm-button.crm-i-button input[type="button"], +.crm-container .crm-button.crm-i-button input.crm-form-submit { + padding-left: 1.6em; +} + .crm-container a.helpicon { opacity: .8; } diff --git a/js/crm.ajax.js b/js/crm.ajax.js index e65bd7b2a22c..9a3c3f1ce572 100644 --- a/js/crm.ajax.js +++ b/js/crm.ajax.js @@ -517,7 +517,7 @@ added.push(identifier); } // display:none causes the form to not submit when pressing "enter" - $el.parents(buttonContainers).css({height: 0, padding: 0, margin: 0, overflow: 'hidden'}); + $el.parents(buttonContainers).css({height: 0, padding: 0, margin: 0, overflow: 'hidden'}).find('.crm-button-icon').hide(); }); $el.dialog('option', 'buttons', buttons); } diff --git a/templates/CRM/Admin/Form/Preferences/Display.tpl b/templates/CRM/Admin/Form/Preferences/Display.tpl index 44ff0ba7aabb..049a5ebf9975 100644 --- a/templates/CRM/Admin/Form/Preferences/Display.tpl +++ b/templates/CRM/Admin/Form/Preferences/Display.tpl @@ -161,8 +161,8 @@ {$form.editor_id.html}   - - + + {$form.ckeditor_config.html} diff --git a/templates/CRM/Admin/Page/APIExplorer.tpl b/templates/CRM/Admin/Page/APIExplorer.tpl index 9c127fcb6fca..1e1b8eb497da 100644 --- a/templates/CRM/Admin/Page/APIExplorer.tpl +++ b/templates/CRM/Admin/Page/APIExplorer.tpl @@ -285,8 +285,8 @@
- - + +
diff --git a/templates/CRM/Contact/Form/Search/Custom/FullText.tpl b/templates/CRM/Contact/Form/Search/Custom/FullText.tpl index 51a3c91956d8..6a7d8b516a80 100644 --- a/templates/CRM/Contact/Form/Search/Custom/FullText.tpl +++ b/templates/CRM/Contact/Form/Search/Custom/FullText.tpl @@ -62,7 +62,7 @@ {if !$table and $summary.addShowAllLink.Contact}
{ts}View all results for contacts{/ts} + title="{ts}View all results for contacts{/ts}"> {ts}View all results for contacts{/ts}
{/if} {* note we using location="below" because we don't want to use rows per page for now. And therefore don't put location="bottom" for now. *} {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if} @@ -125,7 +125,7 @@ {if !$table and $summary.addShowAllLink.Activity}
{ts}View all results for activities{/ts} + title="{ts}View all results for activities{/ts}"> {ts}View all results for activities{/ts}
{/if} {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if} @@ -182,7 +182,7 @@ {if !$table and $summary.addShowAllLink.Case}
{ts}View all results for cases{/ts} + title="{ts}View all results for cases{/ts}"> {ts}View all results for cases{/ts}
{/if} {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if} @@ -236,7 +236,7 @@ {if !$table and $summary.addShowAllLink.Contribution}
{ts}View all results for contributions{/ts} + title="{ts}View all results for contributions{/ts}"> {ts}View all results for contributions{/ts}
{/if} {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if} @@ -293,7 +293,7 @@ {if !$table and $summary.addShowAllLink.Participant}
{ts}View all results for participants{/ts} + title="{ts}View all results for participants{/ts}"> {ts}View all results for participants{/ts}
{/if} {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if} {* END Actions/Results section *} @@ -349,7 +349,7 @@ {if !$table and $summary.addShowAllLink.Membership}
{ts}View all results for memberships{/ts} + title="{ts}View all results for memberships{/ts}"> {ts}View all results for memberships{/ts}
{/if} {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if} @@ -395,7 +395,7 @@ {if !$table and $summary.addShowAllLink.File}
{ts}View all results for files{/ts} + title="{ts}View all results for files{/ts}"> {ts}View all results for files{/ts}
{/if} {if $table}{include file="CRM/common/pager.tpl" location="below"}{/if} {* END Actions/Results section *} diff --git a/templates/CRM/Event/Form/ParticipantFeeSelection.tpl b/templates/CRM/Event/Form/ParticipantFeeSelection.tpl index f9d3364dae5a..02246d54cba6 100644 --- a/templates/CRM/Event/Form/ParticipantFeeSelection.tpl +++ b/templates/CRM/Event/Form/ParticipantFeeSelection.tpl @@ -87,7 +87,7 @@ CRM.$(function($) {
{include file="CRM/common/formButtons.tpl" location="bottom"}
{if !$email}
- {ts}You will not be able to send an automatic email receipt for this payment because there is no email address recorded for this contact. If you want a receipt to be sent when this payment is recorded, click Cancel and then click Edit from the Summary tab to add an email address before recording the payment.{/ts} +  {ts}You will not be able to send an automatic email receipt for this payment because there is no email address recorded for this contact. If you want a receipt to be sent when this payment is recorded, click Cancel and then click Edit from the Summary tab to add an email address before recording the payment.{/ts}
{/if} diff --git a/templates/CRM/Event/Page/DashBoard.tpl b/templates/CRM/Event/Page/DashBoard.tpl index d01250ee681b..3ca364f09329 100644 --- a/templates/CRM/Event/Page/DashBoard.tpl +++ b/templates/CRM/Event/Page/DashBoard.tpl @@ -87,7 +87,7 @@ {if $actionColumn} {include file="CRM/common/tasks.tpl" location="botton"} {if $instanceUrl} - + {/if}
{if $values.isMap} - {ts}Map{/ts} +  {ts}Map{/ts}  |  {/if} {if $values.configure} diff --git a/templates/CRM/Report/Form/Actions.tpl b/templates/CRM/Report/Form/Actions.tpl index 34690c1d916d..9aee8f669585 100644 --- a/templates/CRM/Report/Form/Actions.tpl +++ b/templates/CRM/Report/Form/Actions.tpl @@ -20,7 +20,7 @@
   {ts}Existing report(s) from this template{/ts}   {ts}Existing report(s) from this template{/ts}
diff --git a/templates/CRM/Tag/Page/Tag.tpl b/templates/CRM/Tag/Page/Tag.tpl index e0ded1e4bcb4..e59f24246a76 100644 --- a/templates/CRM/Tag/Page/Tag.tpl +++ b/templates/CRM/Tag/Page/Tag.tpl @@ -463,16 +463,16 @@ <% {rdelim} %> @@ -519,21 +519,21 @@
<% if(!tagset) {ldelim} %> " class="button crm-popup" title="{ts}Create new tag under this one{/ts}"> - {ts}Add Child{/ts} +   {ts}Add Child{/ts} <% {rdelim} %> " class="button crm-popup" title="{ts}Duplicate this tag{/ts}"> - {ts}Clone Tag{/ts} +   {ts}Clone Tag{/ts} <% if(!data.is_reserved || adminReserved) {ldelim} %> <% if(tagsetCount) {ldelim} %> - {ts}Move Tag{/ts} +   {ts}Move Tag{/ts} <% {rdelim} %> <% if(!hasChildren) {ldelim} %> " class="button crm-popup small-popup"> - {ts}Delete{/ts} +   {ts}Delete{/ts} <% {rdelim} %> <% {rdelim} %> @@ -551,16 +551,16 @@
<% if(!reserved || adminReserved) {ldelim} %> " class="button crm-popup small-popup" title="{ts}Combine tags into one{/ts}"> - {ts}Merge Tags{/ts} +   {ts}Merge Tags{/ts} <% if(tagsetCount) {ldelim} %> - {ts}Move Tags{/ts} +   {ts}Move Tags{/ts} <% {rdelim} %> <% if(!hasChildren) {ldelim} %> " class="button crm-popup small-popup"> - {ts}Delete All{/ts} +   {ts}Delete All{/ts} <% {rdelim} %> <% {rdelim} %> diff --git a/templates/CRM/common/formButtons.tpl b/templates/CRM/common/formButtons.tpl index 7af01e646bec..92b41f4dff0d 100644 --- a/templates/CRM/common/formButtons.tpl +++ b/templates/CRM/common/formButtons.tpl @@ -41,10 +41,14 @@ {crmGetAttribute html=$html attr='crm-icon' assign='icon'} {capture assign=iconPrefix}{$icon|truncate:3:"":true}{/capture} {if $icon && $iconPrefix eq 'fa-'} + {assign var='buttonClass' value=' crm-i-button'} {capture assign=iconDisp}{/capture} + {elseif $icon} + {assign var='buttonClass' value=' crm-icon-button'} + {capture assign=iconDisp} {/capture} {/if} {crmGetAttribute html=$html attr='disabled' assign='disabled'} - + {$iconDisp} {$html} From f19d0e356d5de15686880d4adb84075d8dbb6053 Mon Sep 17 00:00:00 2001 From: Matthew Wire Date: Tue, 1 Sep 2020 22:41:21 +0100 Subject: [PATCH 071/834] Remove unused passbyreference and var from ipn_process_transaction --- api/v3/Contribution.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/api/v3/Contribution.php b/api/v3/Contribution.php index 63036179825e..aa3d7f156cb8 100644 --- a/api/v3/Contribution.php +++ b/api/v3/Contribution.php @@ -602,7 +602,6 @@ function civicrm_api3_contribution_repeattransaction($params) { ); } - $original_contribution = clone $contribution; $input['payment_processor_id'] = civicrm_api3('contributionRecur', 'getvalue', [ 'return' => 'payment_processor_id', 'id' => $contribution->contribution_recur_id, @@ -626,7 +625,7 @@ function civicrm_api3_contribution_repeattransaction($params) { ]; $input = array_intersect_key($params, array_fill_keys($passThroughParams, NULL)); - return _ipn_process_transaction($params, $contribution, $input, $ids, $original_contribution); + return _ipn_process_transaction($params, $contribution, $input, $ids); } catch (Exception $e) { throw new API_Exception('failed to load related objects' . $e->getMessage() . "\n" . $e->getTraceAsString()); @@ -645,13 +644,11 @@ function civicrm_api3_contribution_repeattransaction($params) { * * @param array $ids * - * @param CRM_Contribute_BAO_Contribution $firstContribution - * * @return mixed * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ -function _ipn_process_transaction(&$params, $contribution, $input, $ids, $firstContribution = NULL) { +function _ipn_process_transaction($params, $contribution, $input, $ids) { $objects = $contribution->_relatedObjects; $objects['contribution'] = &$contribution; $input['component'] = $contribution->_component; From f708b86917999b76e537b0e5cfed6c93ae0be2b6 Mon Sep 17 00:00:00 2001 From: Noah Miller Date: Tue, 1 Sep 2020 15:23:25 -0700 Subject: [PATCH 072/834] dev/core#1974 - ChangeFieldType - "Select (w/serialize)" field should be treated like older "Multi-Select" fields --- CRM/Custom/Form/ChangeFieldType.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CRM/Custom/Form/ChangeFieldType.php b/CRM/Custom/Form/ChangeFieldType.php index 24650919ae6c..8ba794c5cc56 100644 --- a/CRM/Custom/Form/ChangeFieldType.php +++ b/CRM/Custom/Form/ChangeFieldType.php @@ -54,6 +54,9 @@ public function preProcess() { $params = ['id' => $this->_id]; CRM_Core_BAO_CustomField::retrieve($params, $this->_values); + if ($this->_values['html_type'] == 'Select' && $this->_values['serialize']) { + $this->_values['html_type'] = 'Multi-Select'; + } $this->_htmlTypeTransitions = self::fieldTypeTransitions(CRM_Utils_Array::value('data_type', $this->_values), CRM_Utils_Array::value('html_type', $this->_values) ); From adb4fb9615269dd72e001ca583eddd5854742046 Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 2 Sep 2020 11:54:40 +1200 Subject: [PATCH 073/834] [REF] Remove most interaction with With this change we are only looking up 1) objects['paymentProcessor'] - we should probably just pass in the id 2) objects['contribution'] - we could make this a param in it's own right & remove objects 3) objects['event'] - just used to get event title - we could do a query off participant --- CRM/Contribute/BAO/Contribution.php | 10 +++++----- CRM/Core/Payment/BaseIPN.php | 6 +++++- CRM/Event/Form/Task/Batch.php | 6 +++++- api/v3/Contribution.php | 6 +++++- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index bc56e48995f8..238c4f196d3c 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -4449,7 +4449,10 @@ public static function completeOrder($input, $ids, $objects, $isPostPaymentCreat // @todo see if we even need this - it's used further down to create an activity // but the BAO layer should create that - we just need to add a test to cover it & can // maybe remove $ids altogether. - $contributionContactID = $ids['related_contact'] ?? NULL; + $contributionContactID = $ids['related_contact']; + $participantID = $ids['participant']; + $recurringContributionID = $ids['contributionRecur']; + // Unset ids just to make it clear it's not used again. unset($ids); // The previous details are used when calculating line items so keep it before any code that 'does something' @@ -4472,9 +4475,6 @@ public static function completeOrder($input, $ids, $objects, $isPostPaymentCreat 'financial_type_id', ]; - $participant = $objects['participant'] ?? NULL; - $recurContrib = $objects['contributionRecur'] ?? NULL; - $recurringContributionID = (empty($recurContrib->id)) ? NULL : $recurContrib->id; $event = $objects['event'] ?? NULL; $paymentProcessorId = ''; @@ -4520,7 +4520,7 @@ public static function completeOrder($input, $ids, $objects, $isPostPaymentCreat } else { if (empty($input['IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved'])) { - $participantParams['id'] = $participant->id; + $participantParams['id'] = $participantID; $participantParams['status_id'] = 'Registered'; civicrm_api3('Participant', 'create', $participantParams); } diff --git a/CRM/Core/Payment/BaseIPN.php b/CRM/Core/Payment/BaseIPN.php index e4312a5fd797..fdfce40a0f58 100644 --- a/CRM/Core/Payment/BaseIPN.php +++ b/CRM/Core/Payment/BaseIPN.php @@ -468,7 +468,11 @@ private function cancelMembership($membership, $membershipStatusID, $onlyCancelP * @throws \CiviCRM_API3_Exception */ public function completeTransaction($input, $ids, $objects) { - CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects); + CRM_Contribute_BAO_Contribution::completeOrder($input, [ + 'related_contact' => $ids['related_contact'] ?? NULL, + 'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL, + 'contributionRecur' => !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL, + ], $objects); } /** diff --git a/CRM/Event/Form/Task/Batch.php b/CRM/Event/Form/Task/Batch.php index 16d8752bde39..bc00eccc68d1 100644 --- a/CRM/Event/Form/Task/Batch.php +++ b/CRM/Event/Form/Task/Batch.php @@ -417,7 +417,11 @@ public static function updateContributionStatus($params) { //complete the contribution. // @todo use the api - ie civicrm_api3('Contribution', 'completetransaction', $input); // as this method is not preferred / supported. - CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects); + CRM_Contribute_BAO_Contribution::completeOrder($input, [ + 'related_contact' => $ids['related_contact'] ?? NULL, + 'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL, + 'contributionRecur' => NULL, + ], $objects); // reset template values before processing next transactions $template->clearTemplateVars(); diff --git a/api/v3/Contribution.php b/api/v3/Contribution.php index 63036179825e..e011ab5b1c02 100644 --- a/api/v3/Contribution.php +++ b/api/v3/Contribution.php @@ -681,7 +681,11 @@ function _ipn_process_transaction(&$params, $contribution, $input, $ids, $firstC if (!empty($params['payment_instrument_id'])) { $input['payment_instrument_id'] = $params['payment_instrument_id']; } - return CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects, + return CRM_Contribute_BAO_Contribution::completeOrder($input, [ + 'related_contact' => $ids['related_contact'] ?? NULL, + 'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL, + 'contributionRecur' => !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL, + ], $objects, $params['is_post_payment_create'] ?? NULL); } From 4eba8926824dac77b94659cd1a25ea4c57b5fd53 Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 2 Sep 2020 18:43:08 +1200 Subject: [PATCH 074/834] [REF] Add test for existing Participant batch update cancel and fix to not call BaseIPN->cancelled I dug into what is 'achieved' by calling BaseIPN->cancelled here and everything except the bit where the contribution is updated to cancelled is actually bypassed so a simple api call suffices. I also discovered the cancellation of the contribution is highly conditional and arguably illogical. I will separately log a gitlab to discuss whether it still makes sense, but I wouldn't want that discussion to derail this no-change cleanup --- CRM/Core/Payment/BaseIPN.php | 17 ++++++------ CRM/Event/Form/Task/Batch.php | 9 ++----- .../phpunit/CRM/Event/Form/Task/BatchTest.php | 26 ++++++++++++++++--- .../CRMTraits/Financial/OrderTrait.php | 12 +++++++++ 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/CRM/Core/Payment/BaseIPN.php b/CRM/Core/Payment/BaseIPN.php index e4312a5fd797..594718780986 100644 --- a/CRM/Core/Payment/BaseIPN.php +++ b/CRM/Core/Payment/BaseIPN.php @@ -324,19 +324,18 @@ public function cancelled(&$objects, $transaction = NULL, $input = []) { CRM_Contribute_BAO_ContributionRecur::copyCustomValues($objects['contributionRecur']->id, $contribution->id); } - if (empty($input['IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved'])) { - if (!empty($memberships)) { - foreach ($memberships as $membership) { - if ($membership) { - $this->cancelMembership($membership, $membership->status_id); - } + if (!empty($memberships)) { + foreach ($memberships as $membership) { + if ($membership) { + $this->cancelMembership($membership, $membership->status_id); } } + } - if ($participant) { - $this->cancelParticipant($participant->id); - } + if ($participant) { + $this->cancelParticipant($participant->id); } + if ($transaction) { $transaction->commit(); } diff --git a/CRM/Event/Form/Task/Batch.php b/CRM/Event/Form/Task/Batch.php index 16d8752bde39..31f690d3633a 100644 --- a/CRM/Event/Form/Task/Batch.php +++ b/CRM/Event/Form/Task/Batch.php @@ -285,7 +285,8 @@ public static function updatePendingOnlineContribution($participantId, $statusId $contributionStatusId = array_search('Completed', $contributionStatuses); } if (array_key_exists($statusId, $negativeStatuses)) { - $contributionStatusId = array_search('Cancelled', $contributionStatuses); + civicrm_api3('Contribution', 'create', ['id' => $contributionId, 'contribution_status_id' => 'Cancelled']); + return; } if (!$contributionStatusId || !$participantId || !$contributionId) { @@ -374,12 +375,6 @@ public static function updateContributionStatus($params) { 'flip' => 1, ]); $input['IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved'] = $params['IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved'] ?? NULL; - if ($statusId == $contributionStatuses['Cancelled']) { - $transaction = new CRM_Core_Transaction(); - $baseIPN->cancelled($objects, $transaction, $input); - $transaction->commit(); - return; - } if ($statusId == $contributionStatuses['Failed']) { $transaction = new CRM_Core_Transaction(); $baseIPN->failed($objects, $transaction, $input); diff --git a/tests/phpunit/CRM/Event/Form/Task/BatchTest.php b/tests/phpunit/CRM/Event/Form/Task/BatchTest.php index 15f24a48bf0e..d9e590cfbd5c 100644 --- a/tests/phpunit/CRM/Event/Form/Task/BatchTest.php +++ b/tests/phpunit/CRM/Event/Form/Task/BatchTest.php @@ -7,19 +7,19 @@ * @group headless */ class CRM_Event_Form_Task_BatchTest extends CiviUnitTestCase { + use CRMTraits_Financial_OrderTrait; /** * Test the the submit function on the event participant submit function. - * - * @todo extract submit functions on other Batch update classes, use dataprovider to test on all. */ public function testSubmit() { - $group = $this->CustomGroupCreate(['extends' => 'Participant', 'title' => 'Participant']); + $group = $this->customGroupCreate(['extends' => 'Participant', 'title' => 'Participant']); $field = $this->customFieldCreate(['custom_group_id' => $group['id'], 'html_type' => 'CheckBox', 'option_values' => ['two' => 'A couple', 'three' => 'A few', 'four' => 'Too Many']]); $participantID = $this->participantCreate(); $participant = $this->callAPISuccessGetSingle('Participant', ['id' => $participantID]); $this->assertEquals(2, $participant['participant_status_id']); + /* @var CRM_Event_Form_Task_Batch $form */ $form = $this->getFormObject('CRM_Event_Form_Task_Batch'); $form->submit(['field' => [$participantID => ['participant_status_id' => 1, 'custom_' . $field['id'] => ['two' => 1, 'four' => 1]]]]); @@ -27,4 +27,24 @@ public function testSubmit() { $this->assertEquals(1, $participant['participant_status_id']); } + /** + * Test the the submit function on the event participant submit function. + * + * Test is to establish existing behaviour prior to code cleanup. It turns out the existing + * code ONLY cancels the contribution as well as the participant record if is_pay_later is true + * AND the source is 'Online Event Registration'. + */ + public function testSubmitCancel() { + $this->createEventOrder(['source' => 'Online Event Registration', 'is_pay_later' => 1]); + $participantCancelledStatusID = CRM_Core_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'status_id', 'Cancelled'); + + /* @var CRM_Event_Form_Task_Batch $form */ + $form = $this->getFormObject('CRM_Event_Form_Task_Batch'); + $form->submit(['field' => [$this->ids['Participant'][0] => ['participant_status' => $participantCancelledStatusID]]]); + + $participant = $this->callAPISuccessGetSingle('Participant', ['id' => $this->ids['Participant'][0]]); + $this->assertEquals($participantCancelledStatusID, $participant['participant_status_id']); + $this->callAPISuccessGetSingle('Contribution', ['id' => $this->ids['Contribution'][0], 'contribution_status_id' => 'Cancelled']); + } + } diff --git a/tests/phpunit/CRMTraits/Financial/OrderTrait.php b/tests/phpunit/CRMTraits/Financial/OrderTrait.php index 5bd1292a5665..c927e7185747 100644 --- a/tests/phpunit/CRMTraits/Financial/OrderTrait.php +++ b/tests/phpunit/CRMTraits/Financial/OrderTrait.php @@ -162,6 +162,18 @@ protected function createMultipleMembershipOrder() { $this->ids['Contribution'][0] = $orderID; } + /** + * Create an order for an event. + * + * @param array $orderParams + * + * @throws \CRM_Core_Exception + */ + protected function createEventOrder($orderParams = []) { + $this->ids['Contribution'][0] = $this->callAPISuccess('Order', 'create', array_merge($this->getParticipantOrderParams(), $orderParams))['id']; + $this->ids['Participant'][0] = $this->callAPISuccessGetValue('ParticipantPayment', ['return' => 'participant_id', 'contribution_id' => $this->ids['Contribution'][0]]); + } + /** * Create an extraneous contribution to throw off any 'number one bugs'. * From 642fc9b1936c72d15e74941b1ee3c54036058ea4 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Tue, 1 Sep 2020 16:08:39 -0700 Subject: [PATCH 075/834] Add release-notes/5.28.4.md --- release-notes.md | 9 +++++++++ release-notes/5.28.4.md | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 release-notes/5.28.4.md diff --git a/release-notes.md b/release-notes.md index 31038359517f..d2d501d26a72 100644 --- a/release-notes.md +++ b/release-notes.md @@ -26,6 +26,15 @@ Released September 2, 2020 - **[Credits](release-notes/5.29.0.md#credits)** - **[Feedback](release-notes/5.29.0.md#feedback)** +## CiviCRM 5.28.4 + +Released September 1, 2020 + +- **[Synopsis](release-notes/5.28.4.md#synopsis)** +- **[Bugs resolved](release-notes/5.28.4.md#bugs)** +- **[Credits](release-notes/5.28.4.md#credits)** +- **[Feedback](release-notes/5.28.4.md#feedback)** + ## CiviCRM 5.28.3 Released August 22, 2020 diff --git a/release-notes/5.28.4.md b/release-notes/5.28.4.md new file mode 100644 index 000000000000..6851eba8aed5 --- /dev/null +++ b/release-notes/5.28.4.md @@ -0,0 +1,40 @@ +# CiviCRM 5.28.4 + +Released September 1, 2020 + +- **[Synopsis](#synopsis)** +- **[Bugs resolved](#bugs)** +- **[Credits](#credits)** +- **[Feedback](#feedback)** + +## Synopsis + +| *Does this version...?* | | +| --------------------------------------------------------------- | -------- | +| Change the database schema? | no | +| Alter the API? | no | +| Require attention to configuration options? | no | +| Fix problems installing or upgrading to a previous version? | no | +| Introduce features? | no | +| **Fix bugs?** | **yes** | + +## Bugs resolved + +* **_CiviCase_: Fix interpretation of "Assign to Creator" option (when disabled) ([dev/core#1982](https://lab.civicrm.org/dev/core/-/issues/1982): [#18301](https://github.com/civicrm/civicrm-core/pull/18301))** +* **_CiviContribute_: Fix tax calculation for multiline transactions ([dev/core#1983](https://lab.civicrm.org/dev/core/-/issues/1983): [#18290](https://github.com/civicrm/civicrm-core/pull/18290))** +* **_CiviContribute_: Fix tax calculation for offline membership renewals ([dev/core#1972](https://lab.civicrm.org/dev/core/-/issues/1972): [#18271](https://github.com/civicrm/civicrm-core/pull/18271))** +* **_Custom Fields_: Fix conversion from "Multi-Select" to "Select" ([dev/core#1974](https://lab.civicrm.org/dev/core/-/issues/1974): [#18304](https://github.com/civicrm/civicrm-core/pull/18304), [#18272](https://github.com/civicrm/civicrm-core/pull/18272))** + +## Credits + +This release was developed by the following authors and reviewers: + +Wikimedia Foundation - Eileen McNaughton; Semper IT - Karin Gerritsen; Lighthouse Consulting and +Design - Brian Shaughnessy; Lemniscus - Noah Miller; JMA Consulting - Seamus Lee; Dave D; CiviCRM - +Tim Otten; Circle Interactive - Pradeep Nayak + +## Feedback + +These release notes are edited by Tim Otten and Andrew Hunt. If you'd like to +provide feedback on them, please login to https://chat.civicrm.org/civicrm and +contact `@agh1`. From d3082f177b95c02101a0282593b644208f85e82d Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Tue, 1 Sep 2020 16:19:19 -0700 Subject: [PATCH 076/834] Fix anchors in release-notes/5.28.3.md --- release-notes/5.28.3.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/release-notes/5.28.3.md b/release-notes/5.28.3.md index 3d89063f2dc7..4f83984cdae7 100644 --- a/release-notes/5.28.3.md +++ b/release-notes/5.28.3.md @@ -7,7 +7,7 @@ Released August 22, 2020 - **[Credits](#credits)** - **[Feedback](#feedback)** -## Synopsis +## Synopsis | *Does this version...?* | | | --------------------------------------------------------------- | -------- | @@ -18,7 +18,7 @@ Released August 22, 2020 | Introduce features? | no | | **Fix bugs?** | **yes** | -## Bugs resolved +## Bugs resolved * **_CiviContribute_: Re-enable "Cancel" button in backend UI for recurring contributions ([dev/core#1961](https://lab.civicrm.org/dev/core/-/issues/1961): [#18204](https://github.com/civicrm/civicrm-core/pull/18204))** * **_CiviContribute_: Contributions sometimes display "RoundingNecessaryException" ([dev/core#1959](https://lab.civicrm.org/dev/core/-/issues/1959): [#18206](https://github.com/civicrm/civicrm-core/pull/18206))** @@ -28,7 +28,7 @@ Released August 22, 2020 * **_Quick Search_: Deleted contacts are incorrectly displayed ([#18213](https://github.com/civicrm/civicrm-core/pull/18213))** * **_Styling_: Collapse icon is incorrect ([dev/core#1963](https://lab.civicrm.org/dev/core/-/issues/1963): [#18205](https://github.com/civicrm/civicrm-core/pull/18205))** -## Credits +## Credits This release was developed by the following authors and reviewers: @@ -36,7 +36,7 @@ Wikimedia Foundation - Eileen McNaughton; MillerTech - Chamil Wijesooriya; MJW C Wire; JMA Consulting - Seamus Lee; Dave D; CiviCoop - Jaap Jansma; CiviCRM - Tim Otten; Circle Interactive - Pradeep Nayak; Australian Greens - Andrew Cormick-Dockery -## Feedback +## Feedback These release notes are edited by Tim Otten and Andrew Hunt. If you'd like to provide feedback on them, please login to https://chat.civicrm.org/civicrm and From 97887960bd0f2a4755efbce61d34109f9441210a Mon Sep 17 00:00:00 2001 From: Matthew Wire Date: Tue, 1 Sep 2020 22:55:05 +0100 Subject: [PATCH 077/834] Filter params in completetransaction --- api/v3/Contribution.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/api/v3/Contribution.php b/api/v3/Contribution.php index 1e70afa7ecbf..286eb8770843 100644 --- a/api/v3/Contribution.php +++ b/api/v3/Contribution.php @@ -472,28 +472,32 @@ function _civicrm_api3_contribution_sendconfirmation_spec(&$params) { */ function civicrm_api3_contribution_completetransaction($params) { $input = $ids = []; - if (isset($params['payment_processor_id'])) { - $input['payment_processor_id'] = $params['payment_processor_id']; - } + $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = $params['id']; if (!$contribution->find(TRUE)) { throw new API_Exception('A valid contribution ID is required', 'invalid_data'); } + if (isset($params['payment_processor_id'])) { + $input['payment_processor_id'] = $params['payment_processor_id']; + } if (!$contribution->loadRelatedObjects($input, $ids, TRUE)) { throw new API_Exception('failed to load related objects'); } elseif ($contribution->contribution_status_id == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed')) { throw new API_Exception(ts('Contribution already completed'), 'contribution_completed'); } - $input['trxn_id'] = $params['trxn_id'] ?? $contribution->trxn_id; + $params['trxn_id'] = $params['trxn_id'] ?? $contribution->trxn_id; - if (!empty($params['fee_amount'])) { - $input['fee_amount'] = $params['fee_amount']; - } - return _ipn_process_transaction($params, $contribution, $input, $ids); + $passThroughParams = [ + 'fee_amount', + 'payment_processor_id', + 'trxn_id', + ]; + $input = array_intersect_key($params, array_fill_keys($passThroughParams, NULL)); + return _ipn_process_transaction($params, $contribution, $input, $ids); } /** From c6fe32d7f76413db0210c7669fa5bcc83baaa022 Mon Sep 17 00:00:00 2001 From: demeritcowboy Date: Wed, 2 Sep 2020 10:35:18 -0400 Subject: [PATCH 078/834] typo in call to nestedGroup --- CRM/Admin/Form/ScheduleReminders.php | 2 +- CRM/Core/PseudoConstant.php | 2 +- tests/phpunit/CRM/Contact/BAO/GroupTest.php | 49 +++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/CRM/Admin/Form/ScheduleReminders.php b/CRM/Admin/Form/ScheduleReminders.php index c881ef779387..f9ab1c98a6a2 100644 --- a/CRM/Admin/Form/ScheduleReminders.php +++ b/CRM/Admin/Form/ScheduleReminders.php @@ -250,7 +250,7 @@ public function buildQuickForm() { $this->addEntityRef('recipient_manual_id', ts('Manual Recipients'), ['multiple' => TRUE, 'create' => TRUE]); $this->add('select', 'group_id', ts('Group'), - CRM_Core_PseudoConstant::nestedGroup('Mailing'), FALSE, ['class' => 'crm-select2 huge'] + CRM_Core_PseudoConstant::nestedGroup(), FALSE, ['class' => 'crm-select2 huge'] ); // multilingual only options diff --git a/CRM/Core/PseudoConstant.php b/CRM/Core/PseudoConstant.php index 758db214acba..c1f5af9e77f3 100644 --- a/CRM/Core/PseudoConstant.php +++ b/CRM/Core/PseudoConstant.php @@ -857,7 +857,7 @@ public static function group($groupType = NULL, $excludeHidden = TRUE) { * @param bool $excludeHidden * @return array */ - public static function nestedGroup($checkPermissions = TRUE, $groupType = NULL, $excludeHidden = TRUE) { + public static function nestedGroup(bool $checkPermissions = TRUE, $groupType = NULL, bool $excludeHidden = TRUE) { $groups = $checkPermissions ? self::group($groupType, $excludeHidden) : self::allGroup($groupType, $excludeHidden); return CRM_Contact_BAO_Group::getGroupsHierarchy($groups, NULL, '  ', TRUE); } diff --git a/tests/phpunit/CRM/Contact/BAO/GroupTest.php b/tests/phpunit/CRM/Contact/BAO/GroupTest.php index 3ab280ae7111..1b50f79b52e7 100644 --- a/tests/phpunit/CRM/Contact/BAO/GroupTest.php +++ b/tests/phpunit/CRM/Contact/BAO/GroupTest.php @@ -109,6 +109,55 @@ public function testGroupHirearchy() { $this->assertFalse(array_key_exists($group3->id, $groupsHierarchy)); } + /** + * Test nestedGroup pseudoconstant + */ + public function testNestedGroup() { + $params = [ + 'name' => 'groupa', + 'title' => 'Parent Group A', + 'description' => 'Parent Group One', + 'visibility' => 'User and User Admin Only', + 'is_active' => 1, + // mailing group + 'group_type' => ['2' => 1], + ]; + $group1 = CRM_Contact_BAO_Group::create($params); + + $params = [ + 'name' => 'groupb', + 'title' => 'Parent Group B', + 'description' => 'Parent Group Two', + 'visibility' => 'User and User Admin Only', + 'is_active' => 1, + ]; + $group2 = CRM_Contact_BAO_Group::create($params); + + $params = [ + 'name' => 'groupc', + 'title' => 'Child Group C', + 'description' => 'Child Group C', + 'visibility' => 'User and User Admin Only', + 'is_active' => 1, + 'parents' => [ + $group2->id => 1, + ], + ]; + $group3 = CRM_Contact_BAO_Group::create($params); + + // Check with no group type restriction + $nestedGroup = CRM_Core_PseudoConstant::nestedGroup(); + $this->assertEquals([ + $group1->id => 'Parent Group A', + $group2->id => 'Parent Group B', + $group3->id => '  Child Group C', + ], $nestedGroup); + + // Check restrict to mailing groups + $nestedGroup = CRM_Core_PseudoConstant::nestedGroup(TRUE, 'Mailing'); + $this->assertSame([$group1->id => 'Parent Group A'], $nestedGroup); + } + /** * Test adding a smart group. */ From 973aefd508538b0d31174fcf7e3d2125410c9002 Mon Sep 17 00:00:00 2001 From: Aidan Saunders Date: Wed, 2 Sep 2020 19:54:53 +0100 Subject: [PATCH 079/834] Fix spec labels - see dev/mail#24, item 1 --- api/v3/MailingEventSubscribe.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/v3/MailingEventSubscribe.php b/api/v3/MailingEventSubscribe.php index 92a8795bdb20..7046fe166efc 100644 --- a/api/v3/MailingEventSubscribe.php +++ b/api/v3/MailingEventSubscribe.php @@ -66,12 +66,12 @@ function civicrm_api3_mailing_event_subscribe_create($params) { function _civicrm_api3_mailing_event_subscribe_create_spec(&$params) { $params['email'] = [ 'api.required' => 1, - 'title' => 'Unsubscribe Email', + 'title' => 'Subscribe Email', 'type' => CRM_Utils_Type::T_STRING, ]; $params['group_id'] = [ 'api.required' => 1, - 'title' => 'Unsubscribe From Group', + 'title' => 'Subscribe To Group', 'type' => CRM_Utils_Type::T_INT, ]; } From 8bd48a0fb0a615fc56536e4a4cd62e002fb74308 Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Tue, 1 Sep 2020 13:16:51 -0400 Subject: [PATCH 080/834] 5.29 Release Notes first pass --- contributor-key.yml | 12 + release-notes/5.29.0.md | 689 ++++++++++++++++++++++------------------ 2 files changed, 386 insertions(+), 315 deletions(-) diff --git a/contributor-key.yml b/contributor-key.yml index e594d3db88c1..fe7abc511963 100644 --- a/contributor-key.yml +++ b/contributor-key.yml @@ -449,6 +449,9 @@ organization: Wikimedia Foundation jira : ejegg +- github : elcapo + name : Carlos Capote + - github : elisseck name : Eli Lisseck organization: AGH Strategies @@ -596,6 +599,9 @@ - github : GValFr35 +- github : irenemeisel + name : Irene Meisel + - github : kewljuice name : Wouter Hechtermans organization: Calibrate @@ -1116,8 +1122,12 @@ organization: CompuCorp jira : mukesh +- github : muniodiego + name : Diego Muñio + - github : mwestergaard name : Mark Westergaard + organization: iXiam - github : nbrettell name : Nathan Brettell @@ -1271,6 +1281,8 @@ - name : Rareș Pamfil jira : rares +- github : Rar9 + - github : ray-wright name : Ray Wright diff --git a/release-notes/5.29.0.md b/release-notes/5.29.0.md index edc783189cb3..5d2a66308d8a 100644 --- a/release-notes/5.29.0.md +++ b/release-notes/5.29.0.md @@ -31,618 +31,677 @@ Released September 2, 2020 ## Bugs resolved -### Core CiviCRM - -- **dev/core#1983 Fix to tax calculation on multi-line-item ([18290](https://github.com/civicrm/civicrm-core/pull/18290))** +### CiviCase -- **dev/core#1982 - 5.29 version of PR 18282 ([18301](https://github.com/civicrm/civicrm-core/pull/18301))** +- **Use case id to get relationship for activity creation ([17256](https://github.com/civicrm/civicrm-core/pull/17256) and [17764](https://github.com/civicrm/civicrm-core/pull/17764))** -- **dev/core#1972 Fix tax_amount calclation on renewal form ([18271](https://github.com/civicrm/civicrm-core/pull/18271))** - -- **Update contributor key for Andrew ([18230](https://github.com/civicrm/civicrm-core/pull/18230))** +### Core CiviCRM -- **5.28.2 & 5.29.3 Release Notes ([18228](https://github.com/civicrm/civicrm-core/pull/18228))** +- **reporting#21 - don't multiple contribution details when a 1-to-many r… ([15435](https://github.com/civicrm/civicrm-core/pull/15435))** -- **dev/core#1937 - Upgrade message about needing composer patching turned on and updating mysql in DSN strings ([18174](https://github.com/civicrm/civicrm-core/pull/18174))** +- **Contribution Summary Report: Taking the currency filtered in the "general total" row. Implements dev/report#27 ([16736](https://github.com/civicrm/civicrm-core/pull/16736))** -- **Revert "Swap out button/submit inputs for button elements" ([18185](https://github.com/civicrm/civicrm-core/pull/18185))** +- **dev/report#43 - Icon after saving a civireport instance is misleading ([17863](https://github.com/civicrm/civicrm-core/pull/17863))** -- **Installation doclinks not getting url-rewritten ([18175](https://github.com/civicrm/civicrm-core/pull/18175))** -- **Fix 5.29 (unreleased) regression using temp tables ([18133](https://github.com/civicrm/civicrm-core/pull/18133))** -- **Swap out button/submit inputs for button elements ([18091](https://github.com/civicrm/civicrm-core/pull/18091))** +- **dev/membership#18 Enhance parameters for Job.process_membership ([16298](https://github.com/civicrm/civicrm-core/pull/16298))** -- **dev/event#40 - EventCart - Check legacy setting until extension is public ([18101](https://github.com/civicrm/civicrm-core/pull/18101))** -- **dev/wordpress#66 Re-instate newer variables but with more support for… ([18068](https://github.com/civicrm/civicrm-core/pull/18068))** +- **mailing#70 Don't create users for test mail if user doesn't have permission ([17867](https://github.com/civicrm/civicrm-core/pull/17867))** -- **dev/core#1895 fix first/last name adv search ([17950](https://github.com/civicrm/civicrm-core/pull/17950))** -- **dev/core#1905 force backend links for new "configure" buttons ([18088](https://github.com/civicrm/civicrm-core/pull/18088))** -- **dev/core#1932 - Make status-checks more polite during upgrade ([18085](https://github.com/civicrm/civicrm-core/pull/18085))** +- **dev/drupal#114 and dev/core#1647 - Remove resource url status check ([17754](https://github.com/civicrm/civicrm-core/pull/17754))** -- **dev/core#1928 Fix HTML5 error due to required attribute being set swi… ([18080](https://github.com/civicrm/civicrm-core/pull/18080))** +- **dev/drupal#127 - require email for the Useradd task and errors not showing ([17915](https://github.com/civicrm/civicrm-core/pull/17915))** -- **5.28 ([18084](https://github.com/civicrm/civicrm-core/pull/18084))** +- **dev/drupal#127 - CRM_Core_Session::setStatus() gets ignored sometimes ([17914](https://github.com/civicrm/civicrm-core/pull/17914))** -- **5.28 ([18082](https://github.com/civicrm/civicrm-core/pull/18082))** - **dev/financial#135 Remove stub function from payflowPro ([18078](https://github.com/civicrm/civicrm-core/pull/18078))** -- **5.28 ([18077](https://github.com/civicrm/civicrm-core/pull/18077))** - -- **Fix button name on updated form ([18000](https://github.com/civicrm/civicrm-core/pull/18000))** - -- **[NFC] Fix provider unit test on PHP7.4 ([18073](https://github.com/civicrm/civicrm-core/pull/18073))** - -- **[REF] Move handling of form elements back to the Form ([17981](https://github.com/civicrm/civicrm-core/pull/17981))** - -- **Do not pass-by-reference to recur function ([18071](https://github.com/civicrm/civicrm-core/pull/18071))** - - **dev/financial#135 Remove unreachable doDirectPayment from manual processor ([18072](https://github.com/civicrm/civicrm-core/pull/18072))** -- **Refactor "applyLocale" and remove references to "language" column in UFMatch table ([18049](https://github.com/civicrm/civicrm-core/pull/18049))** - -- **Show cron warning on Scheduled Jobs admin page ([18065](https://github.com/civicrm/civicrm-core/pull/18065))** +- **dev/financial#139 contribution receive_date shouldn't change when payments come in ([17777](https://github.com/civicrm/civicrm-core/pull/17777))** -- **5.28 ([18069](https://github.com/civicrm/civicrm-core/pull/18069))** -- **Use correct pdf package to generate pdf file on invoice download/email activity ([18056](https://github.com/civicrm/civicrm-core/pull/18056))** +- **event#35: move statusBounce out of BAO layer; don't allow self-service when dis… ([18040](https://github.com/civicrm/civicrm-core/pull/18040))** -- **Fix buggy placement of icons on buttons ([18005](https://github.com/civicrm/civicrm-core/pull/18005))** +- **event#38 fix wording on event reg page ([17695](https://github.com/civicrm/civicrm-core/pull/17695))** -- **[REF] Even less variable variables ([18058](https://github.com/civicrm/civicrm-core/pull/18058))** +- **dev/event#40 - EventCart - Check legacy setting until extension is public ([18101](https://github.com/civicrm/civicrm-core/pull/18101))** -- **dev/core#1905 rework #17942 with simpler ts strings ([18064](https://github.com/civicrm/civicrm-core/pull/18064))** -- **Extract code to set isEmailReceipt in Contribution.completeOrder ([18039](https://github.com/civicrm/civicrm-core/pull/18039))** -- **[REF] remove first attempt to set currency in repeattransaction flow ([18055](https://github.com/civicrm/civicrm-core/pull/18055))** +- **dev/wordpress#66 Re-instate newer variables but with more support for… ([18068](https://github.com/civicrm/civicrm-core/pull/18068))** -- **5.28 ([18063](https://github.com/civicrm/civicrm-core/pull/18063))** -- **dev/core#1905 Add configure icons on public pages ([17942](https://github.com/civicrm/civicrm-core/pull/17942))** +- **dev/core#183 Use TempTable builder to generate table for import ([17827](https://github.com/civicrm/civicrm-core/pull/17827))** -- **CRM_Utils_Hook: deprecation warning and short array syntax ([17995](https://github.com/civicrm/civicrm-core/pull/17995))** +- **[dev/core#750] Don't check server variables if we're running in CLI ([17636](https://github.com/civicrm/civicrm-core/pull/17636))** -- **event#35: move statusBounce out of BAO layer; don't allow self-service when dis… ([18040](https://github.com/civicrm/civicrm-core/pull/18040))** +- **dev/core#785 Differentiate smart group from regular group using icon in select2 field ([17927](https://github.com/civicrm/civicrm-core/pull/17927))** -- **Why not make the buttons flat? ([18054](https://github.com/civicrm/civicrm-core/pull/18054))** +- **dev/core#785 Differentiate smart group from regular group using icon in select2 field ([13958](https://github.com/civicrm/civicrm-core/pull/13958))** -- **[REF] Simplify location metadata handling in Export class ([17951](https://github.com/civicrm/civicrm-core/pull/17951))** +- **dev/core#1090 Update extendedSerializeData to use the Backbone namesp… ([17855](https://github.com/civicrm/civicrm-core/pull/17855))** -- **5.28 ([18059](https://github.com/civicrm/civicrm-core/pull/18059))** +- **dev/core#1113 - Decimal Separator - Invalid value "total_amount" (NaN,N) creating or editing a membership ([16429](https://github.com/civicrm/civicrm-core/pull/16429))** -- **[REF] Do not pass by reference to the recur function ([18057](https://github.com/civicrm/civicrm-core/pull/18057))** +- **dev/core#1137 - Allow ssl connection to mysql by specifying in DSN ([17706](https://github.com/civicrm/civicrm-core/pull/17706))** -- **[REF] Simplify getMembershipStatusByDate more ([18051](https://github.com/civicrm/civicrm-core/pull/18051))** +- **dev/core#1280 Fix ContributionPage soft_credit translation ([16838](https://github.com/civicrm/civicrm-core/pull/16838))** -- **Fix obscure dedupe scenario where 'bad' location data can overwrite good data ([17993](https://github.com/civicrm/civicrm-core/pull/17993))** +- **dev/core#1578 - Fix APIv4 chaining with custom fields ([17866](https://github.com/civicrm/civicrm-core/pull/17866))** -- **dev/drupal#127 - require email for the Useradd task and errors not showing ([17915](https://github.com/civicrm/civicrm-core/pull/17915))** +- **dev/core#1665 Remove the having clause as well as having needs a group by ([18052](https://github.com/civicrm/civicrm-core/pull/18052))** -- **dev/core#1921 [Ref] remove isoToMysql ([18025](https://github.com/civicrm/civicrm-core/pull/18025))** +- **dev/core#1670 copy custom fields from master to shared address ([17580](https://github.com/civicrm/civicrm-core/pull/17580))** -- **dev/core#1665 Remove the having clause as well as having needs a group by ([18052](https://github.com/civicrm/civicrm-core/pull/18052))** +- **dev/core#1679: Ensure Paypal IPN always updates the next scheduled payment date ([17744](https://github.com/civicrm/civicrm-core/pull/17744))** -- **Remove ORDER BY and GROUP BY from alphabetQuery to improve performance ([16877](https://github.com/civicrm/civicrm-core/pull/16877))** +- **dev/core#1725 Only export primary address fields ([17458](https://github.com/civicrm/civicrm-core/pull/17458))** -- **Fix JQuery Validation for radios ([17937](https://github.com/civicrm/civicrm-core/pull/17937))** +- **dev/core#1751: [Create Email] Only Show Update/Save Template when User has Permission to Edit Templates ([17480](https://github.com/civicrm/civicrm-core/pull/17480))** -- **[REF] Simplify membership status date handling ([18030](https://github.com/civicrm/civicrm-core/pull/18030))** +- **dev/core#1755 Fix reCaptcha on Mailing Subscribe ([17305](https://github.com/civicrm/civicrm-core/pull/17305))** -- **Fix PaypalIPN single function to not receive-by-reference ([18044](https://github.com/civicrm/civicrm-core/pull/18044))** +- **dev/core#1767 Fix phone key parsing in CRM_Dedupe_Finder ([17361](https://github.com/civicrm/civicrm-core/pull/17361) and [17882](https://github.com/civicrm/civicrm-core/pull/17882))** -- **Remove requirement to pass 'contribution_status_id' => Pending from order.create ([18018](https://github.com/civicrm/civicrm-core/pull/18018))** +- **dev/core#1768 - Add CiviMail synchronisation frequency setting. ([17709](https://github.com/civicrm/civicrm-core/pull/17709))** -- **[REF] Clean up handling of financial_type_id override ([18032](https://github.com/civicrm/civicrm-core/pull/18032))** +- **core#1795: Searchable Parent tags ([17513](https://github.com/civicrm/civicrm-core/pull/17513))** -- **Use saved contribution's line items rather than the primaryContributionID ([18033](https://github.com/civicrm/civicrm-core/pull/18033))** +- **core#1805: Autocomplete-select custom field is not searchable ([17569](https://github.com/civicrm/civicrm-core/pull/17569))** -- **[REF] Remove transaction from completeOrder signature ([18046](https://github.com/civicrm/civicrm-core/pull/18046))** +- **dev/core#1812 Missing view when logging set in a non-US English instance ([17815](https://github.com/civicrm/civicrm-core/pull/17815))** -- **Wrap multi record custom field inside a div ([17966](https://github.com/civicrm/civicrm-core/pull/17966))** +- **core#1826: Ignore location_type_id when deduping postal address ([17645](https://github.com/civicrm/civicrm-core/pull/17645))** -- **[Test fix] We might need this to ensure really quick test runs don't fail ([18045](https://github.com/civicrm/civicrm-core/pull/18045))** +- **dev/core#1827 activity search - fixing search by tags ([17655](https://github.com/civicrm/civicrm-core/pull/17655) and [17755](https://github.com/civicrm/civicrm-core/pull/17755))** -- **dev/core#1137 - Allow ssl connection to mysql by specifying in DSN ([17706](https://github.com/civicrm/civicrm-core/pull/17706))** +- **dev/core#1853 - Fix validation errors when removing contact subtype ([17765](https://github.com/civicrm/civicrm-core/pull/17765))** -- **Test - attempt to replicate #17852 ([18038](https://github.com/civicrm/civicrm-core/pull/18038))** +- **dev/core#1855 - Allow different output formats for CiviReport results and untangle code ([17901](https://github.com/civicrm/civicrm-core/pull/17901))** -- **[REF] Remove transaction from BaseIPN completeTransaction call ([18042](https://github.com/civicrm/civicrm-core/pull/18042))** +- **dev/core#1858 Prevent Duplicate contact records being created and har… ([17769](https://github.com/civicrm/civicrm-core/pull/17769))** -- **Remove url-tracking in mass sms. dev/core#1843 ([17700](https://github.com/civicrm/civicrm-core/pull/17700))** +- **dev/core#1861 fix failure to unset location_type_id when saving uffield ([17812](https://github.com/civicrm/civicrm-core/pull/17812))** -- **Do not overwrite values saved from the repeatContribution routine ([17972](https://github.com/civicrm/civicrm-core/pull/17972))** +- **dev/core#1863 Downgrade checkEnvironment level and skip non-prod checks ([17807](https://github.com/civicrm/civicrm-core/pull/17807))** -- **dev/core#1916 - Fix naming of case export fields / remove ones that aren't true ([18043](https://github.com/civicrm/civicrm-core/pull/18043))** +- **dev/core#1868 - Regression - Description field is always blank on profiles admin page and slew of E_NOTICES ([17786](https://github.com/civicrm/civicrm-core/pull/17786))** -- **SystemCheck: add ability to efficiently run only specified checks ([17824](https://github.com/civicrm/civicrm-core/pull/17824))** +- **dev/core#1869 - Include BOM in attachment when sending CSV CiviReport via mail_report job ([17806](https://github.com/civicrm/civicrm-core/pull/17806))** -- **Add testing to Authorize.net and remove the lines that are repeated ([18028](https://github.com/civicrm/civicrm-core/pull/18028))** +- **dev/core#1871 - require_once's that include "packages/" in the path don't work on drupal 8 ([17822](https://github.com/civicrm/civicrm-core/pull/17822))** -- **Add test on status calculation ([18037](https://github.com/civicrm/civicrm-core/pull/18037))** +- **dev/core#1872 - Packages and vendor path calculation used in system check is outdated ([17844](https://github.com/civicrm/civicrm-core/pull/17844))** -- **Fix qill typo ([18041](https://github.com/civicrm/civicrm-core/pull/18041))** +- **dev/core#1874 - Failing test for new Individual form ([17835](https://github.com/civicrm/civicrm-core/pull/17835))** -- **dev/core#1919 - Missing resubscribe url in text/plain version of unsubscribe confirmation email ([18015](https://github.com/civicrm/civicrm-core/pull/18015))** +- **dev/core#1880 add backticks to custom field insertions ([17848](https://github.com/civicrm/civicrm-core/pull/17848))** -- **[Ref] Simplify is_email_receipt in sendMail ([18029](https://github.com/civicrm/civicrm-core/pull/18029))** +- **dev/core#1888 - Fix one line in PR 17888 ([17898](https://github.com/civicrm/civicrm-core/pull/17898))** -- **Update flexmailer release information ([17912](https://github.com/civicrm/civicrm-core/pull/17912))** +- **dev/core#1888 and dev/core#1885 - Fatal error on advanced search and warnings and missing group display on contact form ([17888](https://github.com/civicrm/civicrm-core/pull/17888))** -- **[REF] [Test] Minor simplification on test ([18019](https://github.com/civicrm/civicrm-core/pull/18019))** +- **dev/core#1894 - Make CRM_Activity_Form_SearchTest::testQill less time-sensitive ([17902](https://github.com/civicrm/civicrm-core/pull/17902))** -- **dev/core#1906 - Allow payment create api to record payment on Failed … ([17943](https://github.com/civicrm/civicrm-core/pull/17943))** +- **dev/core#1895 fix first/last name adv search ([17950](https://github.com/civicrm/civicrm-core/pull/17950))** -- **Fix for failing test ([18036](https://github.com/civicrm/civicrm-core/pull/18036))** +- **dev/core#1902: "Contribution Source" profile field has no effect ([17930](https://github.com/civicrm/civicrm-core/pull/17930))** -- **Remove invalid use of crmMoney formatter ([18031](https://github.com/civicrm/civicrm-core/pull/18031))** +- **dev/core#1905 force backend links for new "configure" buttons ([18088](https://github.com/civicrm/civicrm-core/pull/18088))** -- **Remove main PaymentExpress class ([18010](https://github.com/civicrm/civicrm-core/pull/18010))** +- **dev/core#1905 rework #17942 with simpler ts strings ([18064](https://github.com/civicrm/civicrm-core/pull/18064))** -- **[Ref] Remove transaction instantiation in PaypalPro ([18026](https://github.com/civicrm/civicrm-core/pull/18026))** +- **dev/core#1905 Add configure icons on public pages ([17942](https://github.com/civicrm/civicrm-core/pull/17942))** -- **[REF] Stop instantiating transaction in PaypalIPN ([18020](https://github.com/civicrm/civicrm-core/pull/18020))** +- **dev/core#1906 - Allow payment create api to record payment on Failed … ([17943](https://github.com/civicrm/civicrm-core/pull/17943))** -- **Remove unused parameter ids['billing'] ([18021](https://github.com/civicrm/civicrm-core/pull/18021))** +- **dev/core#1909 Fix E-notice when adding a field on a profile ([17962](https://github.com/civicrm/civicrm-core/pull/17962))** -- **5.28 to master ([18023](https://github.com/civicrm/civicrm-core/pull/18023))** +- **dev/core#1909 Fix e-notice when adding a payment processor ([17964](https://github.com/civicrm/civicrm-core/pull/17964))** -- **Cache loader - remove legacy handling, handle null result from setting ([17999](https://github.com/civicrm/civicrm-core/pull/17999))** +- **dev/core#1909 - E_NOTICE opening file-on-case ([17959](https://github.com/civicrm/civicrm-core/pull/17959))** -- **Change inform-icon to fa-info-circle ([18001](https://github.com/civicrm/civicrm-core/pull/18001))** +- **dev/core#1909 - E_NOTICE on contribution edit ([18006](https://github.com/civicrm/civicrm-core/pull/18006))** -- **[REF] Remove pass-by-reference & always empty param ([17984](https://github.com/civicrm/civicrm-core/pull/17984))** +- **dev/core#1909 - Avoid E_Notice on SMS provider form when no default url ([17985](https://github.com/civicrm/civicrm-core/pull/17985))** -- **CIVICRM_BAO_CACHE_ADAPTER - Remove obsolete option ([17990](https://github.com/civicrm/civicrm-core/pull/17990))** +- **dev/core#1913 Allow for schemas to be added by extensions if they are… ([17986](https://github.com/civicrm/civicrm-core/pull/17986))** -- **SQL temp table not using utf8mb4 if server default already set to utf8mb4 ([18012](https://github.com/civicrm/civicrm-core/pull/18012))** +- **dev/core#1915 - E_NOTICE when making pcp contribution ([18002](https://github.com/civicrm/civicrm-core/pull/18002))** -- **Wrong link to admin page in error message about FROM address on PCP page ([17996](https://github.com/civicrm/civicrm-core/pull/17996))** +- **dev/core#1916 - Fix naming of case export fields / remove ones that aren't true ([18043](https://github.com/civicrm/civicrm-core/pull/18043))** -- **[REF] Tighten up function signature for dedupePair ([17923](https://github.com/civicrm/civicrm-core/pull/17923))** +- **dev/core#1918 - Remove dubious qfkey checking code that never runs ([18007](https://github.com/civicrm/civicrm-core/pull/18007))** -- **[Ref] Move noisily deprecate BaseIPN->sendMail, call api from it rather than BAO function ([17982](https://github.com/civicrm/civicrm-core/pull/17982))** +- **dev/core#1919 - Missing resubscribe url in text/plain version of unsubscribe confirmation email ([18015](https://github.com/civicrm/civicrm-core/pull/18015))** -- **[REF] Use CRM_Utils_Mail::send for sending emails for confirming unsu… ([17396](https://github.com/civicrm/civicrm-core/pull/17396))** +- **dev/core#1921 [Ref] remove isoToMysql ([18025](https://github.com/civicrm/civicrm-core/pull/18025))** -- **CRM_Core_BAO_Cache - Remove functions deprecated a year ago ([17989](https://github.com/civicrm/civicrm-core/pull/17989))** +- **dev/core#1928 Fix HTML5 error due to required attribute being set swi… ([18080](https://github.com/civicrm/civicrm-core/pull/18080))** -- **[Test framework] re-re-fix test and add test for test ([18013](https://github.com/civicrm/civicrm-core/pull/18013))** +- **dev/core#1932 - Make status-checks more polite during upgrade ([18085](https://github.com/civicrm/civicrm-core/pull/18085))** -- **Re-fix test ([18009](https://github.com/civicrm/civicrm-core/pull/18009))** +- **dev/core#1937 - Upgrade message about needing composer patching turned on and updating mysql in DSN strings ([18174](https://github.com/civicrm/civicrm-core/pull/18174))** -- **[NFC] Improve docs for APIv4 Save action ([18004](https://github.com/civicrm/civicrm-core/pull/18004))** +- **dev/core#1983 Fix to tax calculation on multi-line-item ([18290](https://github.com/civicrm/civicrm-core/pull/18290))** -- **dev/core#1909 - E_NOTICE on contribution edit ([18006](https://github.com/civicrm/civicrm-core/pull/18006))** +- **dev/core#1972 Fix tax_amount calclation on renewal form ([18271](https://github.com/civicrm/civicrm-core/pull/18271))** -- **dev/core#1918 - Remove dubious qfkey checking code that never runs ([18007](https://github.com/civicrm/civicrm-core/pull/18007))** -- **5.28 ([18008](https://github.com/civicrm/civicrm-core/pull/18008))** +- **Revert "Swap out button/submit inputs for button elements" ([18185](https://github.com/civicrm/civicrm-core/pull/18185))** -- **[REF] Reduce calls to CRM_Member_PseudoConstant::membershipType ([17987](https://github.com/civicrm/civicrm-core/pull/17987))** +- **Swap out button/submit inputs for button elements ([18091](https://github.com/civicrm/civicrm-core/pull/18091))** -- **dev/core#1915 - E_NOTICE when making pcp contribution ([18002](https://github.com/civicrm/civicrm-core/pull/18002))** +- **Installation doclinks not getting url-rewritten ([18175](https://github.com/civicrm/civicrm-core/pull/18175))** -- **[Test framework] - Update failing test ([18003](https://github.com/civicrm/civicrm-core/pull/18003))** +- **Fix button name on updated form ([18000](https://github.com/civicrm/civicrm-core/pull/18000))** -- **Fix repeattransaction api to use custom data from the template contribution ([17975](https://github.com/civicrm/civicrm-core/pull/17975))** +- **Refactor "applyLocale" and remove references to "language" column in UFMatch table ([18049](https://github.com/civicrm/civicrm-core/pull/18049))** -- **dev/financial#139 contribution receive_date shouldn't change when payments come in ([17777](https://github.com/civicrm/civicrm-core/pull/17777))** +- **Show cron warning on Scheduled Jobs admin page ([18065](https://github.com/civicrm/civicrm-core/pull/18065))** -- **5.28 ([17997](https://github.com/civicrm/civicrm-core/pull/17997))** +- **Use correct pdf package to generate pdf file on invoice download/email activity ([18056](https://github.com/civicrm/civicrm-core/pull/18056))** -- **Fix case activity field set to allow long details to be exported ([17970](https://github.com/civicrm/civicrm-core/pull/17970))** +- **Fix buggy placement of icons on buttons ([18005](https://github.com/civicrm/civicrm-core/pull/18005))** -- **dev/core#1913 Allow for schemas to be added by extensions if they are… ([17986](https://github.com/civicrm/civicrm-core/pull/17986))** +- **CRM_Utils_Hook: deprecation warning and short array syntax ([17995](https://github.com/civicrm/civicrm-core/pull/17995))** -- **[REF] Use Standard function cacheClause to re-use contact acl cache t… ([17707](https://github.com/civicrm/civicrm-core/pull/17707))** +- **Why not make the buttons flat? ([18054](https://github.com/civicrm/civicrm-core/pull/18054))** -- **Fixed filling default values for tagssets in the advanced search form ([17978](https://github.com/civicrm/civicrm-core/pull/17978))** +- **Fix obscure dedupe scenario where 'bad' location data can overwrite good data ([17993](https://github.com/civicrm/civicrm-core/pull/17993))** -- **dev/core#1909 - Avoid E_Notice on SMS provider form when no default url ([17985](https://github.com/civicrm/civicrm-core/pull/17985))** +- **Remove ORDER BY and GROUP BY from alphabetQuery to improve performance ([16877](https://github.com/civicrm/civicrm-core/pull/16877))** -- **Remove duplicate cache flush ([17988](https://github.com/civicrm/civicrm-core/pull/17988))** +- **Fix JQuery Validation for radios ([17937](https://github.com/civicrm/civicrm-core/pull/17937))** -- **[REF] Extract setUserContext on contribution form & cleanup on backend add membership form ([17968](https://github.com/civicrm/civicrm-core/pull/17968))** +- **Fix PaypalIPN single function to not receive-by-reference ([18044](https://github.com/civicrm/civicrm-core/pull/18044))** -- **dev/core#1755 Fix reCaptcha on Mailing Subscribe ([17305](https://github.com/civicrm/civicrm-core/pull/17305))** +- **Remove requirement to pass 'contribution_status_id' => Pending from order.create ([18018](https://github.com/civicrm/civicrm-core/pull/18018))** -- **[REF] Make explicit what we are doing with 'values' in this code ([17979](https://github.com/civicrm/civicrm-core/pull/17979))** +- **Use saved contribution's line items rather than the primaryContributionID ([18033](https://github.com/civicrm/civicrm-core/pull/18033))** -- **Simplify caching of status checks ([17817](https://github.com/civicrm/civicrm-core/pull/17817))** +- **Wrap multi record custom field inside a div ([17966](https://github.com/civicrm/civicrm-core/pull/17966))** -- **[REF] Reduce interaction between dedupe code and createProfileContact ([17920](https://github.com/civicrm/civicrm-core/pull/17920))** +- **Remove url-tracking in mass sms. dev/core#1843 ([17700](https://github.com/civicrm/civicrm-core/pull/17700))** -- **ensure custom field checkboxes are populated in profiles ([17977](https://github.com/civicrm/civicrm-core/pull/17977))** +- **Do not overwrite values saved from the repeatContribution routine ([17972](https://github.com/civicrm/civicrm-core/pull/17972))** -- **dev/core#785 Differentiate smart group from regular group using icon in select2 field ([17927](https://github.com/civicrm/civicrm-core/pull/17927))** +- **SystemCheck: add ability to efficiently run only specified checks ([17824](https://github.com/civicrm/civicrm-core/pull/17824))** -- **[REF] Minor code clean up ([17974](https://github.com/civicrm/civicrm-core/pull/17974))** +- **Change inform-icon to fa-info-circle ([18001](https://github.com/civicrm/civicrm-core/pull/18001))** -- **5.28 to master ([17976](https://github.com/civicrm/civicrm-core/pull/17976))** +- **CIVICRM_BAO_CACHE_ADAPTER - Remove obsolete option ([17990](https://github.com/civicrm/civicrm-core/pull/17990))** -- **[REF] Grant cleanup ([17967](https://github.com/civicrm/civicrm-core/pull/17967))** +- **SQL temp table not using utf8mb4 if server default already set to utf8mb4 ([18012](https://github.com/civicrm/civicrm-core/pull/18012))** -- **[NFC] Add in a unit test of calling the contribution page widget endp… ([17965](https://github.com/civicrm/civicrm-core/pull/17965))** +- **Wrong link to admin page in error message about FROM address on PCP page ([17996](https://github.com/civicrm/civicrm-core/pull/17996))** -- **5.28 ([17963](https://github.com/civicrm/civicrm-core/pull/17963))** +- **CRM_Core_BAO_Cache - Remove functions deprecated a year ago ([17989](https://github.com/civicrm/civicrm-core/pull/17989))** -- **dev/core#1909 Fix E-notice when adding a field on a profile ([17962](https://github.com/civicrm/civicrm-core/pull/17962))** +- **Fix repeattransaction api to use custom data from the template contribution ([17975](https://github.com/civicrm/civicrm-core/pull/17975))** -- **dev/core#1909 Fix e-notice when adding a payment processor ([17964](https://github.com/civicrm/civicrm-core/pull/17964))** +- **Fix case activity field set to allow long details to be exported ([17970](https://github.com/civicrm/civicrm-core/pull/17970))** -- **dev/core#1909 - E_NOTICE opening file-on-case ([17959](https://github.com/civicrm/civicrm-core/pull/17959))** +- **Fixed filling default values for tagssets in the advanced search form ([17978](https://github.com/civicrm/civicrm-core/pull/17978))** -- **[Ref] Simplify field reference ([17941](https://github.com/civicrm/civicrm-core/pull/17941))** +- **Remove duplicate cache flush ([17988](https://github.com/civicrm/civicrm-core/pull/17988))** -- **[Test framework] - Combine triplicate createCase functions ([17957](https://github.com/civicrm/civicrm-core/pull/17957))** +- **Simplify caching of status checks ([17817](https://github.com/civicrm/civicrm-core/pull/17817))** -- **[REF] Refactor to use the standard CRM_Core_Form::addRadio function f… ([17932](https://github.com/civicrm/civicrm-core/pull/17932))** +- **ensure custom field checkboxes are populated in profiles ([17977](https://github.com/civicrm/civicrm-core/pull/17977))** - **Upgrade PEAR/mail_mime package to be compliant with PHP7.4 and deploy it using composer ([17948](https://github.com/civicrm/civicrm-core/pull/17948))** -- **[REF] Fix the default to_financial_account_id for generated transactions ([17938](https://github.com/civicrm/civicrm-core/pull/17938))** - - **APIv4 - Add keyword to select all custom fields ([17955](https://github.com/civicrm/civicrm-core/pull/17955))** -- **[REF] Upgrade dompdf version to be more compatible with PHP7.4 ([17946](https://github.com/civicrm/civicrm-core/pull/17946))** - -- **[REF] [Tests] Cleanup test declaration to take advantage of mapping improvements ([17939](https://github.com/civicrm/civicrm-core/pull/17939))** - -- **dev/drupal#127 - CRM_Core_Session::setStatus() gets ignored sometimes ([17914](https://github.com/civicrm/civicrm-core/pull/17914))** - -- **5.28 to master ([17953](https://github.com/civicrm/civicrm-core/pull/17953))** - -- **[REF] Remove unnecessary complexity on im export ([17949](https://github.com/civicrm/civicrm-core/pull/17949))** - -- **NFC - Docblock cleanup ([17945](https://github.com/civicrm/civicrm-core/pull/17945))** - - **CRM_Utils_Check_Component_Case - Guard against post-upgrade crash ([17944](https://github.com/civicrm/civicrm-core/pull/17944))** -- **Remove error checking by-pass in tests ([17940](https://github.com/civicrm/civicrm-core/pull/17940))** - - **Remove extraneous opportunistic cache flush. ([17936](https://github.com/civicrm/civicrm-core/pull/17936))** -- **dev/core#1902: "Contribution Source" profile field has no effect ([17930](https://github.com/civicrm/civicrm-core/pull/17930))** - -- **[REF] GroupContact BAO - Minor code cleanup ([17928](https://github.com/civicrm/civicrm-core/pull/17928))** - -- **5.28 to master ([17931](https://github.com/civicrm/civicrm-core/pull/17931))** +- **Improve caching of current domain ([17916](https://github.com/civicrm/civicrm-core/pull/17916))** -- **[REF] Minor function signuture cleanup ([17922](https://github.com/civicrm/civicrm-core/pull/17922))** +- **Setup UI - Validate that at least one "Component" is enabled ([17778](https://github.com/civicrm/civicrm-core/pull/17778))** -- **[REF] Do not pass variable by reference ([17921](https://github.com/civicrm/civicrm-core/pull/17921))** +- **Member detail report: nest "in" options in parentheses ([17911](https://github.com/civicrm/civicrm-core/pull/17911))** -- **5.28 ([17926](https://github.com/civicrm/civicrm-core/pull/17926))** +- **Fix sticky table header on "Find Activities" page ([17917](https://github.com/civicrm/civicrm-core/pull/17917))** -- **[NFC] Update a few doc/wiki links in code comments ([17918](https://github.com/civicrm/civicrm-core/pull/17918))** +- **Remove unused "ufUniqID" session variable ([17904](https://github.com/civicrm/civicrm-core/pull/17904))** -- **Improve caching of current domain ([17916](https://github.com/civicrm/civicrm-core/pull/17916))** +- **Replace a load of references to the wiki with docs links ([17900](https://github.com/civicrm/civicrm-core/pull/17900))** -- **Setup UI - Validate that at least one "Component" is enabled ([17778](https://github.com/civicrm/civicrm-core/pull/17778))** +- **Remove check for valid email in synchronizeUFMatch ([17771](https://github.com/civicrm/civicrm-core/pull/17771))** -- **Member detail report: nest "in" options in parentheses ([17911](https://github.com/civicrm/civicrm-core/pull/17911))** +- **Call apiv4 from Contribution create rather than fugly addActivity function ([17881](https://github.com/civicrm/civicrm-core/pull/17881))** -- **Fix sticky table header on "Find Activities" page ([17917](https://github.com/civicrm/civicrm-core/pull/17917))** +- **APIv4 - Add BasicEntity helper class ([17899](https://github.com/civicrm/civicrm-core/pull/17899))** -- **5.28 ([17908](https://github.com/civicrm/civicrm-core/pull/17908))** +- **Add APIv4 and pseudoconstants for RelationshipCache ([17879](https://github.com/civicrm/civicrm-core/pull/17879))** -- **[REF] remove unnecessary variable variables ([17903](https://github.com/civicrm/civicrm-core/pull/17903))** +- **Update version in the test_data_second_domain file and also update the setVersion script to update the file version as necessary ([17897](https://github.com/civicrm/civicrm-core/pull/17897))** -- **dev/core#1855 - Allow different output formats for CiviReport results and untangle code ([17901](https://github.com/civicrm/civicrm-core/pull/17901))** +- **Be a little less supportive to cvs ([17896](https://github.com/civicrm/civicrm-core/pull/17896))** -- **[REF] - Add helper function for the repetitive task of fetching multilingual ([17650](https://github.com/civicrm/civicrm-core/pull/17650))** +- **APIv4 - Specify BridgeEntities to assist with joins ([17808](https://github.com/civicrm/civicrm-core/pull/17808))** -- **[unreleased regression] Dummy processor now is ID 7 on buildkit sites ([17905](https://github.com/civicrm/civicrm-core/pull/17905))** +- **Event Cart ext: Move menu entries to extension ([17891](https://github.com/civicrm/civicrm-core/pull/17891))** -- **Remove unused "ufUniqID" session variable ([17904](https://github.com/civicrm/civicrm-core/pull/17904))** +- **EventCart ext: Cleanup and move form components to ext ([17885](https://github.com/civicrm/civicrm-core/pull/17885))** -- **Replace a load of references to the wiki with docs links ([17900](https://github.com/civicrm/civicrm-core/pull/17900))** +- **EventCart ext: Fix autogenerated code, remove unused hooks, update readme ([17884](https://github.com/civicrm/civicrm-core/pull/17884))** -- **dev/core#1894 - Make CRM_Activity_Form_SearchTest::testQill less time-sensitive ([17902](https://github.com/civicrm/civicrm-core/pull/17902))** +- **Load contribution page if live payment processor is disabled but test is available ([17828](https://github.com/civicrm/civicrm-core/pull/17828))** -- **dev/core#183 Use TempTable builder to generate table for import ([17827](https://github.com/civicrm/civicrm-core/pull/17827))** +- **Search debug ([17887](https://github.com/civicrm/civicrm-core/pull/17887))** -- **Remove check for valid email in synchronizeUFMatch ([17771](https://github.com/civicrm/civicrm-core/pull/17771))** +- **Use new checkPermissions shorthand in api calls ([17874](https://github.com/civicrm/civicrm-core/pull/17874))** -- **Call apiv4 from Contribution create rather than fugly addActivity function ([17881](https://github.com/civicrm/civicrm-core/pull/17881))** +- **Simplify flushing group contact cache query to reduce table locking and improve performance ([17846](https://github.com/civicrm/civicrm-core/pull/17846))** -- **APIv4 - Add BasicEntity helper class ([17899](https://github.com/civicrm/civicrm-core/pull/17899))** -- **mailing#70 Don't create users for test mail if user doesn't have permission ([17867](https://github.com/civicrm/civicrm-core/pull/17867))** -- **Add APIv4 and pseudoconstants for RelationshipCache ([17879](https://github.com/civicrm/civicrm-core/pull/17879))** +- **Disable frequency/interval fields if not required. Mark required if they are so they are validated before submit ([17526](https://github.com/civicrm/civicrm-core/pull/17526))** -- **dev/core#1670 copy custom fields from master to shared address ([17580](https://github.com/civicrm/civicrm-core/pull/17580))** +- **Fix currency symbol for Total Amount on contribution page ([17703](https://github.com/civicrm/civicrm-core/pull/17703))** -- **[Ref] Unit test attempt to create reported bugs , minor cleanup ([17560](https://github.com/civicrm/civicrm-core/pull/17560))** +- **RelationshipCache - Add a high-level index to facilitate relationship queries (more fields) ([17781](https://github.com/civicrm/civicrm-core/pull/17781))** -- **Update version in the test_data_second_domain file and also update th… ([17897](https://github.com/civicrm/civicrm-core/pull/17897))** +- **Hooks/Dispatcher - Close loopholes that occur around "preboot" hooks ([17831](https://github.com/civicrm/civicrm-core/pull/17831))** -- **dev/core#1888 - Fix one line in PR 17888 ([17898](https://github.com/civicrm/civicrm-core/pull/17898))** +- **APIv4 - Add shorthand for setCheckPermissions() ([17834](https://github.com/civicrm/civicrm-core/pull/17834))** -- **Be a little less supportive to cvs ([17896](https://github.com/civicrm/civicrm-core/pull/17896))** +- **Use PrematureExit exception instead of weird hack in tests ([17870](https://github.com/civicrm/civicrm-core/pull/17870))** -- **dev/core#1888 and dev/core#1885 - Fatal error on advanced search and warnings and missing group display on contact form ([17888](https://github.com/civicrm/civicrm-core/pull/17888))** +- **Remove unnecessary try/catch per #17729 ([17823](https://github.com/civicrm/civicrm-core/pull/17823))** -- **APIv4 - Specify BridgeEntities to assist with joins ([17808](https://github.com/civicrm/civicrm-core/pull/17808))** +- **Fixed DB Error: syntax error if line item refers to civicrm_case ([16626](https://github.com/civicrm/civicrm-core/pull/16626))** -- **Contribution Summary Report: Taking the currency filtered in the "gen… ([16736](https://github.com/civicrm/civicrm-core/pull/16736))** +- **Fix potential js error on summary screen when reloading blocks ([17865](https://github.com/civicrm/civicrm-core/pull/17865))** -- **Event Cart ext: Move menu entries to extension ([17891](https://github.com/civicrm/civicrm-core/pull/17891))** +- **Search Ext: fix loading options and parsing custom field names ([17864](https://github.com/civicrm/civicrm-core/pull/17864))** -- **5.28 ([17895](https://github.com/civicrm/civicrm-core/pull/17895))** +- **EventCart - Resolve BAO identity and uncommitted DAO changes ([17861](https://github.com/civicrm/civicrm-core/pull/17861))** -- **[Test Framework] - Tests for report downloads ([17892](https://github.com/civicrm/civicrm-core/pull/17892))** +- **Bump lodash from 4.17.15 to 4.17.19 ([17858](https://github.com/civicrm/civicrm-core/pull/17858))** -- **dev/core#1578 - Fix APIv4 chaining with custom fields ([17866](https://github.com/civicrm/civicrm-core/pull/17866))** +- **handlePaymentNotification() should not be declared as a static method ([17849](https://github.com/civicrm/civicrm-core/pull/17849))** -- **Unit test for #17361 ([17882](https://github.com/civicrm/civicrm-core/pull/17882))** +- **Move BAO and template files into event cart ([17743](https://github.com/civicrm/civicrm-core/pull/17743))** -- **EventCart ext: Cleanup and move form components to ext ([17885](https://github.com/civicrm/civicrm-core/pull/17885))** +- **Update regen.sh with new & upcoming core extensions ([17839](https://github.com/civicrm/civicrm-core/pull/17839))** -- **EventCart ext: Fix autogenerated code, remove unused hooks, update readme ([17884](https://github.com/civicrm/civicrm-core/pull/17884))** +- **MembershipRenewalTest - Fix failure ([17830](https://github.com/civicrm/civicrm-core/pull/17830))** -- **Load contribution page if live payment processor is disabled but test is available ([17828](https://github.com/civicrm/civicrm-core/pull/17828))** +- **Remove hard coded charset. ([17826](https://github.com/civicrm/civicrm-core/pull/17826))** -- **Search debug ([17887](https://github.com/civicrm/civicrm-core/pull/17887))** +- **APIv4 - Add activity contacts to APIv4 field spec ([17766](https://github.com/civicrm/civicrm-core/pull/17766))** -- **dev/core#1767 Fix phone key parsing in CRM_Dedupe_Finder ([17361](https://github.com/civicrm/civicrm-core/pull/17361))** +- **Adjust mysql SET NAMES in remaining places as we agreed this was the go ([17825](https://github.com/civicrm/civicrm-core/pull/17825))** -- **[NFC] Fix nonstandard header comments ([17880](https://github.com/civicrm/civicrm-core/pull/17880))** +- **Make new email open and url routes 'public' ([17813](https://github.com/civicrm/civicrm-core/pull/17813))** -- **dev/core#1751: [Create Email] Only Show Update/Save Template when User has Permission to Edit Templates ([17480](https://github.com/civicrm/civicrm-core/pull/17480))** +- **Make api get upgrade-safe ([17729](https://github.com/civicrm/civicrm-core/pull/17729))** -- **Use new checkPermissions shorthand in api calls ([17874](https://github.com/civicrm/civicrm-core/pull/17874))** +- **CRM_Utils_SQL - Add "onDuplicate()" and "syncInto()" helpers ([17780](https://github.com/civicrm/civicrm-core/pull/17780))** -- **5.28 ([17878](https://github.com/civicrm/civicrm-core/pull/17878))** +- **CheckEnv - Give new installs a grace period before 'Cron Not Running' msg ([17800](https://github.com/civicrm/civicrm-core/pull/17800))** -- **Simplify flushing group contact cache query to reduce table locking and improve performance ([17846](https://github.com/civicrm/civicrm-core/pull/17846))** +- **Fix 'Undefined variable: jsSet in CRM_Core_BAO_Mapping::loadSavedMapping()' ([17816](https://github.com/civicrm/civicrm-core/pull/17816))** -- **dev/core#1869 - Include BOM in attachment when sending CSV CiviReport via mail_report job ([17806](https://github.com/civicrm/civicrm-core/pull/17806))** -- **dev/core#1280 Fix ContributionPage soft_credit translation ([16838](https://github.com/civicrm/civicrm-core/pull/16838))** -- **[NFC] Comment block cleanup ([17872](https://github.com/civicrm/civicrm-core/pull/17872))** +- **Add hidden tag to search extension ([17789](https://github.com/civicrm/civicrm-core/pull/17789))** -- **REF Extract addToRecentItems from membership create ([17524](https://github.com/civicrm/civicrm-core/pull/17524))** +- **Sort permittedActivityTypes ([17794](https://github.com/civicrm/civicrm-core/pull/17794))** -- **Disable frequency/interval fields if not required. Mark required if they are so they are validated before submit ([17526](https://github.com/civicrm/civicrm-core/pull/17526))** +- **Add auto-renew status to membership detail report ([17683](https://github.com/civicrm/civicrm-core/pull/17683))** -- **dev/membership#18 Enhance parameters for Job.process_membership ([16298](https://github.com/civicrm/civicrm-core/pull/16298))** +- **APIv4 - Fix saving custom fields with same name ([17791](https://github.com/civicrm/civicrm-core/pull/17791))** -- **Fix currency symbol for Total Amount on contribution page ([17703](https://github.com/civicrm/civicrm-core/pull/17703))** +- **Add system check to ensure WP base page exists ([17698](https://github.com/civicrm/civicrm-core/pull/17698))** -- **RelationshipCache - Add a high-level index to facilitate relationship queries (more fields) ([17781](https://github.com/civicrm/civicrm-core/pull/17781))** +- **Fixed notice error on Relationships report ([17787](https://github.com/civicrm/civicrm-core/pull/17787))** -- **[REF] Fix a couple of jQuery errors that have cropped up ([17871](https://github.com/civicrm/civicrm-core/pull/17871))** +- **getLoggedInContactID() is a static function ([17783](https://github.com/civicrm/civicrm-core/pull/17783))** -- **Hooks/Dispatcher - Close loopholes that occur around "preboot" hooks ([17831](https://github.com/civicrm/civicrm-core/pull/17831))** +- **Add search extension ([17775](https://github.com/civicrm/civicrm-core/pull/17775))** -- **APIv4 - Add shorthand for setCheckPermissions() ([17834](https://github.com/civicrm/civicrm-core/pull/17834))** +- **Remove PaymentExpress ipn class ([17763](https://github.com/civicrm/civicrm-core/pull/17763))** -- **Use PrematureExit exception instead of weird hack in tests ([17870](https://github.com/civicrm/civicrm-core/pull/17870))** +- **Status Checks - Use more specific label regarding "Domain"/"Organization" check ([17776](https://github.com/civicrm/civicrm-core/pull/17776))** -- **dev/core#1113 - Decimal Separator - Invalid value "total_amount" (NaN,N) creating or editing a membership ([16429](https://github.com/civicrm/civicrm-core/pull/16429))** +- **Bump minimum upgradable version to 4.4.7 ([17750](https://github.com/civicrm/civicrm-core/pull/17750))** -- **(REF) regen.sh - Remove unusual handling of `zipcodes.mysql` ([17869](https://github.com/civicrm/civicrm-core/pull/17869))** +- **Remove unused, deprecated functions ([17761](https://github.com/civicrm/civicrm-core/pull/17761))** -- **dev/report#43 - Icon after saving a civireport instance is misleading ([17863](https://github.com/civicrm/civicrm-core/pull/17863))** +- **Improve efficiency of findFiles ([17745](https://github.com/civicrm/civicrm-core/pull/17745))** -- **Remove unnecessary try/catch per #17729 ([17823](https://github.com/civicrm/civicrm-core/pull/17823))** +- **APIv4 Explorer: Improve selection of fields for HAVING ([17746](https://github.com/civicrm/civicrm-core/pull/17746))** -- **dev/core#1725 Only export primary address fields ([17458](https://github.com/civicrm/civicrm-core/pull/17458))** +- **More unused functions in GenCode ([17756](https://github.com/civicrm/civicrm-core/pull/17756))** -- **api_v3_TaxContributionPageTest fix - remove hard coded processor id ([17860](https://github.com/civicrm/civicrm-core/pull/17860))** +- **Fix PHP notice on wordpress permissions form ([17758](https://github.com/civicrm/civicrm-core/pull/17758))** -- **Fixed DB Error: syntax error if line item refers to civicrm_case ([16626](https://github.com/civicrm/civicrm-core/pull/16626))** +- **Convert CRM.utils.formatDate tests to karma ([17757](https://github.com/civicrm/civicrm-core/pull/17757))** -- **Fix potential js error on summary screen when reloading blocks ([17865](https://github.com/civicrm/civicrm-core/pull/17865))** +- **Teach CRM.utils.formatDate to also show time ([17684](https://github.com/civicrm/civicrm-core/pull/17684))** -- **Search Ext: fix loading options and parsing custom field names ([17864](https://github.com/civicrm/civicrm-core/pull/17864))** +- **Add eventcart shell ([17741](https://github.com/civicrm/civicrm-core/pull/17741))** -- **EventCart - Resolve BAO identity and uncommitted DAO changes ([17861](https://github.com/civicrm/civicrm-core/pull/17861))** +- **Fixed for multi-select filter ([615](https://github.com/civicrm/civicrm-drupal/pull/615))** -- **[REF] ScheduledJob cleanup, remove unused var ([17862](https://github.com/civicrm/civicrm-core/pull/17862))** +- **fix url for file field ([608](https://github.com/civicrm/civicrm-drupal/pull/608))** -- **dev/core#1090 Update extendedSerializeData to use the Backbone namesp… ([17855](https://github.com/civicrm/civicrm-core/pull/17855))** +- **Slow down the frequency of WordPress "heartbeat" calls in CiviCRM admin ([214](https://github.com/civicrm/civicrm-wordpress/pull/214))** -- **dev/core#1874 - Failing test for new Individual form ([17835](https://github.com/civicrm/civicrm-core/pull/17835))** +- **Installation - Support "activate first" w/setup UI ([121](https://github.com/civicrm/civicrm-backdrop/pull/121))** +- **Installation - Support "activate first" w/setup UI ([606](https://github.com/civicrm/civicrm-drupal/pull/606))** -- **Bump lodash from 4.17.15 to 4.17.19 ([17858](https://github.com/civicrm/civicrm-core/pull/17858))** +## Miscellany -- **5.28 ([17859](https://github.com/civicrm/civicrm-core/pull/17859))** +- **[REF] Extract setUserContext on contribution form & cleanup on backend add + membership form + ([17968](https://github.com/civicrm/civicrm-core/pull/17968))** -- **[REF] Migrate Event Cart Setting into the Extension ([17841](https://github.com/civicrm/civicrm-core/pull/17841))** +- **[REF] remove unnecessary variable variables + ([17903](https://github.com/civicrm/civicrm-core/pull/17903))** -- **5.28 ([17856](https://github.com/civicrm/civicrm-core/pull/17856))** +- **[REF] - Add helper function for the repetitive task of fetching multilingual + ([17650](https://github.com/civicrm/civicrm-core/pull/17650))** -- **handlePaymentNotification() should not be declared as a static method ([17849](https://github.com/civicrm/civicrm-core/pull/17849))** +- **[Ref] Unit test attempt to create reported bugs , minor cleanup + ([17560](https://github.com/civicrm/civicrm-core/pull/17560))** -- **[REF] Only printOnly once ([17850](https://github.com/civicrm/civicrm-core/pull/17850))** +- **REF Extract addToRecentItems from membership create + ([17524](https://github.com/civicrm/civicrm-core/pull/17524))** -- **API tests - label versions in dataprovider versionThreeAndFour ([17847](https://github.com/civicrm/civicrm-core/pull/17847))** +- **[REF] Fix a couple of jQuery errors that have cropped up + ([17871](https://github.com/civicrm/civicrm-core/pull/17871))** -- **dev/core#1880 add backticks to custom field insertions ([17848](https://github.com/civicrm/civicrm-core/pull/17848))** +- **(REF) regen.sh - Remove unusual handling of `zipcodes.mysql` + ([17869](https://github.com/civicrm/civicrm-core/pull/17869))** -- **(REF) APIv4 ConformanceTest - Split apart into per-entity sub-tests ([17845](https://github.com/civicrm/civicrm-core/pull/17845))** +- **[REF] ScheduledJob cleanup, remove unused var + ([17862](https://github.com/civicrm/civicrm-core/pull/17862))** -- **dev/core#1872 - Packages and vendor path calculation used in system check is outdated ([17844](https://github.com/civicrm/civicrm-core/pull/17844))** +- **[REF] Migrate Event Cart Setting into the Extension + ([17841](https://github.com/civicrm/civicrm-core/pull/17841))** -- **[NFC] Re run regen after recent merges ([17842](https://github.com/civicrm/civicrm-core/pull/17842))** +- **[REF] Only printOnly once + ([17850](https://github.com/civicrm/civicrm-core/pull/17850))** -- **5.28 ([17843](https://github.com/civicrm/civicrm-core/pull/17843))** +- **(REF) APIv4 ConformanceTest - Split apart into per-entity sub-tests + ([17845](https://github.com/civicrm/civicrm-core/pull/17845))** -- **Move BAO and template files into event cart ([17743](https://github.com/civicrm/civicrm-core/pull/17743))** +- **(REF) WebsiteTest - Mitigate flaky failures + ([17833](https://github.com/civicrm/civicrm-core/pull/17833))** -- **Update regen.sh with new & upcoming core extensions ([17839](https://github.com/civicrm/civicrm-core/pull/17839))** +- **[REF] Follow up cleanup + ([17788](https://github.com/civicrm/civicrm-core/pull/17788))** -- **MembershipRenewalTest - Fix failure ([17830](https://github.com/civicrm/civicrm-core/pull/17830))** +- **[REF] Remove ACL join on temp table creation in Member ContributionDetail + report ([17723](https://github.com/civicrm/civicrm-core/pull/17723))** -- **Remove hard coded charset. ([17826](https://github.com/civicrm/civicrm-core/pull/17826))** +- **[REF] Do or do not - there is no try + ([17795](https://github.com/civicrm/civicrm-core/pull/17795))** -- **(REF) WebsiteTest - Mitigate flaky failures ([17833](https://github.com/civicrm/civicrm-core/pull/17833))** +- **[REF] Unused interface CRM_Report_Interface + ([17767](https://github.com/civicrm/civicrm-core/pull/17767))** -- **dev/core#1812 Missing view when logging set in a non-US English instance ([17815](https://github.com/civicrm/civicrm-core/pull/17815))** +- **REF - Cleanup StatusPreference BAO to be more standard + ([17801](https://github.com/civicrm/civicrm-core/pull/17801))** -- **dev/core#785 Differentiate smart group from regular group using icon in select2 field ([13958](https://github.com/civicrm/civicrm-core/pull/13958))** +- **[REF] Reduce interaction between dedupe code and createProfileContact + ([17920](https://github.com/civicrm/civicrm-core/pull/17920))** -- **APIv4 - Add activity contacts to APIv4 field spec ([17766](https://github.com/civicrm/civicrm-core/pull/17766))** +- **[Ref] Simplify field reference + ([17941](https://github.com/civicrm/civicrm-core/pull/17941))** -- **Adjust mysql SET NAMES in remaining places as we agreed this was the go ([17825](https://github.com/civicrm/civicrm-core/pull/17825))** +- **[REF] [Test] Minor simplification on test + ([18019](https://github.com/civicrm/civicrm-core/pull/18019))** -- **core#1805: Autocomplete-select custom field is not searchable ([17569](https://github.com/civicrm/civicrm-core/pull/17569))** +- **[Ref] Simplify is_email_receipt in sendMail + ([18029](https://github.com/civicrm/civicrm-core/pull/18029))** -- **event#38 fix wording on event reg page ([17695](https://github.com/civicrm/civicrm-core/pull/17695))** +- **[REF] Remove transaction from BaseIPN completeTransaction call + ([18042](https://github.com/civicrm/civicrm-core/pull/18042))** -- **core#1795: Searchable Parent tags ([17513](https://github.com/civicrm/civicrm-core/pull/17513))** +- **[REF] Simplify membership status date handling + ([18030](https://github.com/civicrm/civicrm-core/pull/18030))** -- **dev/core#1768 - Add CiviMail synchronisation frequency setting. ([17709](https://github.com/civicrm/civicrm-core/pull/17709))** +- **[REF] Clean up handling of financial_type_id override + ([18032](https://github.com/civicrm/civicrm-core/pull/18032))** -- **Make new email open and url routes 'public' ([17813](https://github.com/civicrm/civicrm-core/pull/17813))** +- **[REF] Remove transaction from completeOrder signature + ([18046](https://github.com/civicrm/civicrm-core/pull/18046))** -- **dev/core#1871 - require_once's that include "packages/" in the path don't work on drupal 8 ([17822](https://github.com/civicrm/civicrm-core/pull/17822))** +- **[Ref] Remove transaction instantiation in PaypalPro + ([18026](https://github.com/civicrm/civicrm-core/pull/18026))** -- **Make api get upgrade-safe ([17729](https://github.com/civicrm/civicrm-core/pull/17729))** +- **[REF] Stop instantiating transaction in PaypalIPN + ([18020](https://github.com/civicrm/civicrm-core/pull/18020))** -- **5.28 ([17799](https://github.com/civicrm/civicrm-core/pull/17799))** +- **[REF] Remove pass-by-reference & always empty param + ([17984](https://github.com/civicrm/civicrm-core/pull/17984))** -- **CRM_Utils_SQL - Add "onDuplicate()" and "syncInto()" helpers ([17780](https://github.com/civicrm/civicrm-core/pull/17780))** +- **[REF] Tighten up function signature for dedupePair + ([17923](https://github.com/civicrm/civicrm-core/pull/17923))** -- **CheckEnv - Give new installs a grace period before 'Cron Not Running' msg ([17800](https://github.com/civicrm/civicrm-core/pull/17800))** +- **[Ref] Move noisily deprecate BaseIPN->sendMail, call api from it rather + than BAO function + ([17982](https://github.com/civicrm/civicrm-core/pull/17982))** -- **Fix 'Undefined variable: jsSet in CRM_Core_BAO_Mapping::loadSavedMapping()' ([17816](https://github.com/civicrm/civicrm-core/pull/17816))** +- **[REF] Use CRM_Utils_Mail::send for sending emails for confirming unsubscribe + resubscribe auto replies and subscribing + ([17396](https://github.com/civicrm/civicrm-core/pull/17396))** -- **dev/core#1861 fix failure to unset location_type_id when saving uffield ([17812](https://github.com/civicrm/civicrm-core/pull/17812))** +- **[REF] Reduce calls to CRM_Member_PseudoConstant::membershipType + ([17987](https://github.com/civicrm/civicrm-core/pull/17987))** -- **dev/core#1863 Downgrade checkEnvironment level and skip non-prod checks ([17807](https://github.com/civicrm/civicrm-core/pull/17807))** +- **[REF] Use Standard function cacheClause to re-use contact acl cache table + ([17707](https://github.com/civicrm/civicrm-core/pull/17707))** -- **reporting#21 - don't multiple contribution details when a 1-to-many r… ([15435](https://github.com/civicrm/civicrm-core/pull/15435))** +- **[REF] Make explicit what we are doing with 'values' in this code + ([17979](https://github.com/civicrm/civicrm-core/pull/17979))** -- **Add hidden tag to search extension ([17789](https://github.com/civicrm/civicrm-core/pull/17789))** +- **[REF] Minor code clean up + ([17974](https://github.com/civicrm/civicrm-core/pull/17974))** -- **[REF] Follow up cleanup ([17788](https://github.com/civicrm/civicrm-core/pull/17788))** +- **[REF] Grant cleanup + ([17967](https://github.com/civicrm/civicrm-core/pull/17967))** -- **[REF] Remove ACL join on temp table creation in Member ContributionDe… ([17723](https://github.com/civicrm/civicrm-core/pull/17723))** +- **[REF] Refactor to use the standard CRM_Core_Form::addRadio function for a + number of radio elements + ([17932](https://github.com/civicrm/civicrm-core/pull/17932))** -- **dev/core#1858 Prevent Duplicate contact records being created and har… ([17769](https://github.com/civicrm/civicrm-core/pull/17769))** +- **[REF] Fix the default to_financial_account_id for generated transactions + ([17938](https://github.com/civicrm/civicrm-core/pull/17938))** -- **REF - Cleanup StatusPreference BAO to be more standard ([17801](https://github.com/civicrm/civicrm-core/pull/17801))** +- **[REF] Upgrade dompdf version to be more compatible with PHP7.4 + ([17946](https://github.com/civicrm/civicrm-core/pull/17946))** -- **Sort permittedActivityTypes ([17794](https://github.com/civicrm/civicrm-core/pull/17794))** +- **[REF] [Tests] Cleanup test declaration to take advantage of mapping + improvements ([17939](https://github.com/civicrm/civicrm-core/pull/17939))** -- **[REF] Do or do not - there is no try ([17795](https://github.com/civicrm/civicrm-core/pull/17795))** +- **[REF] Remove unnecessary complexity on im export + ([17949](https://github.com/civicrm/civicrm-core/pull/17949))** -- **Add auto-renew status to membership detail report ([17683](https://github.com/civicrm/civicrm-core/pull/17683))** +- **[REF] GroupContact BAO - Minor code cleanup + ([17928](https://github.com/civicrm/civicrm-core/pull/17928))** -- **APIv4 - Fix saving custom fields with same name ([17791](https://github.com/civicrm/civicrm-core/pull/17791))** +- **[REF] Minor function signuture cleanup + ([17922](https://github.com/civicrm/civicrm-core/pull/17922))** -- **Add system check to ensure WP base page exists ([17698](https://github.com/civicrm/civicrm-core/pull/17698))** +- **[REF] Do not pass variable by reference + ([17921](https://github.com/civicrm/civicrm-core/pull/17921))** -- **dev/core#1868 - Regression - Description field is always blank on profiles admin page and slew of E_NOTICES ([17786](https://github.com/civicrm/civicrm-core/pull/17786))** +- **[REF] remove first attempt to set currency in repeattransaction flow + ([18055](https://github.com/civicrm/civicrm-core/pull/18055))** -- **Fixed notice error on Relationships report ([17787](https://github.com/civicrm/civicrm-core/pull/17787))** +- **[REF] Even less variable variables + ([18058](https://github.com/civicrm/civicrm-core/pull/18058))** -- **getLoggedInContactID() is a static function ([17783](https://github.com/civicrm/civicrm-core/pull/17783))** +- **[REF] Move handling of form elements back to the Form + ([17981](https://github.com/civicrm/civicrm-core/pull/17981))** -- **5.28 ([17782](https://github.com/civicrm/civicrm-core/pull/17782))** +- **[REF] Simplify location metadata handling in Export class + ([17951](https://github.com/civicrm/civicrm-core/pull/17951))** -- **dev/core#1679: Ensure Paypal IPN always updates the next scheduled payment date ([17744](https://github.com/civicrm/civicrm-core/pull/17744))** +- **[REF] Do not pass by reference to the recur function + ([18057](https://github.com/civicrm/civicrm-core/pull/18057))** -- **5.28 ([17774](https://github.com/civicrm/civicrm-core/pull/17774))** +- **[REF] Simplify getMembershipStatusByDate more + ([18051](https://github.com/civicrm/civicrm-core/pull/18051))** -- **dev/core#1853 - Fix validation errors when removing contact subtype ([17765](https://github.com/civicrm/civicrm-core/pull/17765))** +- **[REF] Remove mail_mime package as now supplied by composer + ([300](https://github.com/civicrm/civicrm-packages/pull/300))** -- **Add search extension ([17775](https://github.com/civicrm/civicrm-core/pull/17775))** +- **[REF] Remove some deprecated size function calls replaced with length + ([299](https://github.com/civicrm/civicrm-packages/pull/299))** -- **[dev/core#750] Don't check server variables if we're running in CLI ([17636](https://github.com/civicrm/civicrm-core/pull/17636))** +- **[NFC] Fix provider unit test on PHP7.4 + ([18073](https://github.com/civicrm/civicrm-core/pull/18073))** -- **Remove PaymentExpress ipn class ([17763](https://github.com/civicrm/civicrm-core/pull/17763))** +- **NFC - Docblock cleanup + ([610](https://github.com/civicrm/civicrm-drupal/pull/610))** -- **Status Checks - Use more specific label regarding "Domain"/"Organization" check ([17776](https://github.com/civicrm/civicrm-core/pull/17776))** +- **[NFC] Update versions file to remove reference to Mail_mime and Mail + ([301](https://github.com/civicrm/civicrm-packages/pull/301))** -- **5.28 to master ([17770](https://github.com/civicrm/civicrm-core/pull/17770))** +- **[NFC] Re run regen after recent merges + ([17842](https://github.com/civicrm/civicrm-core/pull/17842))** -- **dev/drupal#114 and dev/core#1647 - Remove resource url status check ([17754](https://github.com/civicrm/civicrm-core/pull/17754))** +- **[NFC] Fix nonstandard header comments + ([17880](https://github.com/civicrm/civicrm-core/pull/17880))** -- **Bump minimum upgradable version to 4.4.7 ([17750](https://github.com/civicrm/civicrm-core/pull/17750))** +- **[NFC] Add in a unit test of calling the contribution page widget endpoint + and remove unneeded file + ([17965](https://github.com/civicrm/civicrm-core/pull/17965))** -- **[REF] Unused interface CRM_Report_Interface ([17767](https://github.com/civicrm/civicrm-core/pull/17767))** +- **[NFC] Improve docs for APIv4 Save action + ([18004](https://github.com/civicrm/civicrm-core/pull/18004))** -- **[TEST] Failing test for PR 16559 ([17256](https://github.com/civicrm/civicrm-core/pull/17256))** +- **NFC - Docblock cleanup + ([17945](https://github.com/civicrm/civicrm-core/pull/17945))** -- **update to pr 16559 ([17764](https://github.com/civicrm/civicrm-core/pull/17764))** +- **[NFC] Update a few doc/wiki links in code comments + ([17918](https://github.com/civicrm/civicrm-core/pull/17918))** -- **Remove unused, deprecated functions ([17761](https://github.com/civicrm/civicrm-core/pull/17761))** +- **[NFC] Comment block cleanup + ([17872](https://github.com/civicrm/civicrm-core/pull/17872))** -- **Improve efficiency of findFiles ([17745](https://github.com/civicrm/civicrm-core/pull/17745))** +- **[Test] Update hook signature in test + ([609](https://github.com/civicrm/civicrm-drupal/pull/609))** -- **APIv4 Explorer: Improve selection of fields for HAVING ([17746](https://github.com/civicrm/civicrm-core/pull/17746))** +- **Test - attempt to replicate #17852 + ([18038](https://github.com/civicrm/civicrm-core/pull/18038))** -- **More unused functions in GenCode ([17756](https://github.com/civicrm/civicrm-core/pull/17756))** +- **[Test fix] We might need this to ensure really quick test runs don't fail + ([18045](https://github.com/civicrm/civicrm-core/pull/18045))** -- **Fix PHP notice on wordpress permissions form ([17758](https://github.com/civicrm/civicrm-core/pull/17758))** +- **Add testing to Authorize.net and remove the lines that are repeated + ([18028](https://github.com/civicrm/civicrm-core/pull/18028))** -- **core#1826: Ignore location_type_id when deduping postal address ([17645](https://github.com/civicrm/civicrm-core/pull/17645))** +- **Add test on status calculation + ([18037](https://github.com/civicrm/civicrm-core/pull/18037))** -- **[Unit Test] dev/core#1173 and dev/core#1827 - Test for activity tag search - PR 17655 ([17755](https://github.com/civicrm/civicrm-core/pull/17755))** +- **Fix for failing test + ([18036](https://github.com/civicrm/civicrm-core/pull/18036))** -- **Convert CRM.utils.formatDate tests to karma ([17757](https://github.com/civicrm/civicrm-core/pull/17757))** +- **[Test framework] re-re-fix test and add test for test + ([18013](https://github.com/civicrm/civicrm-core/pull/18013))** -- **dev/core#1827 activity search - fixing search by tags ([17655](https://github.com/civicrm/civicrm-core/pull/17655))** +- **Re-fix test ([18009](https://github.com/civicrm/civicrm-core/pull/18009))** -- **Unused functions in GenCode ([17753](https://github.com/civicrm/civicrm-core/pull/17753))** +- **[Test framework] - Update failing test + ([18003](https://github.com/civicrm/civicrm-core/pull/18003))** -- **5.28 ([17751](https://github.com/civicrm/civicrm-core/pull/17751))** +- **[Test framework] - Combine triplicate createCase functions + ([17957](https://github.com/civicrm/civicrm-core/pull/17957))** -- **Teach CRM.utils.formatDate to also show time ([17684](https://github.com/civicrm/civicrm-core/pull/17684))** +- **Remove error checking by-pass in tests + ([17940](https://github.com/civicrm/civicrm-core/pull/17940))** -- **Fix typo in templates/CRM/Report/Form/Tabs/GroupBy.tpl ([17747](https://github.com/civicrm/civicrm-core/pull/17747))** +- **[Test Framework] - Tests for report downloads + ([17892](https://github.com/civicrm/civicrm-core/pull/17892))** -- **Add eventcart shell ([17741](https://github.com/civicrm/civicrm-core/pull/17741))** +- **api_v3_TaxContributionPageTest fix - remove hard coded processor id + ([17860](https://github.com/civicrm/civicrm-core/pull/17860))** -- **Fixed for multi-select filter ([615](https://github.com/civicrm/civicrm-drupal/pull/615))** +- **API tests - label versions in dataprovider versionThreeAndFour + ([17847](https://github.com/civicrm/civicrm-core/pull/17847))** -- **fix url for file field ([608](https://github.com/civicrm/civicrm-drupal/pull/608))** +- **Extract code to set isEmailReceipt in Contribution.completeOrder + ([18039](https://github.com/civicrm/civicrm-core/pull/18039))** -- **[Test] Update hook signature in test ([609](https://github.com/civicrm/civicrm-drupal/pull/609))** +- **Do not pass-by-reference to recur function + ([18071](https://github.com/civicrm/civicrm-core/pull/18071))** -- **NFC - Docblock cleanup ([610](https://github.com/civicrm/civicrm-drupal/pull/610))** +- **Unused functions in GenCode + ([17753](https://github.com/civicrm/civicrm-core/pull/17753))** -- **Installation - Support "activate first" w/setup UI ([606](https://github.com/civicrm/civicrm-drupal/pull/606))** +- **Fix typo in templates/CRM/Report/Form/Tabs/GroupBy.tpl + ([17747](https://github.com/civicrm/civicrm-core/pull/17747))** -- **Slow down the frequency of WordPress "heartbeat" calls in CiviCRM admin ([214](https://github.com/civicrm/civicrm-wordpress/pull/214))** +- **Remove unused "ufUniqID" session variable + ([213](https://github.com/civicrm/civicrm-wordpress/pull/213))** -- **Remove unused "ufUniqID" session variable ([213](https://github.com/civicrm/civicrm-wordpress/pull/213))** +- **Remove Net packages Net_Curl Net_DIME as they do not appear to be used + ([294](https://github.com/civicrm/civicrm-packages/pull/294))** -- **5.28 to master ([212](https://github.com/civicrm/civicrm-wordpress/pull/212))** +- **Update contributor key for Andrew + ([18230](https://github.com/civicrm/civicrm-core/pull/18230))** -- **Installation - Support "activate first" w/setup UI ([121](https://github.com/civicrm/civicrm-backdrop/pull/121))** +- **Fix qill typo + ([18041](https://github.com/civicrm/civicrm-core/pull/18041))** -- **[NFC] Update versions file to remove reference to Mail_mime and Mail ([301](https://github.com/civicrm/civicrm-packages/pull/301))** +- **Update flexmailer release information + ([17912](https://github.com/civicrm/civicrm-core/pull/17912))** -- **[REF] Remove mail_mime package as now supplied by composer ([300](https://github.com/civicrm/civicrm-packages/pull/300))** +- **Remove invalid use of crmMoney formatter + ([18031](https://github.com/civicrm/civicrm-core/pull/18031))** -- **Remove Net packages Net_Curl Net_DIME as they do not appear to be used ([294](https://github.com/civicrm/civicrm-packages/pull/294))** +- **Remove main PaymentExpress class + ([18010](https://github.com/civicrm/civicrm-core/pull/18010))** -- **[REF] Remove some deprecated size function calls replaced with length ([299](https://github.com/civicrm/civicrm-packages/pull/299))** +- **Remove unused parameter ids['billing'] + ([18021](https://github.com/civicrm/civicrm-core/pull/18021))** -## Miscellany +- **Cache loader - remove legacy handling, handle null result from setting + ([17999](https://github.com/civicrm/civicrm-core/pull/17999))** ## Credits This release was developed by the following code authors: -AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman; Christian Wach; Circle Interactive - Pradeep Nayak; CiviCoop - Jaap Jansma, Klaas Eikelboom; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; Coop SymbioTIC - Mathieu Lutfy, Samuel Vanhove; Dave D; dependabot[bot]; Fuzion - Jitendra Purohit; iXiam - César Ramos; JMA Consulting - Monish Deb, Seamus Lee; John Kingsnorth; Joinery - Allen Shaw; Lighthouse Consulting and Design - Brian Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MillerTech - Chamil Wijesooriya; MJCO - Mikey O'Toole; MJW Consulting - Matthew Wire; muniodiego; Progressive Technology Project - Jamie McClelland; Squiffle Consulting - Aidan Saunders; Stephen Palmstrom; Tadpole Collective - Kevin Cristiano; Timbsoft Technologies - Tunbola Ogunwande; Wikimedia Foundation - Eileen McNaughton +AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman; +Christian Wach; Circle Interactive - Pradeep Nayak; CiviCoop - Jaap Jansma, +Klaas Eikelboom; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; +Coop SymbioTIC - Mathieu Lutfy, Samuel Vanhove; Dave D; Fuzion - Jitendra +Purohit; iXiam - César Ramos, Diego Muñio; JMA Consulting - Monish Deb, Seamus +Lee; John Kingsnorth; Joinery - Allen Shaw; Lighthouse Consulting and Design - +Brian Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MillerTech - +Chamil Wijesooriya; MJCO - Mikey O'Toole; MJW Consulting - Matthew Wire; +Progressive Technology Project - Jamie McClelland; Squiffle Consulting - Aidan +Saunders; Stephen Palmstrom; Tadpole Collective - Kevin Cristiano; Timbsoft +Technologies - Tunbola Ogunwande; Wikimedia Foundation - Eileen McNaughton Most authors also reviewed code for this release; in addition, the following reviewers contributed their comments: -AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman, Pengyi Zhang; Andrew Thompson; Artful Robot - Rich Lott; Australian Greens - Andrew Cormick-Dockery, John Twyman; Bastien Ho; Blackfly Solutions - Alan Dixon; Christian Wach; Circle Interactive - Pradeep Nayak; civibot[bot]; CiviCoop - Jaap Jansma, Klaas Eikelboom; civicrm-builder; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; CompuCorp - Jamie Novick; Coop SymbioTIC - Mathieu Lutfy, Samuel Vanhove; Dave D; DevApp - Adam Kwiatkowski; elcapo; Francesc Bassas i Bullich; Freeform Solutions - Herb van den Dool; Fuzion - Jitendra Purohit; Greenpeace Central and Eastern Europe - Patrick Figel; irenemeisel; iXiam - César Ramos; JMA Consulting - Joe Murray, Monish Deb, Seamus Lee; Joinery - Allen Shaw; Korlon - Stuart Gaston; Lighthouse Consulting and Design - Brian Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MillerTech - Chamil Wijesooriya; MJCO - Mikey O'Toole; MJW Consulting - Matthew Wire; Nicol Wistreich; Progressive Technology Project - Jamie McClelland; Rar9; Ray Wright; Semper IT - Karin Gerritsen; Squiffle Consulting - Aidan Saunders; Stephen Palmstrom; Tadpole Collective - Kevin Cristiano; Third Sector Design - Michael McAndrew; Wikimedia Foundation - Eileen McNaughton +Pengyi Zhang; Andrew Thompson; Artful Robot - Rich Lott; Australian Greens - +Andrew Cormick-Dockery, John Twyman; Bastien Ho; Blackfly Solutions - Alan +Dixon; Carlos Capote; CompuCorp - Jamie Novick; DevApp - Adam Kwiatkowski; +Francesc Bassas i Bullich; Freeform Solutions - Herb van den Dool; Greenpeace +Central and Eastern Europe - Patrick Figel; Irene Meisel; JMA Consulting - Joe +Murray; Korlon - Stuart Gaston; Nicol Wistreich; Rar9; Ray Wright; Semper IT - +Karin Gerritsen; Third Sector Design - Michael McAndrew; ## Feedback From f8220fdffdd1601df6eea273f39f5d730acf1ace Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Tue, 1 Sep 2020 14:15:29 -0400 Subject: [PATCH 081/834] commenting and sorting --- release-notes/5.29.0.md | 167 ++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 41 deletions(-) diff --git a/release-notes/5.29.0.md b/release-notes/5.29.0.md index 5d2a66308d8a..a2cea1e2c800 100644 --- a/release-notes/5.29.0.md +++ b/release-notes/5.29.0.md @@ -23,58 +23,134 @@ Released September 2, 2020 ## Features +### CiviMember + +- **Option to update expired memberships as part of the job.process_membership + ([dev/membership#18](https://lab.civicrm.org/dev/membership/-/issues/18): + [16298](https://github.com/civicrm/civicrm-core/pull/16298))** + + Improves the Job.process_membership API/Scheduled job by adding some new + parameters, specifically: + + - exclude_test_memberships: Exclude test memberships from calculations + (default = TRUE) + - only_active_membership_types: Exclude disabled membership types from + calculations (default = TRUE) + - exclude_membership_status_ids: Default: Exclude Pending, Cancelled, + Expired. + + Deceased will always be excluded + +### Backdrop Integration + +- **Support for installing CiviCRM-Backdrop via "setup" UI + ([17749](https://github.com/civicrm/civicrm-core/pull/17749) and + [121](https://github.com/civicrm/civicrm-backdrop/pull/121))** + + Improves the installation and Setup process for Backdrop integration's so that + one enables the CiviCRM module like any other module and then is directed to + the setup screen to complete installation as opposed to having to navigate to + a specific link. + ### Core CiviCRM -- **crm- Missing Summary ([17809](https://github.com/civicrm/civicrm-core/pull/17809))** +- **Icon in status message after saving a civireport is misleading + ([dev/report#43](https://lab.civicrm.org/dev/report/-/issues/43): + [17863](https://github.com/civicrm/civicrm-core/pull/17863))** + + Improves icons in Status Messages for reports. -- **CRM- Missing Summary ([17749](https://github.com/civicrm/civicrm-core/pull/17749))** +- **Remove ORDER BY and GROUP BY from alphabetQuery to improve performance + ([16877](https://github.com/civicrm/civicrm-core/pull/16877))** ## Bugs resolved -### CiviCase +### Wordpress Integration -- **Use case id to get relationship for activity creation ([17256](https://github.com/civicrm/civicrm-core/pull/17256) and [17764](https://github.com/civicrm/civicrm-core/pull/17764))** +- **[civicrm.files] token doesn't work in some cases since 5.27 + ([dev/wordpress#66](https://lab.civicrm.org/dev/wordpress/-/issues/66): + [18068](https://github.com/civicrm/civicrm-core/pull/18068))** -### Core CiviCRM + Ensures the [civicrm.files] token plays nice for sites with older wordpress + file directory set ups. -- **reporting#21 - don't multiple contribution details when a 1-to-many r… ([15435](https://github.com/civicrm/civicrm-core/pull/15435))** +### Drupal Integration -- **Contribution Summary Report: Taking the currency filtered in the "general total" row. Implements dev/report#27 ([16736](https://github.com/civicrm/civicrm-core/pull/16736))** +- **Drupal 8 - Using Create User Record action on a contact with no email is too + quiet. Also CRM_Core_Session::setStatus is sometimes ignored. + ([dev/drupal#127](https://lab.civicrm.org/dev/drupal/-/issues/127): + [17914](https://github.com/civicrm/civicrm-core/pull/17914) and + [17915](https://github.com/civicrm/civicrm-core/pull/17915))** -- **dev/report#43 - Icon after saving a civireport instance is misleading ([17863](https://github.com/civicrm/civicrm-core/pull/17863))** + Require email when adding a user and ensure that errors show. +### CiviCase +- **Fix case activity field set to allow long details to be exported + ([17970](https://github.com/civicrm/civicrm-core/pull/17970))** -- **dev/membership#18 Enhance parameters for Job.process_membership ([16298](https://github.com/civicrm/civicrm-core/pull/16298))** +### CiviContribute +- **Contribution Details Statistics are multiplied under many circumstances + ([dev/report#21](https://lab.civicrm.org/dev/report/-/issues/21): + [15435](https://github.com/civicrm/civicrm-core/pull/15435) and + [17809](https://github.com/civicrm/civicrm-core/pull/17809))** -- **mailing#70 Don't create users for test mail if user doesn't have permission ([17867](https://github.com/civicrm/civicrm-core/pull/17867))** +- **Contribution Summary Report: The "general total" row does not take the + currency filtered + ([dev/report#27](https://lab.civicrm.org/dev/report/-/issues/27): + [16736](https://github.com/civicrm/civicrm-core/pull/16736))** + When the contribution summary report is used by filtering for a currency other + than the site's default currency, the "grand total" row shows the sign of the + default currency instead of the filtered currency. +- **Define the logic that sets (or not) contribution receive_date in relation to + payments + ([dev/financial#139](https://lab.civicrm.org/dev/financial/-/issues/139): + [17777](https://github.com/civicrm/civicrm-core/pull/17777))** -- **dev/drupal#114 and dev/core#1647 - Remove resource url status check ([17754](https://github.com/civicrm/civicrm-core/pull/17754))** + Ensures that the contribution receive_date does not change when payments come + in. -- **dev/drupal#127 - require email for the Useradd task and errors not showing ([17915](https://github.com/civicrm/civicrm-core/pull/17915))** +- **Fix PaypalIPN single function to not receive-by-reference + ([18044](https://github.com/civicrm/civicrm-core/pull/18044))** -- **dev/drupal#127 - CRM_Core_Session::setStatus() gets ignored sometimes ([17914](https://github.com/civicrm/civicrm-core/pull/17914))** +### CiviEvent +- **Can't meaningfully disable self-service transfer/cancellation once enabled + ([dev/event#35](https://lab.civicrm.org/dev/event/-/issues/35): + [18040](https://github.com/civicrm/civicrm-core/pull/18040))** -- **dev/financial#135 Remove stub function from payflowPro ([18078](https://github.com/civicrm/civicrm-core/pull/18078))** +- **Event registration form has inconsistent labeling + ([dev/event#38](https://lab.civicrm.org/dev/event/-/issues/38): + [17695](https://github.com/civicrm/civicrm-core/pull/17695))** -- **dev/financial#135 Remove unreachable doDirectPayment from manual processor ([18072](https://github.com/civicrm/civicrm-core/pull/18072))** +- **Event cart hidden/enabled in buildkit 5.29 RC + ([dev/event#40](https://lab.civicrm.org/dev/event/-/issues/40): + [18101](https://github.com/civicrm/civicrm-core/pull/18101))** -- **dev/financial#139 contribution receive_date shouldn't change when payments come in ([17777](https://github.com/civicrm/civicrm-core/pull/17777))** + Ensures that the Event Cart functionality defaults to off. +### CiviMail -- **event#35: move statusBounce out of BAO layer; don't allow self-service when dis… ([18040](https://github.com/civicrm/civicrm-core/pull/18040))** +- **Test mailings create new contacts even when "Add Contacts" permission is not + present. ([dev/mail#70](https://lab.civicrm.org/dev/mail/-/issues/70): + [17867](https://github.com/civicrm/civicrm-core/pull/17867))** -- **event#38 fix wording on event reg page ([17695](https://github.com/civicrm/civicrm-core/pull/17695))** + Ensures contacts are not created when a user without permissions to create + contacts sends a test email. -- **dev/event#40 - EventCart - Check legacy setting until extension is public ([18101](https://github.com/civicrm/civicrm-core/pull/18101))** +### Core CiviCRM +- **Installing drupal 8 using civicrm-setup leads to "incorrect resource url" + system status check errors + ([dev/drupal#114](https://lab.civicrm.org/dev/drupal/-/issues/114) and + [dev/core#1647](https://lab.civicrm.org/dev/core/-/issues/1647): + [17754](https://github.com/civicrm/civicrm-core/pull/17754))** + Removes the resource url status check. -- **dev/wordpress#66 Re-instate newer variables but with more support for… ([18068](https://github.com/civicrm/civicrm-core/pull/18068))** - **dev/core#183 Use TempTable builder to generate table for import ([17827](https://github.com/civicrm/civicrm-core/pull/17827))** @@ -121,6 +197,8 @@ Released September 2, 2020 - **dev/core#1827 activity search - fixing search by tags ([17655](https://github.com/civicrm/civicrm-core/pull/17655) and [17755](https://github.com/civicrm/civicrm-core/pull/17755))** +- **dev/core#1843 Remove url-tracking in mass sms. ([17700](https://github.com/civicrm/civicrm-core/pull/17700))** + - **dev/core#1853 - Fix validation errors when removing contact subtype ([17765](https://github.com/civicrm/civicrm-core/pull/17765))** - **dev/core#1855 - Allow different output formats for CiviReport results and untangle code ([17901](https://github.com/civicrm/civicrm-core/pull/17901))** @@ -193,43 +271,38 @@ Released September 2, 2020 - **dev/core#1972 Fix tax_amount calclation on renewal form ([18271](https://github.com/civicrm/civicrm-core/pull/18271))** +- **Fix obscure dedupe scenario where 'bad' location data can overwrite good + data ([17993](https://github.com/civicrm/civicrm-core/pull/17993))** + +- **Fix JQuery Validation for radios + ([17937](https://github.com/civicrm/civicrm-core/pull/17937))** + +- **Fix buggy placement of icons on buttons + ([18005](https://github.com/civicrm/civicrm-core/pull/18005))** + + + -- **Revert "Swap out button/submit inputs for button elements" ([18185](https://github.com/civicrm/civicrm-core/pull/18185))** -- **Swap out button/submit inputs for button elements ([18091](https://github.com/civicrm/civicrm-core/pull/18091))** - **Installation doclinks not getting url-rewritten ([18175](https://github.com/civicrm/civicrm-core/pull/18175))** - **Fix button name on updated form ([18000](https://github.com/civicrm/civicrm-core/pull/18000))** -- **Refactor "applyLocale" and remove references to "language" column in UFMatch table ([18049](https://github.com/civicrm/civicrm-core/pull/18049))** - - **Show cron warning on Scheduled Jobs admin page ([18065](https://github.com/civicrm/civicrm-core/pull/18065))** - **Use correct pdf package to generate pdf file on invoice download/email activity ([18056](https://github.com/civicrm/civicrm-core/pull/18056))** -- **Fix buggy placement of icons on buttons ([18005](https://github.com/civicrm/civicrm-core/pull/18005))** - - **CRM_Utils_Hook: deprecation warning and short array syntax ([17995](https://github.com/civicrm/civicrm-core/pull/17995))** - **Why not make the buttons flat? ([18054](https://github.com/civicrm/civicrm-core/pull/18054))** -- **Fix obscure dedupe scenario where 'bad' location data can overwrite good data ([17993](https://github.com/civicrm/civicrm-core/pull/17993))** - -- **Remove ORDER BY and GROUP BY from alphabetQuery to improve performance ([16877](https://github.com/civicrm/civicrm-core/pull/16877))** - -- **Fix JQuery Validation for radios ([17937](https://github.com/civicrm/civicrm-core/pull/17937))** - -- **Fix PaypalIPN single function to not receive-by-reference ([18044](https://github.com/civicrm/civicrm-core/pull/18044))** - - **Remove requirement to pass 'contribution_status_id' => Pending from order.create ([18018](https://github.com/civicrm/civicrm-core/pull/18018))** - **Use saved contribution's line items rather than the primaryContributionID ([18033](https://github.com/civicrm/civicrm-core/pull/18033))** - **Wrap multi record custom field inside a div ([17966](https://github.com/civicrm/civicrm-core/pull/17966))** -- **Remove url-tracking in mass sms. dev/core#1843 ([17700](https://github.com/civicrm/civicrm-core/pull/17700))** - - **Do not overwrite values saved from the repeatContribution routine ([17972](https://github.com/civicrm/civicrm-core/pull/17972))** - **SystemCheck: add ability to efficiently run only specified checks ([17824](https://github.com/civicrm/civicrm-core/pull/17824))** @@ -242,12 +315,8 @@ Released September 2, 2020 - **Wrong link to admin page in error message about FROM address on PCP page ([17996](https://github.com/civicrm/civicrm-core/pull/17996))** -- **CRM_Core_BAO_Cache - Remove functions deprecated a year ago ([17989](https://github.com/civicrm/civicrm-core/pull/17989))** - - **Fix repeattransaction api to use custom data from the template contribution ([17975](https://github.com/civicrm/civicrm-core/pull/17975))** -- **Fix case activity field set to allow long details to be exported ([17970](https://github.com/civicrm/civicrm-core/pull/17970))** - - **Fixed filling default values for tagssets in the advanced search form ([17978](https://github.com/civicrm/civicrm-core/pull/17978))** - **Remove duplicate cache flush ([17988](https://github.com/civicrm/civicrm-core/pull/17988))** @@ -400,11 +469,19 @@ Released September 2, 2020 - **Slow down the frequency of WordPress "heartbeat" calls in CiviCRM admin ([214](https://github.com/civicrm/civicrm-wordpress/pull/214))** -- **Installation - Support "activate first" w/setup UI ([121](https://github.com/civicrm/civicrm-backdrop/pull/121))** - **Installation - Support "activate first" w/setup UI ([606](https://github.com/civicrm/civicrm-drupal/pull/606))** ## Miscellany +- **CRM_Core_BAO_Cache - Remove functions deprecated a year ago + ([17989](https://github.com/civicrm/civicrm-core/pull/17989))** + +- **Fix all core processors to implement doPayment and not doTransferPayment or + doDirectPayment + ([https://lab.civicrm.org/dev/financial/-/issues/135](https://lab.civicrm.org/dev/financial/-/issues/135): + [18078](https://github.com/civicrm/civicrm-core/pull/18078) and + [18072](https://github.com/civicrm/civicrm-core/pull/18072))** + - **[REF] Extract setUserContext on contribution form & cleanup on backend add membership form ([17968](https://github.com/civicrm/civicrm-core/pull/17968))** @@ -565,6 +642,9 @@ Released September 2, 2020 - **[REF] Remove some deprecated size function calls replaced with length ([299](https://github.com/civicrm/civicrm-packages/pull/299))** +- **Refactor "applyLocale" and remove references to "language" column in UFMatch + table ([18049](https://github.com/civicrm/civicrm-core/pull/18049))** + - **[NFC] Fix provider unit test on PHP7.4 ([18073](https://github.com/civicrm/civicrm-core/pull/18073))** @@ -676,6 +756,11 @@ Released September 2, 2020 - **Cache loader - remove legacy handling, handle null result from setting ([17999](https://github.com/civicrm/civicrm-core/pull/17999))** + +- **Use case id to get relationship for activity creation + ([17256](https://github.com/civicrm/civicrm-core/pull/17256) and + [17764](https://github.com/civicrm/civicrm-core/pull/17764))** + ## Credits This release was developed by the following code authors: From 0cce86621f9bc25007b9cfd14e8257b9d3f3fff7 Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Tue, 1 Sep 2020 17:02:10 -0400 Subject: [PATCH 082/834] sorting and annotating --- release-notes/5.29.0.md | 285 ++++++++++++++++++++++++---------------- 1 file changed, 170 insertions(+), 115 deletions(-) diff --git a/release-notes/5.29.0.md b/release-notes/5.29.0.md index a2cea1e2c800..1be7e9ef6661 100644 --- a/release-notes/5.29.0.md +++ b/release-notes/5.29.0.md @@ -54,6 +54,13 @@ Released September 2, 2020 ### Core CiviCRM +- **Contact export is very slow, creates huge temporary tables + ([dev/core#1725](https://lab.civicrm.org/dev/core/-/issues/1725): + [17458](https://github.com/civicrm/civicrm-core/pull/17458))** + + Improves performance when exporting contacts by only exporting primary address + fields. + - **Icon in status message after saving a civireport is misleading ([dev/report#43](https://lab.civicrm.org/dev/report/-/issues/43): [17863](https://github.com/civicrm/civicrm-core/pull/17863))** @@ -63,6 +70,19 @@ Released September 2, 2020 - **Remove ORDER BY and GROUP BY from alphabetQuery to improve performance ([16877](https://github.com/civicrm/civicrm-core/pull/16877))** +- **Differentiate smart group from regular group using icon in select2 field + ([dev/core#785](https://lab.civicrm.org/dev/core/-/issues/785): + [17927](https://github.com/civicrm/civicrm-core/pull/17927) and + [13958](https://github.com/civicrm/civicrm-core/pull/13958))** + + Improves Group's select2 by adding icon next to smart groups. + +- **Feature: Ability to enable SSL for database connection. + ([dev/core#1137](https://lab.civicrm.org/dev/core/-/issues/1137): + [17706](https://github.com/civicrm/civicrm-core/pull/17706))** + + Makes it so sites can use ssl to connect to mysql. + ## Bugs resolved ### Wordpress Integration @@ -74,6 +94,17 @@ Released September 2, 2020 Ensures the [civicrm.files] token plays nice for sites with older wordpress file directory set ups. +- **CiviCRM and the WordPress Pods plugin (since version 2.7.13) is incompatible + due to Pods including marionette v3.3.1 for backbone, newer than CiviCRM's + bundled marionette v1.0.3 + ([dev/core#1090](https://lab.civicrm.org/dev/core/-/issues/1090): + [17855](https://github.com/civicrm/civicrm-core/pull/17855))** + + Ensures CiviCRM plays nicely with recent versions of Elementor Plugin. + +- **Fix PHP notice on wordpress permissions form + ([17758](https://github.com/civicrm/civicrm-core/pull/17758))** + ### Drupal Integration - **Drupal 8 - Using Create User Record action on a contact with no email is too @@ -84,6 +115,13 @@ Released September 2, 2020 Require email when adding a user and ensure that errors show. +- **The following PHP variables are not set: $_SERVER[HTTP_HOST] + ([dev/core#750](https://lab.civicrm.org/dev/core/-/issues/750): + [17636](https://github.com/civicrm/civicrm-core/pull/17636))** + + Skip the server variable checks if running in a CLI environment, removing an + error when running Drush commands against Drupal 8 and Drupal 9 based sites. + ### CiviCase - **Fix case activity field set to allow long details to be exported @@ -91,6 +129,11 @@ Released September 2, 2020 ### CiviContribute +- **Paypal IPN sometimes fails to update the next scheduled payment date when + recording the latest recurring payment + ([dev/core#1679](https://lab.civicrm.org/dev/core/-/issues/1679): + [17744](https://github.com/civicrm/civicrm-core/pull/17744))** + - **Contribution Details Statistics are multiplied under many circumstances ([dev/report#21](https://lab.civicrm.org/dev/report/-/issues/21): [15435](https://github.com/civicrm/civicrm-core/pull/15435) and @@ -116,6 +159,23 @@ Released September 2, 2020 - **Fix PaypalIPN single function to not receive-by-reference ([18044](https://github.com/civicrm/civicrm-core/pull/18044))** +- **On behalf label / Honoree Title / Honoree Description not translatable on + contribution page + ([dev/core#1280](https://lab.civicrm.org/dev/core/-/issues/1280): + [16838](https://github.com/civicrm/civicrm-core/pull/16838))** + +- **Load contribution page if live payment processor is disabled but test is + available ([17828](https://github.com/civicrm/civicrm-core/pull/17828))** + +- **Remove requirement to pass 'contribution_status_id' => Pending from + order.create ([18018](https://github.com/civicrm/civicrm-core/pull/18018))** + +- **Use saved contribution's line items rather than the primaryContributionID + ([18033](https://github.com/civicrm/civicrm-core/pull/18033))** + +- **Do not overwrite values saved from the repeatContribution routine + ([17972](https://github.com/civicrm/civicrm-core/pull/17972))** + ### CiviEvent - **Can't meaningfully disable self-service transfer/cancellation once enabled @@ -141,6 +201,12 @@ Released September 2, 2020 Ensures contacts are not created when a user without permissions to create contacts sends a test email. +### CiviMember + +- **Decimal Separator - Invalid value "total_amount" (NaN,N) creating or editing + a membership ([dev/core#1113](https://lab.civicrm.org/dev/core/-/issues/1113): + [16429](https://github.com/civicrm/civicrm-core/pull/16429))** + ### Core CiviCRM - **Installing drupal 8 using civicrm-setup leads to "incorrect resource url" @@ -151,33 +217,19 @@ Released September 2, 2020 Removes the resource url status check. +- **API4: 500 error on chain with custom field + ([dev/core#1578](https://lab.civicrm.org/dev/core/-/issues/1578): + [17866](https://github.com/civicrm/civicrm-core/pull/17866))** +- **MySQL uses filesort index when building the query which can cause + performance issues + ([dev/core#1665](https://lab.civicrm.org/dev/core/-/issues/1665): + [18052](https://github.com/civicrm/civicrm-core/pull/18052))** -- **dev/core#183 Use TempTable builder to generate table for import ([17827](https://github.com/civicrm/civicrm-core/pull/17827))** - -- **[dev/core#750] Don't check server variables if we're running in CLI ([17636](https://github.com/civicrm/civicrm-core/pull/17636))** - -- **dev/core#785 Differentiate smart group from regular group using icon in select2 field ([17927](https://github.com/civicrm/civicrm-core/pull/17927))** - -- **dev/core#785 Differentiate smart group from regular group using icon in select2 field ([13958](https://github.com/civicrm/civicrm-core/pull/13958))** - -- **dev/core#1090 Update extendedSerializeData to use the Backbone namesp… ([17855](https://github.com/civicrm/civicrm-core/pull/17855))** +- **Custom fields not copied on shared address + ([dev/core#1670](https://lab.civicrm.org/dev/core/-/issues/1670): + [17580](https://github.com/civicrm/civicrm-core/pull/17580))** -- **dev/core#1113 - Decimal Separator - Invalid value "total_amount" (NaN,N) creating or editing a membership ([16429](https://github.com/civicrm/civicrm-core/pull/16429))** - -- **dev/core#1137 - Allow ssl connection to mysql by specifying in DSN ([17706](https://github.com/civicrm/civicrm-core/pull/17706))** - -- **dev/core#1280 Fix ContributionPage soft_credit translation ([16838](https://github.com/civicrm/civicrm-core/pull/16838))** - -- **dev/core#1578 - Fix APIv4 chaining with custom fields ([17866](https://github.com/civicrm/civicrm-core/pull/17866))** - -- **dev/core#1665 Remove the having clause as well as having needs a group by ([18052](https://github.com/civicrm/civicrm-core/pull/18052))** - -- **dev/core#1670 copy custom fields from master to shared address ([17580](https://github.com/civicrm/civicrm-core/pull/17580))** - -- **dev/core#1679: Ensure Paypal IPN always updates the next scheduled payment date ([17744](https://github.com/civicrm/civicrm-core/pull/17744))** - -- **dev/core#1725 Only export primary address fields ([17458](https://github.com/civicrm/civicrm-core/pull/17458))** - **dev/core#1751: [Create Email] Only Show Update/Save Template when User has Permission to Edit Templates ([17480](https://github.com/civicrm/civicrm-core/pull/17480))** @@ -231,23 +283,11 @@ Released September 2, 2020 - **dev/core#1902: "Contribution Source" profile field has no effect ([17930](https://github.com/civicrm/civicrm-core/pull/17930))** -- **dev/core#1905 force backend links for new "configure" buttons ([18088](https://github.com/civicrm/civicrm-core/pull/18088))** - -- **dev/core#1905 rework #17942 with simpler ts strings ([18064](https://github.com/civicrm/civicrm-core/pull/18064))** - -- **dev/core#1905 Add configure icons on public pages ([17942](https://github.com/civicrm/civicrm-core/pull/17942))** +- **dev/core#1905 force backend links for new "configure" buttons ([17942](https://github.com/civicrm/civicrm-core/pull/17942), [18088](https://github.com/civicrm/civicrm-core/pull/18088) and [18064](https://github.com/civicrm/civicrm-core/pull/18064))** - **dev/core#1906 - Allow payment create api to record payment on Failed … ([17943](https://github.com/civicrm/civicrm-core/pull/17943))** -- **dev/core#1909 Fix E-notice when adding a field on a profile ([17962](https://github.com/civicrm/civicrm-core/pull/17962))** - -- **dev/core#1909 Fix e-notice when adding a payment processor ([17964](https://github.com/civicrm/civicrm-core/pull/17964))** - -- **dev/core#1909 - E_NOTICE opening file-on-case ([17959](https://github.com/civicrm/civicrm-core/pull/17959))** - -- **dev/core#1909 - E_NOTICE on contribution edit ([18006](https://github.com/civicrm/civicrm-core/pull/18006))** - -- **dev/core#1909 - Avoid E_Notice on SMS provider form when no default url ([17985](https://github.com/civicrm/civicrm-core/pull/17985))** +- **dev/core#1909 Fix E-notice when adding a field on a profile ([18006](https://github.com/civicrm/civicrm-core/pull/18006), [17962](https://github.com/civicrm/civicrm-core/pull/17962), [17964](https://github.com/civicrm/civicrm-core/pull/17964), [17959](https://github.com/civicrm/civicrm-core/pull/17959), [17985](https://github.com/civicrm/civicrm-core/pull/17985))** - **dev/core#1913 Allow for schemas to be added by extensions if they are… ([17986](https://github.com/civicrm/civicrm-core/pull/17986))** @@ -271,17 +311,6 @@ Released September 2, 2020 - **dev/core#1972 Fix tax_amount calclation on renewal form ([18271](https://github.com/civicrm/civicrm-core/pull/18271))** -- **Fix obscure dedupe scenario where 'bad' location data can overwrite good - data ([17993](https://github.com/civicrm/civicrm-core/pull/17993))** - -- **Fix JQuery Validation for radios - ([17937](https://github.com/civicrm/civicrm-core/pull/17937))** - -- **Fix buggy placement of icons on buttons - ([18005](https://github.com/civicrm/civicrm-core/pull/18005))** - - - @@ -293,34 +322,18 @@ Released September 2, 2020 - **Use correct pdf package to generate pdf file on invoice download/email activity ([18056](https://github.com/civicrm/civicrm-core/pull/18056))** -- **CRM_Utils_Hook: deprecation warning and short array syntax ([17995](https://github.com/civicrm/civicrm-core/pull/17995))** - - **Why not make the buttons flat? ([18054](https://github.com/civicrm/civicrm-core/pull/18054))** -- **Remove requirement to pass 'contribution_status_id' => Pending from order.create ([18018](https://github.com/civicrm/civicrm-core/pull/18018))** - -- **Use saved contribution's line items rather than the primaryContributionID ([18033](https://github.com/civicrm/civicrm-core/pull/18033))** - - **Wrap multi record custom field inside a div ([17966](https://github.com/civicrm/civicrm-core/pull/17966))** -- **Do not overwrite values saved from the repeatContribution routine ([17972](https://github.com/civicrm/civicrm-core/pull/17972))** - - **SystemCheck: add ability to efficiently run only specified checks ([17824](https://github.com/civicrm/civicrm-core/pull/17824))** - **Change inform-icon to fa-info-circle ([18001](https://github.com/civicrm/civicrm-core/pull/18001))** -- **CIVICRM_BAO_CACHE_ADAPTER - Remove obsolete option ([17990](https://github.com/civicrm/civicrm-core/pull/17990))** - -- **SQL temp table not using utf8mb4 if server default already set to utf8mb4 ([18012](https://github.com/civicrm/civicrm-core/pull/18012))** - -- **Wrong link to admin page in error message about FROM address on PCP page ([17996](https://github.com/civicrm/civicrm-core/pull/17996))** - - **Fix repeattransaction api to use custom data from the template contribution ([17975](https://github.com/civicrm/civicrm-core/pull/17975))** - **Fixed filling default values for tagssets in the advanced search form ([17978](https://github.com/civicrm/civicrm-core/pull/17978))** -- **Remove duplicate cache flush ([17988](https://github.com/civicrm/civicrm-core/pull/17988))** - - **Simplify caching of status checks ([17817](https://github.com/civicrm/civicrm-core/pull/17817))** - **ensure custom field checkboxes are populated in profiles ([17977](https://github.com/civicrm/civicrm-core/pull/17977))** @@ -331,22 +344,14 @@ Released September 2, 2020 - **CRM_Utils_Check_Component_Case - Guard against post-upgrade crash ([17944](https://github.com/civicrm/civicrm-core/pull/17944))** -- **Remove extraneous opportunistic cache flush. ([17936](https://github.com/civicrm/civicrm-core/pull/17936))** - - **Improve caching of current domain ([17916](https://github.com/civicrm/civicrm-core/pull/17916))** - **Setup UI - Validate that at least one "Component" is enabled ([17778](https://github.com/civicrm/civicrm-core/pull/17778))** - **Member detail report: nest "in" options in parentheses ([17911](https://github.com/civicrm/civicrm-core/pull/17911))** -- **Fix sticky table header on "Find Activities" page ([17917](https://github.com/civicrm/civicrm-core/pull/17917))** - -- **Remove unused "ufUniqID" session variable ([17904](https://github.com/civicrm/civicrm-core/pull/17904))** - - **Replace a load of references to the wiki with docs links ([17900](https://github.com/civicrm/civicrm-core/pull/17900))** -- **Remove check for valid email in synchronizeUFMatch ([17771](https://github.com/civicrm/civicrm-core/pull/17771))** - - **Call apiv4 from Contribution create rather than fugly addActivity function ([17881](https://github.com/civicrm/civicrm-core/pull/17881))** - **APIv4 - Add BasicEntity helper class ([17899](https://github.com/civicrm/civicrm-core/pull/17899))** @@ -365,7 +370,11 @@ Released September 2, 2020 - **EventCart ext: Fix autogenerated code, remove unused hooks, update readme ([17884](https://github.com/civicrm/civicrm-core/pull/17884))** -- **Load contribution page if live payment processor is disabled but test is available ([17828](https://github.com/civicrm/civicrm-core/pull/17828))** +- **Add eventcart shell ([17741](https://github.com/civicrm/civicrm-core/pull/17741))** + +- **EventCart - Resolve BAO identity and uncommitted DAO changes ([17861](https://github.com/civicrm/civicrm-core/pull/17861))** + +- **Move BAO and template files into event cart ([17743](https://github.com/civicrm/civicrm-core/pull/17743))** - **Search debug ([17887](https://github.com/civicrm/civicrm-core/pull/17887))** @@ -373,42 +382,19 @@ Released September 2, 2020 - **Simplify flushing group contact cache query to reduce table locking and improve performance ([17846](https://github.com/civicrm/civicrm-core/pull/17846))** - - - **Disable frequency/interval fields if not required. Mark required if they are so they are validated before submit ([17526](https://github.com/civicrm/civicrm-core/pull/17526))** - **Fix currency symbol for Total Amount on contribution page ([17703](https://github.com/civicrm/civicrm-core/pull/17703))** - **RelationshipCache - Add a high-level index to facilitate relationship queries (more fields) ([17781](https://github.com/civicrm/civicrm-core/pull/17781))** -- **Hooks/Dispatcher - Close loopholes that occur around "preboot" hooks ([17831](https://github.com/civicrm/civicrm-core/pull/17831))** - -- **APIv4 - Add shorthand for setCheckPermissions() ([17834](https://github.com/civicrm/civicrm-core/pull/17834))** - -- **Use PrematureExit exception instead of weird hack in tests ([17870](https://github.com/civicrm/civicrm-core/pull/17870))** - -- **Remove unnecessary try/catch per #17729 ([17823](https://github.com/civicrm/civicrm-core/pull/17823))** - -- **Fixed DB Error: syntax error if line item refers to civicrm_case ([16626](https://github.com/civicrm/civicrm-core/pull/16626))** - -- **Fix potential js error on summary screen when reloading blocks ([17865](https://github.com/civicrm/civicrm-core/pull/17865))** - - **Search Ext: fix loading options and parsing custom field names ([17864](https://github.com/civicrm/civicrm-core/pull/17864))** -- **EventCart - Resolve BAO identity and uncommitted DAO changes ([17861](https://github.com/civicrm/civicrm-core/pull/17861))** - - **Bump lodash from 4.17.15 to 4.17.19 ([17858](https://github.com/civicrm/civicrm-core/pull/17858))** -- **handlePaymentNotification() should not be declared as a static method ([17849](https://github.com/civicrm/civicrm-core/pull/17849))** - -- **Move BAO and template files into event cart ([17743](https://github.com/civicrm/civicrm-core/pull/17743))** - **Update regen.sh with new & upcoming core extensions ([17839](https://github.com/civicrm/civicrm-core/pull/17839))** -- **MembershipRenewalTest - Fix failure ([17830](https://github.com/civicrm/civicrm-core/pull/17830))** - -- **Remove hard coded charset. ([17826](https://github.com/civicrm/civicrm-core/pull/17826))** - - **APIv4 - Add activity contacts to APIv4 field spec ([17766](https://github.com/civicrm/civicrm-core/pull/17766))** - **Adjust mysql SET NAMES in remaining places as we agreed this was the go ([17825](https://github.com/civicrm/civicrm-core/pull/17825))** @@ -421,10 +407,6 @@ Released September 2, 2020 - **CheckEnv - Give new installs a grace period before 'Cron Not Running' msg ([17800](https://github.com/civicrm/civicrm-core/pull/17800))** -- **Fix 'Undefined variable: jsSet in CRM_Core_BAO_Mapping::loadSavedMapping()' ([17816](https://github.com/civicrm/civicrm-core/pull/17816))** - - - - **Add hidden tag to search extension ([17789](https://github.com/civicrm/civicrm-core/pull/17789))** - **Sort permittedActivityTypes ([17794](https://github.com/civicrm/civicrm-core/pull/17794))** @@ -435,34 +417,20 @@ Released September 2, 2020 - **Add system check to ensure WP base page exists ([17698](https://github.com/civicrm/civicrm-core/pull/17698))** -- **Fixed notice error on Relationships report ([17787](https://github.com/civicrm/civicrm-core/pull/17787))** - -- **getLoggedInContactID() is a static function ([17783](https://github.com/civicrm/civicrm-core/pull/17783))** - - **Add search extension ([17775](https://github.com/civicrm/civicrm-core/pull/17775))** -- **Remove PaymentExpress ipn class ([17763](https://github.com/civicrm/civicrm-core/pull/17763))** - - **Status Checks - Use more specific label regarding "Domain"/"Organization" check ([17776](https://github.com/civicrm/civicrm-core/pull/17776))** - **Bump minimum upgradable version to 4.4.7 ([17750](https://github.com/civicrm/civicrm-core/pull/17750))** -- **Remove unused, deprecated functions ([17761](https://github.com/civicrm/civicrm-core/pull/17761))** - - **Improve efficiency of findFiles ([17745](https://github.com/civicrm/civicrm-core/pull/17745))** - **APIv4 Explorer: Improve selection of fields for HAVING ([17746](https://github.com/civicrm/civicrm-core/pull/17746))** -- **More unused functions in GenCode ([17756](https://github.com/civicrm/civicrm-core/pull/17756))** - -- **Fix PHP notice on wordpress permissions form ([17758](https://github.com/civicrm/civicrm-core/pull/17758))** - - **Convert CRM.utils.formatDate tests to karma ([17757](https://github.com/civicrm/civicrm-core/pull/17757))** - **Teach CRM.utils.formatDate to also show time ([17684](https://github.com/civicrm/civicrm-core/pull/17684))** -- **Add eventcart shell ([17741](https://github.com/civicrm/civicrm-core/pull/17741))** - - **Fixed for multi-select filter ([615](https://github.com/civicrm/civicrm-drupal/pull/615))** - **fix url for file field ([608](https://github.com/civicrm/civicrm-drupal/pull/608))** @@ -471,8 +439,95 @@ Released September 2, 2020 - **Installation - Support "activate first" w/setup UI ([606](https://github.com/civicrm/civicrm-drupal/pull/606))** + + +- **SQL temp table not using utf8mb4 if server default already set to utf8mb4 + ([18012](https://github.com/civicrm/civicrm-core/pull/18012))** + +- **Wrong link to admin page in error message about FROM address on PCP page + ([17996](https://github.com/civicrm/civicrm-core/pull/17996))** + +- **Fix sticky table header on "Find Activities" page + ([17917](https://github.com/civicrm/civicrm-core/pull/17917))** + +- **Fixed DB Error: syntax error if line item refers to civicrm_case + ([16626](https://github.com/civicrm/civicrm-core/pull/16626))** + +- **Fix potential js error on summary screen when reloading blocks + ([17865](https://github.com/civicrm/civicrm-core/pull/17865))** + +- **Fix 'Undefined variable: jsSet in CRM_Core_BAO_Mapping::loadSavedMapping()' + ([17816](https://github.com/civicrm/civicrm-core/pull/17816))** + +- **Fixed notice error on Relationships report + ([17787](https://github.com/civicrm/civicrm-core/pull/17787))** + +- **Fix obscure dedupe scenario where 'bad' location data can overwrite good + data ([17993](https://github.com/civicrm/civicrm-core/pull/17993))** + +- **Fix JQuery Validation for radios + ([17937](https://github.com/civicrm/civicrm-core/pull/17937))** + +- **Fix buggy placement of icons on buttons + ([18005](https://github.com/civicrm/civicrm-core/pull/18005))** + ## Miscellany +- **CRM_Utils_Hook: deprecation warning and short array syntax + ([17995](https://github.com/civicrm/civicrm-core/pull/17995))** + +- **Remove check for valid email in synchronizeUFMatch + ([17771](https://github.com/civicrm/civicrm-core/pull/17771))** + +- **Remove unused "ufUniqID" session variable + ([17904](https://github.com/civicrm/civicrm-core/pull/17904))** + +- **Remove duplicate cache flush + ([17988](https://github.com/civicrm/civicrm-core/pull/17988))** + +- **Remove extraneous opportunistic cache flush. + ([17936](https://github.com/civicrm/civicrm-core/pull/17936))** + +- **Hooks/Dispatcher - Close loopholes that occur around "preboot" hooks + ([17831](https://github.com/civicrm/civicrm-core/pull/17831))** + +- **APIv4 - Add shorthand for setCheckPermissions() + ([17834](https://github.com/civicrm/civicrm-core/pull/17834))** + +- **Use PrematureExit exception instead of weird hack in tests + ([17870](https://github.com/civicrm/civicrm-core/pull/17870))** + +- **Remove unnecessary try/catch per #17729 + ([17823](https://github.com/civicrm/civicrm-core/pull/17823))** + +- **handlePaymentNotification() should not be declared as a static method + ([17849](https://github.com/civicrm/civicrm-core/pull/17849))** + +- **MembershipRenewalTest - Fix failure + ([17830](https://github.com/civicrm/civicrm-core/pull/17830))** + +- **Remove hard coded charset. + ([17826](https://github.com/civicrm/civicrm-core/pull/17826))** + +- **getLoggedInContactID() is a static function + ([17783](https://github.com/civicrm/civicrm-core/pull/17783))** + +- **Remove PaymentExpress ipn class + ([17763](https://github.com/civicrm/civicrm-core/pull/17763))** + +- **Remove unused, deprecated functions + ([17761](https://github.com/civicrm/civicrm-core/pull/17761))** + +- **CIVICRM_BAO_CACHE_ADAPTER - Remove obsolete option + ([17990](https://github.com/civicrm/civicrm-core/pull/17990))** + +- **More unused functions in GenCode + ([17756](https://github.com/civicrm/civicrm-core/pull/17756))** + +- **Temporary tables should follow consistent naming convention + ([dev/core#183](https://lab.civicrm.org/dev/core/-/issues/183): + [17827](https://github.com/civicrm/civicrm-core/pull/17827))** + - **CRM_Core_BAO_Cache - Remove functions deprecated a year ago ([17989](https://github.com/civicrm/civicrm-core/pull/17989))** From 7921235102ae4245af32a59eaef0fc64826d4b56 Mon Sep 17 00:00:00 2001 From: Andrew Hunt Date: Wed, 2 Sep 2020 15:14:34 -0400 Subject: [PATCH 083/834] 5.29.0 release notes: finished writing --- release-notes/5.29.0.md | 678 ++++++++++++++++++++++++++++------------ 1 file changed, 482 insertions(+), 196 deletions(-) diff --git a/release-notes/5.29.0.md b/release-notes/5.29.0.md index 1be7e9ef6661..3c7ff2e5779f 100644 --- a/release-notes/5.29.0.md +++ b/release-notes/5.29.0.md @@ -13,16 +13,42 @@ Released September 2, 2020 | *Does this version...?* | | |:--------------------------------------------------------------- |:-------:| -| Fix security vulnerabilities? | | -| Change the database schema? | | -| Alter the API? | | -| Require attention to configuration options? | | -| Fix problems installing or upgrading to a previous version? | | -| Introduce features? | | -| Fix bugs? | | +| Fix security vulnerabilities? | no | +| **Change the database schema?** | **yes** | +| **Alter the API?** | **yes** | +| **Require attention to configuration options?** | **yes** | +| **Fix problems installing or upgrading to a previous version?** | **yes** | +| **Introduce features?** | **yes** | +| **Fix bugs?** | **yes** | ## Features +### CiviContribute + +- **Add configure and priceset url icons on public contribution & event pages + ([dev/core#1905](https://lab.civicrm.org/dev/core/-/issues/1905): + [17942](https://github.com/civicrm/civicrm-core/pull/17942), + [18088](https://github.com/civicrm/civicrm-core/pull/18088) and + [18064](https://github.com/civicrm/civicrm-core/pull/18064))** + + A "Configure" button now appears to administrators when viewing a contribution + page or event, or a priceset within either, from the frontend. This links to + the corresponding form to administer the contribution page, event, or + priceset. + +### CiviEvent + +- **Move Event Cart to extension + ([17741](https://github.com/civicrm/civicrm-core/pull/17741), + [17743](https://github.com/civicrm/civicrm-core/pull/17743), + [17861](https://github.com/civicrm/civicrm-core/pull/17861), + [17884](https://github.com/civicrm/civicrm-core/pull/17884), + [17885](https://github.com/civicrm/civicrm-core/pull/17885), and + [17891](https://github.com/civicrm/civicrm-core/pull/17891))** + + The event cart features have been moved to a separate extension that is + shipped with core. + ### CiviMember - **Option to update expired memberships as part of the job.process_membership @@ -41,6 +67,13 @@ Released September 2, 2020 Deceased will always be excluded +- **Add auto-renew status to membership detail report + ([17683](https://github.com/civicrm/civicrm-core/pull/17683) and + [17911](https://github.com/civicrm/civicrm-core/pull/17911))** + + The membership detail report can now display and filter on the auto-renew + status of each membership. + ### Backdrop Integration - **Support for installing CiviCRM-Backdrop via "setup" UI @@ -52,6 +85,33 @@ Released September 2, 2020 the setup screen to complete installation as opposed to having to navigate to a specific link. +### Drupal Integration + +- **Upgrading a site that still has "mysql" in the dsn string breaks in latest + master ([dev/core#1937](https://lab.civicrm.org/dev/core/-/issues/1937): + [18174](https://github.com/civicrm/civicrm-core/pull/18174))** + + This adds an upgrade message for 5.29.0 warning that Composer patching will + need to be enabled before upgrading to 5.30.0 or higher. + + The "mysql" in the DSN (as opposed to "mysqli") will not be a problem, but the + solution for this is in a package patch that appears in 5.30. + +- **Installation - Support "activate first" w/setup UI + ([606](https://github.com/civicrm/civicrm-drupal/pull/606))** + + The new setup user interface for Backdrop and WordPress installations is now + available for Drupal 7. + +### WordPress Integration + +- **Add system check to ensure WP base page exists + ([17698](https://github.com/civicrm/civicrm-core/pull/17698))** + + A new system check will display a message if the base page, as set in CiviCRM, + doesn't match an existing page in WordPress. No message will display if + WordPress multisite is enabled. + ### Core CiviCRM - **Contact export is very slow, creates huge temporary tables @@ -61,19 +121,48 @@ Released September 2, 2020 Improves performance when exporting contacts by only exporting primary address fields. +- **Allow different output formats for CiviReport results, like native excel + format, and untangle code + ([dev/core#1855](https://lab.civicrm.org/dev/core/-/issues/1855): + [17901](https://github.com/civicrm/civicrm-core/pull/17901))** + + This makes it possible to extend CiviReport by reworking the PDF and CSV + generation code as new output handlers, and allowing for extension developers + to add new ones. + - **Icon in status message after saving a civireport is misleading ([dev/report#43](https://lab.civicrm.org/dev/report/-/issues/43): [17863](https://github.com/civicrm/civicrm-core/pull/17863))** Improves icons in Status Messages for reports. +- **Show cron warning on Scheduled Jobs admin page + ([18065](https://github.com/civicrm/civicrm-core/pull/18065))** + + A pop-up message will appear on the Scheduled Jobs page if cron has not been + running. + +- **Add APIv4 and pseudoconstants for RelationshipCache + ()** + +- **RelationshipCache - Add a high-level index to facilitate relationship + queries (more fields) + ([17781](https://github.com/civicrm/civicrm-core/pull/17781) and + [17879](https://github.com/civicrm/civicrm-core/pull/17879))** + + A new table caches relationships to provide a single resource for tracking + relationships from a specified contact's perspective. The RelationshipCache + records the "near" and "far" contacts and relationship names along with the + orientation and relationship type. Each record from `civicrm_relationship` + will have two records in `civicrm_relationship_cache`, one for each + orientation. + - **Remove ORDER BY and GROUP BY from alphabetQuery to improve performance ([16877](https://github.com/civicrm/civicrm-core/pull/16877))** - **Differentiate smart group from regular group using icon in select2 field ([dev/core#785](https://lab.civicrm.org/dev/core/-/issues/785): - [17927](https://github.com/civicrm/civicrm-core/pull/17927) and - [13958](https://github.com/civicrm/civicrm-core/pull/13958))** + [17927](https://github.com/civicrm/civicrm-core/pull/17927))** Improves Group's select2 by adding icon next to smart groups. @@ -83,6 +172,65 @@ Released September 2, 2020 Makes it so sites can use ssl to connect to mysql. +- **Add in hook to allow for altering of Profile Schemas + ([dev/core#1913](https://lab.civicrm.org/dev/core/-/issues/1913): + [17986](https://github.com/civicrm/civicrm-core/pull/17986))** + + Extensions that allow additional fields to be added to profiles can now add + schemas so that fields can be added from new entities. + +- **Why not make the buttons flat? + ([18054](https://github.com/civicrm/civicrm-core/pull/18054))** + + Buttons no longer have a gradient background. + +- **SystemCheck: add ability to efficiently run only specified checks + ([17824](https://github.com/civicrm/civicrm-core/pull/17824))** + + The APIv4 system check action can now filter on name, running just the + specified check(s). + +- **Change inform-icon to fa-info-circle + ([18001](https://github.com/civicrm/civicrm-core/pull/18001))** + + The information icon is now produced with Font Awesome rather than a sprite. + +- **APIv4 - Add keyword to select all custom fields + ([17955](https://github.com/civicrm/civicrm-core/pull/17955))** + + You can now use a wildcard to select all custom fields on an entity without + specifying each individually. + +- **Improve caching of current domain + ([17916](https://github.com/civicrm/civicrm-core/pull/17916))** + + Domain information is now cached in one central place, making it more + straightforward to use and flush. + +- **APIv4 - Specify BridgeEntities to assist with joins + ([17808](https://github.com/civicrm/civicrm-core/pull/17808))** + + Minor entities that intermediate many-to-many relationships are now specified + as bridge entities and extend from a new base class. This will allow them to + be distinguished better in the future. + +- **APIv4 - Add shorthand for setCheckPermissions() + ([17834](https://github.com/civicrm/civicrm-core/pull/17834) and + [17874](https://github.com/civicrm/civicrm-core/pull/17874))** + + The CRUD functions of APIv4 now accept a boolean argument that corresponds to + the value sent to `setCheckPermissions()`. This provides a shorthand for a + very common use case. + +- **Add search extension + ([17775](https://github.com/civicrm/civicrm-core/pull/17775), + [17789](https://github.com/civicrm/civicrm-core/pull/17789), and + [17864](https://github.com/civicrm/civicrm-core/pull/17864))** + + An extension that replaces Search Builder is now shipped with core, though it + is hidden from the extensions user interface because it is currently + incompatible with the default theming framework. + ## Bugs resolved ### Wordpress Integration @@ -105,6 +253,10 @@ Released September 2, 2020 - **Fix PHP notice on wordpress permissions form ([17758](https://github.com/civicrm/civicrm-core/pull/17758))** +- **Slow down the frequency of WordPress "heartbeat" calls in CiviCRM admin + ([dev/wordpress#67](https://lab.civicrm.org/dev/wordpress/-/issues/67): + [214](https://github.com/civicrm/civicrm-wordpress/pull/214))** + ### Drupal Integration - **Drupal 8 - Using Create User Record action on a contact with no email is too @@ -122,13 +274,63 @@ Released September 2, 2020 Skip the server variable checks if running in a CLI environment, removing an error when running Drush commands against Drupal 8 and Drupal 9 based sites. +- **Can't find recaptcha in Drupal 8 + ([dev/core#1871](https://lab.civicrm.org/dev/core/-/issues/1871): + [17822](https://github.com/civicrm/civicrm-core/pull/17822))** + +- **Fixed for multi-select filter + ([615](https://github.com/civicrm/civicrm-drupal/pull/615))** + + This fixes filters for multi-select fields in Views. + +- **fix url for file field + ([608](https://github.com/civicrm/civicrm-drupal/pull/608))** + + This fixes the generated URL for file fields in Views. + ### CiviCase - **Fix case activity field set to allow long details to be exported ([17970](https://github.com/civicrm/civicrm-core/pull/17970))** +- **Case export has two fields that are not what they say they are + ([dev/core#1916](https://lab.civicrm.org/dev/core/-/issues/1916): + [18043](https://github.com/civicrm/civicrm-core/pull/18043))** + +- **Collapsed custom field set for activities with a required radio makes case + activity buttons seem disabled + ([dev/core#1928](https://lab.civicrm.org/dev/core/-/issues/1928): + [18080](https://github.com/civicrm/civicrm-core/pull/18080))** + +- **CRM_Utils_Check_Component_Case - Guard against post-upgrade crash + ([17944](https://github.com/civicrm/civicrm-core/pull/17944))** + ### CiviContribute +- **Define the logic that sets (or not) contribution receive_date in relation to + payments + ([dev/financial#139](https://lab.civicrm.org/dev/financial/-/issues/139): + [17777](https://github.com/civicrm/civicrm-core/pull/17777) and + [18000](https://github.com/civicrm/civicrm-core/pull/18000))** + + This undoes a regression where the contribution date of a pay-later + contribution would change when a payment completes the contribution. The + contribution date is the accrual date and is distinct from the date the + contribution is entered and the date when all payments have been received. + + In the process, this changes the "Update pending contribution status" search + action to be "Record payments for contributions". The result is generally the + same, but the status change comes about because the payments complete the + contributions. You can modify contribution statuses in bulk with a profile by + using the "Update multiple contributions" action. + +- **Disable frequency/interval fields if not required. Mark required if they are + so they are validated before submit + ([17526](https://github.com/civicrm/civicrm-core/pull/17526))** + + The frequency and interval fields for recurring contributions now use jQuery + validation to disable or require them as appropriate. + - **Paypal IPN sometimes fails to update the next scheduled payment date when recording the latest recurring payment ([dev/core#1679](https://lab.civicrm.org/dev/core/-/issues/1679): @@ -148,17 +350,20 @@ Released September 2, 2020 than the site's default currency, the "grand total" row shows the sign of the default currency instead of the filtered currency. -- **Define the logic that sets (or not) contribution receive_date in relation to - payments - ([dev/financial#139](https://lab.civicrm.org/dev/financial/-/issues/139): - [17777](https://github.com/civicrm/civicrm-core/pull/17777))** - - Ensures that the contribution receive_date does not change when payments come - in. - - **Fix PaypalIPN single function to not receive-by-reference ([18044](https://github.com/civicrm/civicrm-core/pull/18044))** +- **Allow Failed -> Completed status for contributions + ([dev/core#1906](https://lab.civicrm.org/dev/core/-/issues/1906): + [17943](https://github.com/civicrm/civicrm-core/pull/17943))** + +- **Fix currency symbol for Total Amount on contribution page + ([17703](https://github.com/civicrm/civicrm-core/pull/17703))** + + This resolves a but where the total amount would show the default system + currency symbol regardless of the currency configured on the contribution + page. + - **On behalf label / Honoree Title / Honoree Description not translatable on contribution page ([dev/core#1280](https://lab.civicrm.org/dev/core/-/issues/1280): @@ -176,6 +381,32 @@ Released September 2, 2020 - **Do not overwrite values saved from the repeatContribution routine ([17972](https://github.com/civicrm/civicrm-core/pull/17972))** +- **"Contribution Source" profile field has no effect on new contribution + ([dev/core#1902](https://lab.civicrm.org/dev/core/-/issues/1902): + [17930](https://github.com/civicrm/civicrm-core/pull/17930))** + +- **Incorrect check for "soft_credit" after making pcp donation + ([dev/core#1915](https://lab.civicrm.org/dev/core/-/issues/1915): + [18002](https://github.com/civicrm/civicrm-core/pull/18002))** + +- **Total Tax Amount on the Contribution (generated in backoffice/offline) no + longer adds up to sum of the Tax Amount for the individual line items + ([dev/core#1983](https://lab.civicrm.org/dev/core/-/issues/1983): + [18290](https://github.com/civicrm/civicrm-core/pull/18290))** + +- **Downloaded Invoice activity attaches non wkhtmltopdf invoice + ([dev/core#1922](https://lab.civicrm.org/dev/core/-/issues/1922): + [18056](https://github.com/civicrm/civicrm-core/pull/18056))** + + Downloading an invoice would ignore whether wkhtmltopdf was configured and + always use dompdf. + +- **Fix repeattransaction api to use custom data from the template contribution + ([17975](https://github.com/civicrm/civicrm-core/pull/17975))** + + The Contribution.repeattransaction API will now copy custom data from the + template rather than the earliest related contribution. + ### CiviEvent - **Can't meaningfully disable self-service transfer/cancellation once enabled @@ -194,6 +425,13 @@ Released September 2, 2020 ### CiviMail +- **Remove url-tracking in mass SMS + ([dev/core#1843](https://lab.civicrm.org/dev/core/-/issues/1843): + [17700](https://github.com/civicrm/civicrm-core/pull/17700))** + + Rewritten URLs were confusing and made SMS messages unnecessarily long. There + also have never been SMS tracking reports to use them anyway. + - **Test mailings create new contacts even when "Add Contacts" permission is not present. ([dev/mail#70](https://lab.civicrm.org/dev/mail/-/issues/70): [17867](https://github.com/civicrm/civicrm-core/pull/17867))** @@ -201,14 +439,57 @@ Released September 2, 2020 Ensures contacts are not created when a user without permissions to create contacts sends a test email. +- **Mailing Subscription form does not validate reCaptcha + ([dev/core#1755](https://lab.civicrm.org/dev/core/-/issues/1755): + [17305](https://github.com/civicrm/civicrm-core/pull/17305))** + +- **Text version of unsubscribed email is missing the link to resubscribe + ([dev/core#1919](https://lab.civicrm.org/dev/core/-/issues/1919): + [18015](https://github.com/civicrm/civicrm-core/pull/18015))** + +- **Add CiviMail synchronisation frequency setting + (in support of [dev/core#1768](https://lab.civicrm.org/dev/core/-/issues/1768): + [17709](https://github.com/civicrm/civicrm-core/pull/17709))** + + This adds a setting for updating how frequently the database should be updated + with the status of emails that have been sent. Updating the database more + frequently can slow the process down, but it reduces the likelihood that a + recipient will be re-emailed when recovering from a stalled mailing. + +- **Make new email open and url routes 'public' + ([17813](https://github.com/civicrm/civicrm-core/pull/17813))** + + This denotes the paths for open and URL tracking as "public" pages as they are + meant to be used by unauthenticated email clients and recipients. + ### CiviMember - **Decimal Separator - Invalid value "total_amount" (NaN,N) creating or editing a membership ([dev/core#1113](https://lab.civicrm.org/dev/core/-/issues/1113): [16429](https://github.com/civicrm/civicrm-core/pull/16429))** +- **Offline Membership Renewal Tax Calculation is incorrect [regression] + ([dev/core#1972](https://lab.civicrm.org/dev/core/-/issues/1972): + [18271](https://github.com/civicrm/civicrm-core/pull/18271))** + ### Core CiviCRM +- **CRM_Dedupe_Finder parses phone key incorrectly + ([dev/core#1767](https://lab.civicrm.org/dev/core/-/issues/1767): + [17361](https://github.com/civicrm/civicrm-core/pull/17361) and + [17882](https://github.com/civicrm/civicrm-core/pull/17882))** + + The process to match duplicate contacts was not accurately identifying phone + fields, causing contacts with matching phone numbers to be treated as if they + didn't match. + +- **Dedupe: Location type is treated inconsistently + ([dev/core#1826](https://lab.civicrm.org/dev/core/-/issues/1826): + [17645](https://github.com/civicrm/civicrm-core/pull/17645))** + + Duplicate matching now ignores location type for postal address fields, now + consistent with how it has ignored phone and email location types. + - **Installing drupal 8 using civicrm-setup leads to "incorrect resource url" system status check errors ([dev/drupal#114](https://lab.civicrm.org/dev/drupal/-/issues/114) and @@ -230,216 +511,173 @@ Released September 2, 2020 ([dev/core#1670](https://lab.civicrm.org/dev/core/-/issues/1670): [17580](https://github.com/civicrm/civicrm-core/pull/17580))** +- **Email template permissions + ([dev/core#1751](https://lab.civicrm.org/dev/core/-/issues/1751): + [17480](https://github.com/civicrm/civicrm-core/pull/17480))** + + Users sending an email are no longer able to save or modify a template if they + lack the "edit message templates" permission. + +- **Using Parent tag in search form doesn't pull contacts marked with child tag + in search form result + ([dev/core#1795](https://lab.civicrm.org/dev/core/-/issues/1795): + [17513](https://github.com/civicrm/civicrm-core/pull/17513))** + +- **Activity Search : Tags are not working + ([dev/core#1827](https://lab.civicrm.org/dev/core/-/issues/1827): + [17655](https://github.com/civicrm/civicrm-core/pull/17655) and + [17755](https://github.com/civicrm/civicrm-core/pull/17755))** -- **dev/core#1751: [Create Email] Only Show Update/Save Template when User has Permission to Edit Templates ([17480](https://github.com/civicrm/civicrm-core/pull/17480))** - -- **dev/core#1755 Fix reCaptcha on Mailing Subscribe ([17305](https://github.com/civicrm/civicrm-core/pull/17305))** - -- **dev/core#1767 Fix phone key parsing in CRM_Dedupe_Finder ([17361](https://github.com/civicrm/civicrm-core/pull/17361) and [17882](https://github.com/civicrm/civicrm-core/pull/17882))** - -- **dev/core#1768 - Add CiviMail synchronisation frequency setting. ([17709](https://github.com/civicrm/civicrm-core/pull/17709))** - -- **core#1795: Searchable Parent tags ([17513](https://github.com/civicrm/civicrm-core/pull/17513))** - -- **core#1805: Autocomplete-select custom field is not searchable ([17569](https://github.com/civicrm/civicrm-core/pull/17569))** - -- **dev/core#1812 Missing view when logging set in a non-US English instance ([17815](https://github.com/civicrm/civicrm-core/pull/17815))** - -- **core#1826: Ignore location_type_id when deduping postal address ([17645](https://github.com/civicrm/civicrm-core/pull/17645))** - -- **dev/core#1827 activity search - fixing search by tags ([17655](https://github.com/civicrm/civicrm-core/pull/17655) and [17755](https://github.com/civicrm/civicrm-core/pull/17755))** - -- **dev/core#1843 Remove url-tracking in mass sms. ([17700](https://github.com/civicrm/civicrm-core/pull/17700))** - -- **dev/core#1853 - Fix validation errors when removing contact subtype ([17765](https://github.com/civicrm/civicrm-core/pull/17765))** - -- **dev/core#1855 - Allow different output formats for CiviReport results and untangle code ([17901](https://github.com/civicrm/civicrm-core/pull/17901))** - -- **dev/core#1858 Prevent Duplicate contact records being created and har… ([17769](https://github.com/civicrm/civicrm-core/pull/17769))** - -- **dev/core#1861 fix failure to unset location_type_id when saving uffield ([17812](https://github.com/civicrm/civicrm-core/pull/17812))** - -- **dev/core#1863 Downgrade checkEnvironment level and skip non-prod checks ([17807](https://github.com/civicrm/civicrm-core/pull/17807))** - -- **dev/core#1868 - Regression - Description field is always blank on profiles admin page and slew of E_NOTICES ([17786](https://github.com/civicrm/civicrm-core/pull/17786))** - -- **dev/core#1869 - Include BOM in attachment when sending CSV CiviReport via mail_report job ([17806](https://github.com/civicrm/civicrm-core/pull/17806))** - -- **dev/core#1871 - require_once's that include "packages/" in the path don't work on drupal 8 ([17822](https://github.com/civicrm/civicrm-core/pull/17822))** - -- **dev/core#1872 - Packages and vendor path calculation used in system check is outdated ([17844](https://github.com/civicrm/civicrm-core/pull/17844))** - -- **dev/core#1874 - Failing test for new Individual form ([17835](https://github.com/civicrm/civicrm-core/pull/17835))** - -- **dev/core#1880 add backticks to custom field insertions ([17848](https://github.com/civicrm/civicrm-core/pull/17848))** - -- **dev/core#1888 - Fix one line in PR 17888 ([17898](https://github.com/civicrm/civicrm-core/pull/17898))** - -- **dev/core#1888 and dev/core#1885 - Fatal error on advanced search and warnings and missing group display on contact form ([17888](https://github.com/civicrm/civicrm-core/pull/17888))** +- **Autocomplete-select custom field is not searchable + ([dev/core#1805](https://lab.civicrm.org/dev/core/-/issues/1805): + [17569](https://github.com/civicrm/civicrm-core/pull/17569))** -- **dev/core#1894 - Make CRM_Activity_Form_SearchTest::testQill less time-sensitive ([17902](https://github.com/civicrm/civicrm-core/pull/17902))** +- **Do not allow enabling database logging when using multilingual + ([dev/core#1812](https://lab.civicrm.org/dev/core/-/issues/1812): + [17815](https://github.com/civicrm/civicrm-core/pull/17815))** -- **dev/core#1895 fix first/last name adv search ([17950](https://github.com/civicrm/civicrm-core/pull/17950))** + This has never worked, with network errors as the result. This prevents it + from being set. -- **dev/core#1902: "Contribution Source" profile field has no effect ([17930](https://github.com/civicrm/civicrm-core/pull/17930))** +- **Can't remove subtype if any required custom field is based on it + ([dev/core#1853](https://lab.civicrm.org/dev/core/-/issues/1853): + [17765](https://github.com/civicrm/civicrm-core/pull/17765))** -- **dev/core#1905 force backend links for new "configure" buttons ([17942](https://github.com/civicrm/civicrm-core/pull/17942), [18088](https://github.com/civicrm/civicrm-core/pull/18088) and [18064](https://github.com/civicrm/civicrm-core/pull/18064))** +- **User account form action not passing along contact id correctly possibly + leading to duplicate contacts + ([dev/core#1858](https://lab.civicrm.org/dev/core/-/issues/1858): + [17769](https://github.com/civicrm/civicrm-core/pull/17769))** -- **dev/core#1906 - Allow payment create api to record payment on Failed … ([17943](https://github.com/civicrm/civicrm-core/pull/17943))** + When creating a user account from a contact record, the contact ID was not + properly passed along. Depending upon your unsupervised duplicate matching + rule, it would potentially create a duplicate contact record. -- **dev/core#1909 Fix E-notice when adding a field on a profile ([18006](https://github.com/civicrm/civicrm-core/pull/18006), [17962](https://github.com/civicrm/civicrm-core/pull/17962), [17964](https://github.com/civicrm/civicrm-core/pull/17964), [17959](https://github.com/civicrm/civicrm-core/pull/17959), [17985](https://github.com/civicrm/civicrm-core/pull/17985))** +- **Cannot edit a profile field of "Email" to use the Location Type "Primary" + ([dev/core#1861](https://lab.civicrm.org/dev/core/-/issues/1861): + [17812](https://github.com/civicrm/civicrm-core/pull/17812))** -- **dev/core#1913 Allow for schemas to be added by extensions if they are… ([17986](https://github.com/civicrm/civicrm-core/pull/17986))** + The location type would get reset to your default location type rather than + staying as "primary". -- **dev/core#1915 - E_NOTICE when making pcp contribution ([18002](https://github.com/civicrm/civicrm-core/pull/18002))** +- **Downgrade checkEnvironment level and skip non-prod checks (part of + [dev/core#1863](https://lab.civicrm.org/dev/core/-/issues/1863): + [17807](https://github.com/civicrm/civicrm-core/pull/17807))** -- **dev/core#1916 - Fix naming of case export fields / remove ones that aren't true ([18043](https://github.com/civicrm/civicrm-core/pull/18043))** + In non-production environments, a system check produces a message saying so. + This downgrades it from an "Alert" to a more appropriate level of "Notice", + and it also suppresses cron and CiviMail system checks in this situation. -- **dev/core#1918 - Remove dubious qfkey checking code that never runs ([18007](https://github.com/civicrm/civicrm-core/pull/18007))** +- **Slew of E_NOTICES on Profiles admin page and description field is always + blank ([dev/core#1868](https://lab.civicrm.org/dev/core/-/issues/1868): + [17786](https://github.com/civicrm/civicrm-core/pull/17786))** -- **dev/core#1919 - Missing resubscribe url in text/plain version of unsubscribe confirmation email ([18015](https://github.com/civicrm/civicrm-core/pull/18015))** +- **CiviReport sent as email via mail_report job with a CSV attachment doesn't + show UTF8 characters properly in Excel + ([dev/core#1869](https://lab.civicrm.org/dev/core/-/issues/1869): + [17806](https://github.com/civicrm/civicrm-core/pull/17806))** -- **dev/core#1921 [Ref] remove isoToMysql ([18025](https://github.com/civicrm/civicrm-core/pull/18025))** +- **References to packages path in security status checks are assumed to be + under civicrm root + ([dev/core#1872](https://lab.civicrm.org/dev/core/-/issues/1872): + [17844](https://github.com/civicrm/civicrm-core/pull/17844))** -- **dev/core#1928 Fix HTML5 error due to required attribute being set swi… ([18080](https://github.com/civicrm/civicrm-core/pull/18080))** + This changes how package and vendor paths are calculated in system checks + since (in Drupal 8 and potentially elsewhere) they may not be within the + CiviCRM root folder. -- **dev/core#1932 - Make status-checks more polite during upgrade ([18085](https://github.com/civicrm/civicrm-core/pull/18085))** +- **custom data insert/update error if using reserved words + ([dev/core#1880](https://lab.civicrm.org/dev/core/-/issues/1880): + [17848](https://github.com/civicrm/civicrm-core/pull/17848))** -- **dev/core#1937 - Upgrade message about needing composer patching turned on and updating mysql in DSN strings ([18174](https://github.com/civicrm/civicrm-core/pull/18174))** +- **Advanced Search (search by complete or partial name) returns all contacts + ([dev/core#1895](https://lab.civicrm.org/dev/core/-/issues/1895): + [17950](https://github.com/civicrm/civicrm-core/pull/17950))** -- **dev/core#1983 Fix to tax calculation on multi-line-item ([18290](https://github.com/civicrm/civicrm-core/pull/18290))** + The ability to search first/last name was recently added to core. The primary + intent was to properly handle advanced searches that originate from + quicksearch first/last name filters. While that was working, a first/last name + search that originated directly from an advanced search was not working. This + fixes the issue. -- **dev/core#1972 Fix tax_amount calclation on renewal form ([18271](https://github.com/civicrm/civicrm-core/pull/18271))** +- **Miscellaneous E_NOTICES + ([dev/core#1909](https://lab.civicrm.org/dev/core/-/issues/1909): + [18006](https://github.com/civicrm/civicrm-core/pull/18006), + [17962](https://github.com/civicrm/civicrm-core/pull/17962), + [17964](https://github.com/civicrm/civicrm-core/pull/17964), + [17959](https://github.com/civicrm/civicrm-core/pull/17959), and + [17985](https://github.com/civicrm/civicrm-core/pull/17985))** +- **CRM_Core_Key::valid() does backwards comparison + ([dev/core#1918](https://lab.civicrm.org/dev/core/-/issues/1918): + [18007](https://github.com/civicrm/civicrm-core/pull/18007))** + This removes legacy code that appeared to attempt to check for a valid + quickForm key but wasn't effectively doing so. +- **Upgrade fails for 4.6 => 5.29 during status-check + ([dev/core#1932](https://lab.civicrm.org/dev/core/-/issues/1932): + [18085](https://github.com/civicrm/civicrm-core/pull/18085))** -- **Installation doclinks not getting url-rewritten ([18175](https://github.com/civicrm/civicrm-core/pull/18175))** +- **Installation doclinks not getting url-rewritten + ([18175](https://github.com/civicrm/civicrm-core/pull/18175))** -- **Fix button name on updated form ([18000](https://github.com/civicrm/civicrm-core/pull/18000))** + Links to the new installation documentation are now handled in the same way as + other documentation sites. -- **Show cron warning on Scheduled Jobs admin page ([18065](https://github.com/civicrm/civicrm-core/pull/18065))** +- **Wrap multi record custom field inside a div + ([17966](https://github.com/civicrm/civicrm-core/pull/17966))** -- **Use correct pdf package to generate pdf file on invoice download/email activity ([18056](https://github.com/civicrm/civicrm-core/pull/18056))** +- **Fixed filling default values for tagssets in the advanced search form + ([17978](https://github.com/civicrm/civicrm-core/pull/17978))** -- **Why not make the buttons flat? ([18054](https://github.com/civicrm/civicrm-core/pull/18054))** +- **Simplify caching of status checks + ([17817](https://github.com/civicrm/civicrm-core/pull/17817))** -- **Wrap multi record custom field inside a div ([17966](https://github.com/civicrm/civicrm-core/pull/17966))** +- **ensure custom field checkboxes are populated in profiles + ([17977](https://github.com/civicrm/civicrm-core/pull/17977))** -- **SystemCheck: add ability to efficiently run only specified checks ([17824](https://github.com/civicrm/civicrm-core/pull/17824))** +- **Upgrade PEAR/mail_mime package to be compliant with PHP7.4 and deploy it + using composer ([17948](https://github.com/civicrm/civicrm-core/pull/17948))** -- **Change inform-icon to fa-info-circle ([18001](https://github.com/civicrm/civicrm-core/pull/18001))** +- **Setup UI - Validate that at least one "Component" is enabled + ([17778](https://github.com/civicrm/civicrm-core/pull/17778))** -- **Fix repeattransaction api to use custom data from the template contribution ([17975](https://github.com/civicrm/civicrm-core/pull/17975))** + This prevents you from proceeding with installation without enabling at least + one component. Not enabling any components causes a messy failed + installation. -- **Fixed filling default values for tagssets in the advanced search form ([17978](https://github.com/civicrm/civicrm-core/pull/17978))** - -- **Simplify caching of status checks ([17817](https://github.com/civicrm/civicrm-core/pull/17817))** - -- **ensure custom field checkboxes are populated in profiles ([17977](https://github.com/civicrm/civicrm-core/pull/17977))** - -- **Upgrade PEAR/mail_mime package to be compliant with PHP7.4 and deploy it using composer ([17948](https://github.com/civicrm/civicrm-core/pull/17948))** - -- **APIv4 - Add keyword to select all custom fields ([17955](https://github.com/civicrm/civicrm-core/pull/17955))** - -- **CRM_Utils_Check_Component_Case - Guard against post-upgrade crash ([17944](https://github.com/civicrm/civicrm-core/pull/17944))** - -- **Improve caching of current domain ([17916](https://github.com/civicrm/civicrm-core/pull/17916))** - -- **Setup UI - Validate that at least one "Component" is enabled ([17778](https://github.com/civicrm/civicrm-core/pull/17778))** - -- **Member detail report: nest "in" options in parentheses ([17911](https://github.com/civicrm/civicrm-core/pull/17911))** - -- **Replace a load of references to the wiki with docs links ([17900](https://github.com/civicrm/civicrm-core/pull/17900))** - -- **Call apiv4 from Contribution create rather than fugly addActivity function ([17881](https://github.com/civicrm/civicrm-core/pull/17881))** - -- **APIv4 - Add BasicEntity helper class ([17899](https://github.com/civicrm/civicrm-core/pull/17899))** - -- **Add APIv4 and pseudoconstants for RelationshipCache ([17879](https://github.com/civicrm/civicrm-core/pull/17879))** - -- **Update version in the test_data_second_domain file and also update the setVersion script to update the file version as necessary ([17897](https://github.com/civicrm/civicrm-core/pull/17897))** - -- **Be a little less supportive to cvs ([17896](https://github.com/civicrm/civicrm-core/pull/17896))** - -- **APIv4 - Specify BridgeEntities to assist with joins ([17808](https://github.com/civicrm/civicrm-core/pull/17808))** - -- **Event Cart ext: Move menu entries to extension ([17891](https://github.com/civicrm/civicrm-core/pull/17891))** - -- **EventCart ext: Cleanup and move form components to ext ([17885](https://github.com/civicrm/civicrm-core/pull/17885))** - -- **EventCart ext: Fix autogenerated code, remove unused hooks, update readme ([17884](https://github.com/civicrm/civicrm-core/pull/17884))** - -- **Add eventcart shell ([17741](https://github.com/civicrm/civicrm-core/pull/17741))** - -- **EventCart - Resolve BAO identity and uncommitted DAO changes ([17861](https://github.com/civicrm/civicrm-core/pull/17861))** - -- **Move BAO and template files into event cart ([17743](https://github.com/civicrm/civicrm-core/pull/17743))** +- **Call apiv4 from Contribution create rather than fugly addActivity function + ([17881](https://github.com/civicrm/civicrm-core/pull/17881))** - **Search debug ([17887](https://github.com/civicrm/civicrm-core/pull/17887))** -- **Use new checkPermissions shorthand in api calls ([17874](https://github.com/civicrm/civicrm-core/pull/17874))** - -- **Simplify flushing group contact cache query to reduce table locking and improve performance ([17846](https://github.com/civicrm/civicrm-core/pull/17846))** +- **Simplify flushing group contact cache query to reduce table locking and + improve performance + ([17846](https://github.com/civicrm/civicrm-core/pull/17846))** -- **Disable frequency/interval fields if not required. Mark required if they are so they are validated before submit ([17526](https://github.com/civicrm/civicrm-core/pull/17526))** +- **Make api get upgrade-safe + ([17729](https://github.com/civicrm/civicrm-core/pull/17729))** -- **Fix currency symbol for Total Amount on contribution page ([17703](https://github.com/civicrm/civicrm-core/pull/17703))** + This avoids database errors when the code and database versions don't match by + preventing the API from accessing fields that are unsupported in the database. -- **RelationshipCache - Add a high-level index to facilitate relationship queries (more fields) ([17781](https://github.com/civicrm/civicrm-core/pull/17781))** +- **CheckEnv - Give new installs a grace period before 'Cron Not Running' msg + ([17800](https://github.com/civicrm/civicrm-core/pull/17800))** -- **Search Ext: fix loading options and parsing custom field names ([17864](https://github.com/civicrm/civicrm-core/pull/17864))** + The system check will lower the severity of the "cron not running" check for + the first 24 hours after a site has been installed. -- **Bump lodash from 4.17.15 to 4.17.19 ([17858](https://github.com/civicrm/civicrm-core/pull/17858))** +- **Sort permittedActivityTypes + ([17794](https://github.com/civicrm/civicrm-core/pull/17794))** + This improves performance on filtering activity types by making it possible to + compare a list of permitted types with the overall list of activity type IDs. -- **Update regen.sh with new & upcoming core extensions ([17839](https://github.com/civicrm/civicrm-core/pull/17839))** - -- **APIv4 - Add activity contacts to APIv4 field spec ([17766](https://github.com/civicrm/civicrm-core/pull/17766))** - -- **Adjust mysql SET NAMES in remaining places as we agreed this was the go ([17825](https://github.com/civicrm/civicrm-core/pull/17825))** - -- **Make new email open and url routes 'public' ([17813](https://github.com/civicrm/civicrm-core/pull/17813))** - -- **Make api get upgrade-safe ([17729](https://github.com/civicrm/civicrm-core/pull/17729))** - -- **CRM_Utils_SQL - Add "onDuplicate()" and "syncInto()" helpers ([17780](https://github.com/civicrm/civicrm-core/pull/17780))** - -- **CheckEnv - Give new installs a grace period before 'Cron Not Running' msg ([17800](https://github.com/civicrm/civicrm-core/pull/17800))** - -- **Add hidden tag to search extension ([17789](https://github.com/civicrm/civicrm-core/pull/17789))** - -- **Sort permittedActivityTypes ([17794](https://github.com/civicrm/civicrm-core/pull/17794))** - -- **Add auto-renew status to membership detail report ([17683](https://github.com/civicrm/civicrm-core/pull/17683))** - -- **APIv4 - Fix saving custom fields with same name ([17791](https://github.com/civicrm/civicrm-core/pull/17791))** - -- **Add system check to ensure WP base page exists ([17698](https://github.com/civicrm/civicrm-core/pull/17698))** - -- **Add search extension ([17775](https://github.com/civicrm/civicrm-core/pull/17775))** - -- **Status Checks - Use more specific label regarding "Domain"/"Organization" check ([17776](https://github.com/civicrm/civicrm-core/pull/17776))** - -- **Bump minimum upgradable version to 4.4.7 ([17750](https://github.com/civicrm/civicrm-core/pull/17750))** - -- **Improve efficiency of findFiles ([17745](https://github.com/civicrm/civicrm-core/pull/17745))** - -- **APIv4 Explorer: Improve selection of fields for HAVING ([17746](https://github.com/civicrm/civicrm-core/pull/17746))** - -- **Convert CRM.utils.formatDate tests to karma ([17757](https://github.com/civicrm/civicrm-core/pull/17757))** - -- **Teach CRM.utils.formatDate to also show time ([17684](https://github.com/civicrm/civicrm-core/pull/17684))** - -- **Fixed for multi-select filter ([615](https://github.com/civicrm/civicrm-drupal/pull/615))** - -- **fix url for file field ([608](https://github.com/civicrm/civicrm-drupal/pull/608))** - -- **Slow down the frequency of WordPress "heartbeat" calls in CiviCRM admin ([214](https://github.com/civicrm/civicrm-wordpress/pull/214))** - -- **Installation - Support "activate first" w/setup UI ([606](https://github.com/civicrm/civicrm-drupal/pull/606))** - +- **APIv4 - Fix saving custom fields with same name + ([17791](https://github.com/civicrm/civicrm-core/pull/17791))** +- **Status Checks - Use more specific label regarding "Domain"/"Organization" + check ([17776](https://github.com/civicrm/civicrm-core/pull/17776))** - **SQL temp table not using utf8mb4 if server default already set to utf8mb4 ([18012](https://github.com/civicrm/civicrm-core/pull/18012))** @@ -468,11 +706,63 @@ Released September 2, 2020 - **Fix JQuery Validation for radios ([17937](https://github.com/civicrm/civicrm-core/pull/17937))** -- **Fix buggy placement of icons on buttons - ([18005](https://github.com/civicrm/civicrm-core/pull/18005))** - ## Miscellany +- **APIv4 - Add BasicEntity helper class + ([17899](https://github.com/civicrm/civicrm-core/pull/17899))** + +- **Bump minimum upgradable version to 4.4.7 + ([17750](https://github.com/civicrm/civicrm-core/pull/17750))** + +- **Update version in the test_data_second_domain file and also update the + setVersion script to update the file version as necessary + ([17897](https://github.com/civicrm/civicrm-core/pull/17897))** + +- **Be a little less supportive to cvs + ([17896](https://github.com/civicrm/civicrm-core/pull/17896))** + +- **Replace a load of references to the wiki with docs links + ([17900](https://github.com/civicrm/civicrm-core/pull/17900))** + +- **Remove unneccessary isoToDate function + ([dev/core#1921](https://lab.civicrm.org/dev/core/-/issues/1921): + [18025](https://github.com/civicrm/civicrm-core/pull/18025))** + +- **CRM_Activity_Form_SearchTest::testQill can fail semi-randomly + ([dev/core#1894](https://lab.civicrm.org/dev/core/-/issues/1894): + [17902](https://github.com/civicrm/civicrm-core/pull/17902))** + +- **Test for fatal error on new individual form + ([dev/core#1874](https://lab.civicrm.org/dev/core/-/issues/1874): + [17835](https://github.com/civicrm/civicrm-core/pull/17835))** + +- **Bump lodash from 4.17.15 to 4.17.19 + ([17858](https://github.com/civicrm/civicrm-core/pull/17858))** + +- **Update regen.sh with new & upcoming core extensions + ([17839](https://github.com/civicrm/civicrm-core/pull/17839))** + +- **APIv4 - Add activity contacts to APIv4 field spec + ([17766](https://github.com/civicrm/civicrm-core/pull/17766))** + +- **Adjust mysql SET NAMES in remaining places as we agreed this was the go + ([17825](https://github.com/civicrm/civicrm-core/pull/17825))** + +- **CRM_Utils_SQL - Add "onDuplicate()" and "syncInto()" helpers + ([17780](https://github.com/civicrm/civicrm-core/pull/17780))** + +- **Improve efficiency of findFiles + ([17745](https://github.com/civicrm/civicrm-core/pull/17745))** + +- **APIv4 Explorer: Improve selection of fields for HAVING + ([17746](https://github.com/civicrm/civicrm-core/pull/17746))** + +- **Convert CRM.utils.formatDate tests to karma + ([17757](https://github.com/civicrm/civicrm-core/pull/17757))** + +- **Teach CRM.utils.formatDate to also show time + ([17684](https://github.com/civicrm/civicrm-core/pull/17684))** + - **CRM_Utils_Hook: deprecation warning and short array syntax ([17995](https://github.com/civicrm/civicrm-core/pull/17995))** @@ -491,9 +781,6 @@ Released September 2, 2020 - **Hooks/Dispatcher - Close loopholes that occur around "preboot" hooks ([17831](https://github.com/civicrm/civicrm-core/pull/17831))** -- **APIv4 - Add shorthand for setCheckPermissions() - ([17834](https://github.com/civicrm/civicrm-core/pull/17834))** - - **Use PrematureExit exception instead of weird hack in tests ([17870](https://github.com/civicrm/civicrm-core/pull/17870))** @@ -811,7 +1098,6 @@ Released September 2, 2020 - **Cache loader - remove legacy handling, handle null result from setting ([17999](https://github.com/civicrm/civicrm-core/pull/17999))** - - **Use case id to get relationship for activity creation ([17256](https://github.com/civicrm/civicrm-core/pull/17256) and [17764](https://github.com/civicrm/civicrm-core/pull/17764))** @@ -835,13 +1121,13 @@ Technologies - Tunbola Ogunwande; Wikimedia Foundation - Eileen McNaughton Most authors also reviewed code for this release; in addition, the following reviewers contributed their comments: -Pengyi Zhang; Andrew Thompson; Artful Robot - Rich Lott; Australian Greens - -Andrew Cormick-Dockery, John Twyman; Bastien Ho; Blackfly Solutions - Alan -Dixon; Carlos Capote; CompuCorp - Jamie Novick; DevApp - Adam Kwiatkowski; +Agileware - Pengyi Zhang; Andrew Thompson; Artful Robot - Rich Lott; Australian +Greens - Andrew Cormick-Dockery, John Twyman; Bastien Ho; Blackfly Solutions - +Alan Dixon; Carlos Capote; CompuCorp - Jamie Novick; DevApp - Adam Kwiatkowski; Francesc Bassas i Bullich; Freeform Solutions - Herb van den Dool; Greenpeace Central and Eastern Europe - Patrick Figel; Irene Meisel; JMA Consulting - Joe Murray; Korlon - Stuart Gaston; Nicol Wistreich; Rar9; Ray Wright; Semper IT - -Karin Gerritsen; Third Sector Design - Michael McAndrew; +Karin Gerritsen; Third Sector Design - Michael McAndrew ## Feedback From 47c2ce56529939b65bc5e710c045a685c4bd3e7e Mon Sep 17 00:00:00 2001 From: Andrew Hunt Date: Wed, 2 Sep 2020 15:19:00 -0400 Subject: [PATCH 084/834] 5.29.0 release notes: get sections in right order --- release-notes/5.29.0.md | 606 ++++++++++++++++++++-------------------- 1 file changed, 303 insertions(+), 303 deletions(-) diff --git a/release-notes/5.29.0.md b/release-notes/5.29.0.md index 3c7ff2e5779f..bae0454eb091 100644 --- a/release-notes/5.29.0.md +++ b/release-notes/5.29.0.md @@ -23,95 +23,6 @@ Released September 2, 2020 ## Features -### CiviContribute - -- **Add configure and priceset url icons on public contribution & event pages - ([dev/core#1905](https://lab.civicrm.org/dev/core/-/issues/1905): - [17942](https://github.com/civicrm/civicrm-core/pull/17942), - [18088](https://github.com/civicrm/civicrm-core/pull/18088) and - [18064](https://github.com/civicrm/civicrm-core/pull/18064))** - - A "Configure" button now appears to administrators when viewing a contribution - page or event, or a priceset within either, from the frontend. This links to - the corresponding form to administer the contribution page, event, or - priceset. - -### CiviEvent - -- **Move Event Cart to extension - ([17741](https://github.com/civicrm/civicrm-core/pull/17741), - [17743](https://github.com/civicrm/civicrm-core/pull/17743), - [17861](https://github.com/civicrm/civicrm-core/pull/17861), - [17884](https://github.com/civicrm/civicrm-core/pull/17884), - [17885](https://github.com/civicrm/civicrm-core/pull/17885), and - [17891](https://github.com/civicrm/civicrm-core/pull/17891))** - - The event cart features have been moved to a separate extension that is - shipped with core. - -### CiviMember - -- **Option to update expired memberships as part of the job.process_membership - ([dev/membership#18](https://lab.civicrm.org/dev/membership/-/issues/18): - [16298](https://github.com/civicrm/civicrm-core/pull/16298))** - - Improves the Job.process_membership API/Scheduled job by adding some new - parameters, specifically: - - - exclude_test_memberships: Exclude test memberships from calculations - (default = TRUE) - - only_active_membership_types: Exclude disabled membership types from - calculations (default = TRUE) - - exclude_membership_status_ids: Default: Exclude Pending, Cancelled, - Expired. - - Deceased will always be excluded - -- **Add auto-renew status to membership detail report - ([17683](https://github.com/civicrm/civicrm-core/pull/17683) and - [17911](https://github.com/civicrm/civicrm-core/pull/17911))** - - The membership detail report can now display and filter on the auto-renew - status of each membership. - -### Backdrop Integration - -- **Support for installing CiviCRM-Backdrop via "setup" UI - ([17749](https://github.com/civicrm/civicrm-core/pull/17749) and - [121](https://github.com/civicrm/civicrm-backdrop/pull/121))** - - Improves the installation and Setup process for Backdrop integration's so that - one enables the CiviCRM module like any other module and then is directed to - the setup screen to complete installation as opposed to having to navigate to - a specific link. - -### Drupal Integration - -- **Upgrading a site that still has "mysql" in the dsn string breaks in latest - master ([dev/core#1937](https://lab.civicrm.org/dev/core/-/issues/1937): - [18174](https://github.com/civicrm/civicrm-core/pull/18174))** - - This adds an upgrade message for 5.29.0 warning that Composer patching will - need to be enabled before upgrading to 5.30.0 or higher. - - The "mysql" in the DSN (as opposed to "mysqli") will not be a problem, but the - solution for this is in a package patch that appears in 5.30. - -- **Installation - Support "activate first" w/setup UI - ([606](https://github.com/civicrm/civicrm-drupal/pull/606))** - - The new setup user interface for Backdrop and WordPress installations is now - available for Drupal 7. - -### WordPress Integration - -- **Add system check to ensure WP base page exists - ([17698](https://github.com/civicrm/civicrm-core/pull/17698))** - - A new system check will display a message if the base page, as set in CiviCRM, - doesn't match an existing page in WordPress. No message will display if - WordPress multisite is enabled. - ### Core CiviCRM - **Contact export is very slow, creates huge temporary tables @@ -231,246 +142,96 @@ Released September 2, 2020 is hidden from the extensions user interface because it is currently incompatible with the default theming framework. -## Bugs resolved - -### Wordpress Integration - -- **[civicrm.files] token doesn't work in some cases since 5.27 - ([dev/wordpress#66](https://lab.civicrm.org/dev/wordpress/-/issues/66): - [18068](https://github.com/civicrm/civicrm-core/pull/18068))** - - Ensures the [civicrm.files] token plays nice for sites with older wordpress - file directory set ups. - -- **CiviCRM and the WordPress Pods plugin (since version 2.7.13) is incompatible - due to Pods including marionette v3.3.1 for backbone, newer than CiviCRM's - bundled marionette v1.0.3 - ([dev/core#1090](https://lab.civicrm.org/dev/core/-/issues/1090): - [17855](https://github.com/civicrm/civicrm-core/pull/17855))** - - Ensures CiviCRM plays nicely with recent versions of Elementor Plugin. - -- **Fix PHP notice on wordpress permissions form - ([17758](https://github.com/civicrm/civicrm-core/pull/17758))** - -- **Slow down the frequency of WordPress "heartbeat" calls in CiviCRM admin - ([dev/wordpress#67](https://lab.civicrm.org/dev/wordpress/-/issues/67): - [214](https://github.com/civicrm/civicrm-wordpress/pull/214))** - -### Drupal Integration - -- **Drupal 8 - Using Create User Record action on a contact with no email is too - quiet. Also CRM_Core_Session::setStatus is sometimes ignored. - ([dev/drupal#127](https://lab.civicrm.org/dev/drupal/-/issues/127): - [17914](https://github.com/civicrm/civicrm-core/pull/17914) and - [17915](https://github.com/civicrm/civicrm-core/pull/17915))** - - Require email when adding a user and ensure that errors show. - -- **The following PHP variables are not set: $_SERVER[HTTP_HOST] - ([dev/core#750](https://lab.civicrm.org/dev/core/-/issues/750): - [17636](https://github.com/civicrm/civicrm-core/pull/17636))** - - Skip the server variable checks if running in a CLI environment, removing an - error when running Drush commands against Drupal 8 and Drupal 9 based sites. - -- **Can't find recaptcha in Drupal 8 - ([dev/core#1871](https://lab.civicrm.org/dev/core/-/issues/1871): - [17822](https://github.com/civicrm/civicrm-core/pull/17822))** - -- **Fixed for multi-select filter - ([615](https://github.com/civicrm/civicrm-drupal/pull/615))** - - This fixes filters for multi-select fields in Views. - -- **fix url for file field - ([608](https://github.com/civicrm/civicrm-drupal/pull/608))** - - This fixes the generated URL for file fields in Views. - -### CiviCase - -- **Fix case activity field set to allow long details to be exported - ([17970](https://github.com/civicrm/civicrm-core/pull/17970))** - -- **Case export has two fields that are not what they say they are - ([dev/core#1916](https://lab.civicrm.org/dev/core/-/issues/1916): - [18043](https://github.com/civicrm/civicrm-core/pull/18043))** - -- **Collapsed custom field set for activities with a required radio makes case - activity buttons seem disabled - ([dev/core#1928](https://lab.civicrm.org/dev/core/-/issues/1928): - [18080](https://github.com/civicrm/civicrm-core/pull/18080))** - -- **CRM_Utils_Check_Component_Case - Guard against post-upgrade crash - ([17944](https://github.com/civicrm/civicrm-core/pull/17944))** - ### CiviContribute -- **Define the logic that sets (or not) contribution receive_date in relation to - payments - ([dev/financial#139](https://lab.civicrm.org/dev/financial/-/issues/139): - [17777](https://github.com/civicrm/civicrm-core/pull/17777) and - [18000](https://github.com/civicrm/civicrm-core/pull/18000))** - - This undoes a regression where the contribution date of a pay-later - contribution would change when a payment completes the contribution. The - contribution date is the accrual date and is distinct from the date the - contribution is entered and the date when all payments have been received. - - In the process, this changes the "Update pending contribution status" search - action to be "Record payments for contributions". The result is generally the - same, but the status change comes about because the payments complete the - contributions. You can modify contribution statuses in bulk with a profile by - using the "Update multiple contributions" action. - -- **Disable frequency/interval fields if not required. Mark required if they are - so they are validated before submit - ([17526](https://github.com/civicrm/civicrm-core/pull/17526))** - - The frequency and interval fields for recurring contributions now use jQuery - validation to disable or require them as appropriate. - -- **Paypal IPN sometimes fails to update the next scheduled payment date when - recording the latest recurring payment - ([dev/core#1679](https://lab.civicrm.org/dev/core/-/issues/1679): - [17744](https://github.com/civicrm/civicrm-core/pull/17744))** - -- **Contribution Details Statistics are multiplied under many circumstances - ([dev/report#21](https://lab.civicrm.org/dev/report/-/issues/21): - [15435](https://github.com/civicrm/civicrm-core/pull/15435) and - [17809](https://github.com/civicrm/civicrm-core/pull/17809))** - -- **Contribution Summary Report: The "general total" row does not take the - currency filtered - ([dev/report#27](https://lab.civicrm.org/dev/report/-/issues/27): - [16736](https://github.com/civicrm/civicrm-core/pull/16736))** - - When the contribution summary report is used by filtering for a currency other - than the site's default currency, the "grand total" row shows the sign of the - default currency instead of the filtered currency. - -- **Fix PaypalIPN single function to not receive-by-reference - ([18044](https://github.com/civicrm/civicrm-core/pull/18044))** - -- **Allow Failed -> Completed status for contributions - ([dev/core#1906](https://lab.civicrm.org/dev/core/-/issues/1906): - [17943](https://github.com/civicrm/civicrm-core/pull/17943))** - -- **Fix currency symbol for Total Amount on contribution page - ([17703](https://github.com/civicrm/civicrm-core/pull/17703))** - - This resolves a but where the total amount would show the default system - currency symbol regardless of the currency configured on the contribution - page. - -- **On behalf label / Honoree Title / Honoree Description not translatable on - contribution page - ([dev/core#1280](https://lab.civicrm.org/dev/core/-/issues/1280): - [16838](https://github.com/civicrm/civicrm-core/pull/16838))** - -- **Load contribution page if live payment processor is disabled but test is - available ([17828](https://github.com/civicrm/civicrm-core/pull/17828))** - -- **Remove requirement to pass 'contribution_status_id' => Pending from - order.create ([18018](https://github.com/civicrm/civicrm-core/pull/18018))** - -- **Use saved contribution's line items rather than the primaryContributionID - ([18033](https://github.com/civicrm/civicrm-core/pull/18033))** - -- **Do not overwrite values saved from the repeatContribution routine - ([17972](https://github.com/civicrm/civicrm-core/pull/17972))** - -- **"Contribution Source" profile field has no effect on new contribution - ([dev/core#1902](https://lab.civicrm.org/dev/core/-/issues/1902): - [17930](https://github.com/civicrm/civicrm-core/pull/17930))** - -- **Incorrect check for "soft_credit" after making pcp donation - ([dev/core#1915](https://lab.civicrm.org/dev/core/-/issues/1915): - [18002](https://github.com/civicrm/civicrm-core/pull/18002))** +- **Add configure and priceset url icons on public contribution & event pages + ([dev/core#1905](https://lab.civicrm.org/dev/core/-/issues/1905): + [17942](https://github.com/civicrm/civicrm-core/pull/17942), + [18088](https://github.com/civicrm/civicrm-core/pull/18088) and + [18064](https://github.com/civicrm/civicrm-core/pull/18064))** -- **Total Tax Amount on the Contribution (generated in backoffice/offline) no - longer adds up to sum of the Tax Amount for the individual line items - ([dev/core#1983](https://lab.civicrm.org/dev/core/-/issues/1983): - [18290](https://github.com/civicrm/civicrm-core/pull/18290))** + A "Configure" button now appears to administrators when viewing a contribution + page or event, or a priceset within either, from the frontend. This links to + the corresponding form to administer the contribution page, event, or + priceset. -- **Downloaded Invoice activity attaches non wkhtmltopdf invoice - ([dev/core#1922](https://lab.civicrm.org/dev/core/-/issues/1922): - [18056](https://github.com/civicrm/civicrm-core/pull/18056))** +### CiviEvent - Downloading an invoice would ignore whether wkhtmltopdf was configured and - always use dompdf. +- **Move Event Cart to extension + ([17741](https://github.com/civicrm/civicrm-core/pull/17741), + [17743](https://github.com/civicrm/civicrm-core/pull/17743), + [17861](https://github.com/civicrm/civicrm-core/pull/17861), + [17884](https://github.com/civicrm/civicrm-core/pull/17884), + [17885](https://github.com/civicrm/civicrm-core/pull/17885), and + [17891](https://github.com/civicrm/civicrm-core/pull/17891))** -- **Fix repeattransaction api to use custom data from the template contribution - ([17975](https://github.com/civicrm/civicrm-core/pull/17975))** + The event cart features have been moved to a separate extension that is + shipped with core. - The Contribution.repeattransaction API will now copy custom data from the - template rather than the earliest related contribution. +### CiviMember -### CiviEvent +- **Option to update expired memberships as part of the job.process_membership + ([dev/membership#18](https://lab.civicrm.org/dev/membership/-/issues/18): + [16298](https://github.com/civicrm/civicrm-core/pull/16298))** -- **Can't meaningfully disable self-service transfer/cancellation once enabled - ([dev/event#35](https://lab.civicrm.org/dev/event/-/issues/35): - [18040](https://github.com/civicrm/civicrm-core/pull/18040))** + Improves the Job.process_membership API/Scheduled job by adding some new + parameters, specifically: -- **Event registration form has inconsistent labeling - ([dev/event#38](https://lab.civicrm.org/dev/event/-/issues/38): - [17695](https://github.com/civicrm/civicrm-core/pull/17695))** + - exclude_test_memberships: Exclude test memberships from calculations + (default = TRUE) + - only_active_membership_types: Exclude disabled membership types from + calculations (default = TRUE) + - exclude_membership_status_ids: Default: Exclude Pending, Cancelled, + Expired. -- **Event cart hidden/enabled in buildkit 5.29 RC - ([dev/event#40](https://lab.civicrm.org/dev/event/-/issues/40): - [18101](https://github.com/civicrm/civicrm-core/pull/18101))** + Deceased will always be excluded - Ensures that the Event Cart functionality defaults to off. +- **Add auto-renew status to membership detail report + ([17683](https://github.com/civicrm/civicrm-core/pull/17683) and + [17911](https://github.com/civicrm/civicrm-core/pull/17911))** -### CiviMail + The membership detail report can now display and filter on the auto-renew + status of each membership. -- **Remove url-tracking in mass SMS - ([dev/core#1843](https://lab.civicrm.org/dev/core/-/issues/1843): - [17700](https://github.com/civicrm/civicrm-core/pull/17700))** +### Backdrop Integration - Rewritten URLs were confusing and made SMS messages unnecessarily long. There - also have never been SMS tracking reports to use them anyway. +- **Support for installing CiviCRM-Backdrop via "setup" UI + ([17749](https://github.com/civicrm/civicrm-core/pull/17749) and + [121](https://github.com/civicrm/civicrm-backdrop/pull/121))** -- **Test mailings create new contacts even when "Add Contacts" permission is not - present. ([dev/mail#70](https://lab.civicrm.org/dev/mail/-/issues/70): - [17867](https://github.com/civicrm/civicrm-core/pull/17867))** + Improves the installation and Setup process for Backdrop integration's so that + one enables the CiviCRM module like any other module and then is directed to + the setup screen to complete installation as opposed to having to navigate to + a specific link. - Ensures contacts are not created when a user without permissions to create - contacts sends a test email. +### Drupal Integration -- **Mailing Subscription form does not validate reCaptcha - ([dev/core#1755](https://lab.civicrm.org/dev/core/-/issues/1755): - [17305](https://github.com/civicrm/civicrm-core/pull/17305))** +- **Upgrading a site that still has "mysql" in the dsn string breaks in latest + master ([dev/core#1937](https://lab.civicrm.org/dev/core/-/issues/1937): + [18174](https://github.com/civicrm/civicrm-core/pull/18174))** -- **Text version of unsubscribed email is missing the link to resubscribe - ([dev/core#1919](https://lab.civicrm.org/dev/core/-/issues/1919): - [18015](https://github.com/civicrm/civicrm-core/pull/18015))** + This adds an upgrade message for 5.29.0 warning that Composer patching will + need to be enabled before upgrading to 5.30.0 or higher. -- **Add CiviMail synchronisation frequency setting - (in support of [dev/core#1768](https://lab.civicrm.org/dev/core/-/issues/1768): - [17709](https://github.com/civicrm/civicrm-core/pull/17709))** + The "mysql" in the DSN (as opposed to "mysqli") will not be a problem, but the + solution for this is in a package patch that appears in 5.30. - This adds a setting for updating how frequently the database should be updated - with the status of emails that have been sent. Updating the database more - frequently can slow the process down, but it reduces the likelihood that a - recipient will be re-emailed when recovering from a stalled mailing. +- **Installation - Support "activate first" w/setup UI + ([606](https://github.com/civicrm/civicrm-drupal/pull/606))** -- **Make new email open and url routes 'public' - ([17813](https://github.com/civicrm/civicrm-core/pull/17813))** + The new setup user interface for Backdrop and WordPress installations is now + available for Drupal 7. - This denotes the paths for open and URL tracking as "public" pages as they are - meant to be used by unauthenticated email clients and recipients. +### WordPress Integration -### CiviMember +- **Add system check to ensure WP base page exists + ([17698](https://github.com/civicrm/civicrm-core/pull/17698))** -- **Decimal Separator - Invalid value "total_amount" (NaN,N) creating or editing - a membership ([dev/core#1113](https://lab.civicrm.org/dev/core/-/issues/1113): - [16429](https://github.com/civicrm/civicrm-core/pull/16429))** + A new system check will display a message if the base page, as set in CiviCRM, + doesn't match an existing page in WordPress. No message will display if + WordPress multisite is enabled. -- **Offline Membership Renewal Tax Calculation is incorrect [regression] - ([dev/core#1972](https://lab.civicrm.org/dev/core/-/issues/1972): - [18271](https://github.com/civicrm/civicrm-core/pull/18271))** +## Bugs resolved ### Core CiviCRM @@ -706,6 +467,245 @@ Released September 2, 2020 - **Fix JQuery Validation for radios ([17937](https://github.com/civicrm/civicrm-core/pull/17937))** +### CiviCase + +- **Fix case activity field set to allow long details to be exported + ([17970](https://github.com/civicrm/civicrm-core/pull/17970))** + +- **Case export has two fields that are not what they say they are + ([dev/core#1916](https://lab.civicrm.org/dev/core/-/issues/1916): + [18043](https://github.com/civicrm/civicrm-core/pull/18043))** + +- **Collapsed custom field set for activities with a required radio makes case + activity buttons seem disabled + ([dev/core#1928](https://lab.civicrm.org/dev/core/-/issues/1928): + [18080](https://github.com/civicrm/civicrm-core/pull/18080))** + +- **CRM_Utils_Check_Component_Case - Guard against post-upgrade crash + ([17944](https://github.com/civicrm/civicrm-core/pull/17944))** + +### CiviContribute + +- **Define the logic that sets (or not) contribution receive_date in relation to + payments + ([dev/financial#139](https://lab.civicrm.org/dev/financial/-/issues/139): + [17777](https://github.com/civicrm/civicrm-core/pull/17777) and + [18000](https://github.com/civicrm/civicrm-core/pull/18000))** + + This undoes a regression where the contribution date of a pay-later + contribution would change when a payment completes the contribution. The + contribution date is the accrual date and is distinct from the date the + contribution is entered and the date when all payments have been received. + + In the process, this changes the "Update pending contribution status" search + action to be "Record payments for contributions". The result is generally the + same, but the status change comes about because the payments complete the + contributions. You can modify contribution statuses in bulk with a profile by + using the "Update multiple contributions" action. + +- **Disable frequency/interval fields if not required. Mark required if they are + so they are validated before submit + ([17526](https://github.com/civicrm/civicrm-core/pull/17526))** + + The frequency and interval fields for recurring contributions now use jQuery + validation to disable or require them as appropriate. + +- **Paypal IPN sometimes fails to update the next scheduled payment date when + recording the latest recurring payment + ([dev/core#1679](https://lab.civicrm.org/dev/core/-/issues/1679): + [17744](https://github.com/civicrm/civicrm-core/pull/17744))** + +- **Contribution Details Statistics are multiplied under many circumstances + ([dev/report#21](https://lab.civicrm.org/dev/report/-/issues/21): + [15435](https://github.com/civicrm/civicrm-core/pull/15435) and + [17809](https://github.com/civicrm/civicrm-core/pull/17809))** + +- **Contribution Summary Report: The "general total" row does not take the + currency filtered + ([dev/report#27](https://lab.civicrm.org/dev/report/-/issues/27): + [16736](https://github.com/civicrm/civicrm-core/pull/16736))** + + When the contribution summary report is used by filtering for a currency other + than the site's default currency, the "grand total" row shows the sign of the + default currency instead of the filtered currency. + +- **Fix PaypalIPN single function to not receive-by-reference + ([18044](https://github.com/civicrm/civicrm-core/pull/18044))** + +- **Allow Failed -> Completed status for contributions + ([dev/core#1906](https://lab.civicrm.org/dev/core/-/issues/1906): + [17943](https://github.com/civicrm/civicrm-core/pull/17943))** + +- **Fix currency symbol for Total Amount on contribution page + ([17703](https://github.com/civicrm/civicrm-core/pull/17703))** + + This resolves a but where the total amount would show the default system + currency symbol regardless of the currency configured on the contribution + page. + +- **On behalf label / Honoree Title / Honoree Description not translatable on + contribution page + ([dev/core#1280](https://lab.civicrm.org/dev/core/-/issues/1280): + [16838](https://github.com/civicrm/civicrm-core/pull/16838))** + +- **Load contribution page if live payment processor is disabled but test is + available ([17828](https://github.com/civicrm/civicrm-core/pull/17828))** + +- **Remove requirement to pass 'contribution_status_id' => Pending from + order.create ([18018](https://github.com/civicrm/civicrm-core/pull/18018))** + +- **Use saved contribution's line items rather than the primaryContributionID + ([18033](https://github.com/civicrm/civicrm-core/pull/18033))** + +- **Do not overwrite values saved from the repeatContribution routine + ([17972](https://github.com/civicrm/civicrm-core/pull/17972))** + +- **"Contribution Source" profile field has no effect on new contribution + ([dev/core#1902](https://lab.civicrm.org/dev/core/-/issues/1902): + [17930](https://github.com/civicrm/civicrm-core/pull/17930))** + +- **Incorrect check for "soft_credit" after making pcp donation + ([dev/core#1915](https://lab.civicrm.org/dev/core/-/issues/1915): + [18002](https://github.com/civicrm/civicrm-core/pull/18002))** + +- **Total Tax Amount on the Contribution (generated in backoffice/offline) no + longer adds up to sum of the Tax Amount for the individual line items + ([dev/core#1983](https://lab.civicrm.org/dev/core/-/issues/1983): + [18290](https://github.com/civicrm/civicrm-core/pull/18290))** + +- **Downloaded Invoice activity attaches non wkhtmltopdf invoice + ([dev/core#1922](https://lab.civicrm.org/dev/core/-/issues/1922): + [18056](https://github.com/civicrm/civicrm-core/pull/18056))** + + Downloading an invoice would ignore whether wkhtmltopdf was configured and + always use dompdf. + +- **Fix repeattransaction api to use custom data from the template contribution + ([17975](https://github.com/civicrm/civicrm-core/pull/17975))** + + The Contribution.repeattransaction API will now copy custom data from the + template rather than the earliest related contribution. + +### CiviEvent + +- **Can't meaningfully disable self-service transfer/cancellation once enabled + ([dev/event#35](https://lab.civicrm.org/dev/event/-/issues/35): + [18040](https://github.com/civicrm/civicrm-core/pull/18040))** + +- **Event registration form has inconsistent labeling + ([dev/event#38](https://lab.civicrm.org/dev/event/-/issues/38): + [17695](https://github.com/civicrm/civicrm-core/pull/17695))** + +- **Event cart hidden/enabled in buildkit 5.29 RC + ([dev/event#40](https://lab.civicrm.org/dev/event/-/issues/40): + [18101](https://github.com/civicrm/civicrm-core/pull/18101))** + + Ensures that the Event Cart functionality defaults to off. + +### CiviMail + +- **Remove url-tracking in mass SMS + ([dev/core#1843](https://lab.civicrm.org/dev/core/-/issues/1843): + [17700](https://github.com/civicrm/civicrm-core/pull/17700))** + + Rewritten URLs were confusing and made SMS messages unnecessarily long. There + also have never been SMS tracking reports to use them anyway. + +- **Test mailings create new contacts even when "Add Contacts" permission is not + present. ([dev/mail#70](https://lab.civicrm.org/dev/mail/-/issues/70): + [17867](https://github.com/civicrm/civicrm-core/pull/17867))** + + Ensures contacts are not created when a user without permissions to create + contacts sends a test email. + +- **Mailing Subscription form does not validate reCaptcha + ([dev/core#1755](https://lab.civicrm.org/dev/core/-/issues/1755): + [17305](https://github.com/civicrm/civicrm-core/pull/17305))** + +- **Text version of unsubscribed email is missing the link to resubscribe + ([dev/core#1919](https://lab.civicrm.org/dev/core/-/issues/1919): + [18015](https://github.com/civicrm/civicrm-core/pull/18015))** + +- **Add CiviMail synchronisation frequency setting + (in support of [dev/core#1768](https://lab.civicrm.org/dev/core/-/issues/1768): + [17709](https://github.com/civicrm/civicrm-core/pull/17709))** + + This adds a setting for updating how frequently the database should be updated + with the status of emails that have been sent. Updating the database more + frequently can slow the process down, but it reduces the likelihood that a + recipient will be re-emailed when recovering from a stalled mailing. + +- **Make new email open and url routes 'public' + ([17813](https://github.com/civicrm/civicrm-core/pull/17813))** + + This denotes the paths for open and URL tracking as "public" pages as they are + meant to be used by unauthenticated email clients and recipients. + +### CiviMember + +- **Decimal Separator - Invalid value "total_amount" (NaN,N) creating or editing + a membership ([dev/core#1113](https://lab.civicrm.org/dev/core/-/issues/1113): + [16429](https://github.com/civicrm/civicrm-core/pull/16429))** + +- **Offline Membership Renewal Tax Calculation is incorrect [regression] + ([dev/core#1972](https://lab.civicrm.org/dev/core/-/issues/1972): + [18271](https://github.com/civicrm/civicrm-core/pull/18271))** + +### Drupal Integration + +- **Drupal 8 - Using Create User Record action on a contact with no email is too + quiet. Also CRM_Core_Session::setStatus is sometimes ignored. + ([dev/drupal#127](https://lab.civicrm.org/dev/drupal/-/issues/127): + [17914](https://github.com/civicrm/civicrm-core/pull/17914) and + [17915](https://github.com/civicrm/civicrm-core/pull/17915))** + + Require email when adding a user and ensure that errors show. + +- **The following PHP variables are not set: $_SERVER[HTTP_HOST] + ([dev/core#750](https://lab.civicrm.org/dev/core/-/issues/750): + [17636](https://github.com/civicrm/civicrm-core/pull/17636))** + + Skip the server variable checks if running in a CLI environment, removing an + error when running Drush commands against Drupal 8 and Drupal 9 based sites. + +- **Can't find recaptcha in Drupal 8 + ([dev/core#1871](https://lab.civicrm.org/dev/core/-/issues/1871): + [17822](https://github.com/civicrm/civicrm-core/pull/17822))** + +- **Fixed for multi-select filter + ([615](https://github.com/civicrm/civicrm-drupal/pull/615))** + + This fixes filters for multi-select fields in Views. + +- **fix url for file field + ([608](https://github.com/civicrm/civicrm-drupal/pull/608))** + + This fixes the generated URL for file fields in Views. + +### Wordpress Integration + +- **[civicrm.files] token doesn't work in some cases since 5.27 + ([dev/wordpress#66](https://lab.civicrm.org/dev/wordpress/-/issues/66): + [18068](https://github.com/civicrm/civicrm-core/pull/18068))** + + Ensures the [civicrm.files] token plays nice for sites with older wordpress + file directory set ups. + +- **CiviCRM and the WordPress Pods plugin (since version 2.7.13) is incompatible + due to Pods including marionette v3.3.1 for backbone, newer than CiviCRM's + bundled marionette v1.0.3 + ([dev/core#1090](https://lab.civicrm.org/dev/core/-/issues/1090): + [17855](https://github.com/civicrm/civicrm-core/pull/17855))** + + Ensures CiviCRM plays nicely with recent versions of Elementor Plugin. + +- **Fix PHP notice on wordpress permissions form + ([17758](https://github.com/civicrm/civicrm-core/pull/17758))** + +- **Slow down the frequency of WordPress "heartbeat" calls in CiviCRM admin + ([dev/wordpress#67](https://lab.civicrm.org/dev/wordpress/-/issues/67): + [214](https://github.com/civicrm/civicrm-wordpress/pull/214))** + ## Miscellany - **APIv4 - Add BasicEntity helper class From c48faf40e3e00c0d6c6e3ccc43ff9fa7e2241998 Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 3 Sep 2020 10:06:24 +1200 Subject: [PATCH 085/834] Remove deprecated handling for partial_amount_to_pay We added deprecation notices back in Feb when we stopped using these params in core. They were never truly supported for extensions but we deprecated in case someone was doing something odd. However, it's 7 months on so time to cleanup --- CRM/Contribute/BAO/Contribution.php | 69 ----------------------------- 1 file changed, 69 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index bc56e48995f8..2c3a56d7e938 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -149,18 +149,6 @@ public static function add(&$params, $ids = []) { } $setPrevContribution = TRUE; - // CRM-13964 partial payment - if (!empty($params['partial_payment_total']) && !empty($params['partial_amount_to_pay'])) { - CRM_Core_Error::deprecatedFunctionWarning('partial_amount params are deprecated from Contribution.create - use Payment.create'); - $partialAmtTotal = $params['partial_payment_total']; - $partialAmtPay = $params['partial_amount_to_pay']; - $params['total_amount'] = $partialAmtTotal; - if ($partialAmtPay < $partialAmtTotal) { - $params['contribution_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Partially paid'); - $params['is_pay_later'] = 0; - $setPrevContribution = FALSE; - } - } if ($contributionID && $setPrevContribution) { $params['prevContribution'] = self::getOriginalContribution($contributionID); } @@ -3569,47 +3557,6 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = } $statusId = $params['contribution']->contribution_status_id; - // CRM-13964 partial payment - if ($contributionStatus == 'Partially paid' - && !empty($params['partial_payment_total']) && !empty($params['partial_amount_to_pay']) - ) { - CRM_Core_Error::deprecatedFunctionWarning('partial_amount params are deprecated from Contribution.create - use Payment.create'); - $partialAmtPay = CRM_Utils_Rule::cleanMoney($params['partial_amount_to_pay']); - $partialAmtTotal = CRM_Utils_Rule::cleanMoney($params['partial_payment_total']); - - $fromFinancialAccountId = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship($params['financial_type_id'], 'Accounts Receivable Account is'); - $statusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); - $params['total_amount'] = $partialAmtPay; - - $balanceTrxnInfo = CRM_Core_BAO_FinancialTrxn::getBalanceTrxnAmt($params['contribution']->id, $params['financial_type_id']); - if (empty($balanceTrxnInfo['trxn_id'])) { - // create new balance transaction record - $toFinancialAccount = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship($params['financial_type_id'], 'Accounts Receivable Account is'); - - $balanceTrxnParams['total_amount'] = $partialAmtTotal; - $balanceTrxnParams['to_financial_account_id'] = $toFinancialAccount; - $balanceTrxnParams['contribution_id'] = $params['contribution']->id; - $balanceTrxnParams['trxn_date'] = !empty($params['contribution']->receive_date) ? $params['contribution']->receive_date : date('YmdHis'); - $balanceTrxnParams['fee_amount'] = $params['fee_amount'] ?? NULL; - $balanceTrxnParams['net_amount'] = $params['net_amount'] ?? NULL; - $balanceTrxnParams['currency'] = $params['contribution']->currency; - $balanceTrxnParams['trxn_id'] = $params['contribution']->trxn_id; - $balanceTrxnParams['status_id'] = $statusId; - $balanceTrxnParams['payment_instrument_id'] = $params['contribution']->payment_instrument_id; - $balanceTrxnParams['check_number'] = $params['check_number'] ?? NULL; - $balanceTrxnParams['pan_truncation'] = $params['pan_truncation'] ?? NULL; - $balanceTrxnParams['card_type_id'] = $params['card_type_id'] ?? NULL; - if (!empty($balanceTrxnParams['from_financial_account_id']) && - ($statusId == array_search('Completed', $contributionStatuses) || $statusId == array_search('Partially paid', $contributionStatuses)) - ) { - $balanceTrxnParams['is_payment'] = 1; - } - if (!empty($params['payment_processor'])) { - $balanceTrxnParams['payment_processor_id'] = $params['payment_processor']; - } - $financialTxn = CRM_Core_BAO_FinancialTrxn::create($balanceTrxnParams); - } - } // build line item array if its not set in $params if (empty($params['line_item']) || $additionalParticipantId) { @@ -4392,22 +4339,6 @@ public static function updateRelatedPledge( } } - /** - * Compute the stats values - * - * @param string $stat either 'mode' or 'median' - * @param string $sql - * @param string $alias of civicrm_contribution - * - * @return array|null - * @deprecated - * - */ - public static function computeStats($stat, $sql, $alias = NULL) { - CRM_Core_Error::deprecatedFunctionWarning('computeStats is now deprecated'); - return []; - } - /** * Is there only one line item attached to the contribution. * From fee6edcef7dd9f4f18bcc13f3beade740b1794e9 Mon Sep 17 00:00:00 2001 From: Pradeep Nayak Date: Thu, 3 Sep 2020 00:19:19 +0100 Subject: [PATCH 086/834] Do not block user incase 'Require approval' is checked --- CRM/Utils/System/Drupal8.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CRM/Utils/System/Drupal8.php b/CRM/Utils/System/Drupal8.php index 78479501e3ab..feb6dc58e5a7 100644 --- a/CRM/Utils/System/Drupal8.php +++ b/CRM/Utils/System/Drupal8.php @@ -49,7 +49,7 @@ public function createUser(&$params, $mail) { if ($user_register_conf != 'visitors' && !$user->hasPermission('administer users')) { $account->block(); } - elseif (!$verify_mail_conf) { + elseif ($verify_mail_conf) { $account->activate(); } @@ -98,7 +98,7 @@ public function createUser(&$params, $mail) { } // If this is a user creating their own account, login them in! - if ($account->isActive() && $user->isAnonymous()) { + if (!$verify_mail_conf && $account->isActive() && $user->isAnonymous()) { \user_login_finalize($account); } From 11eed11026ff92154af770dc3743d0ad617a244e Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 2 Sep 2020 16:34:27 -0700 Subject: [PATCH 087/834] dev/wordpress#73 - Be more forgiving about slash in wpBasePage Overview -------- The correct functioning of `wpBasePage` depends on whether the administrator omitted or added a trailing slash. This is apparent on URLs with a trailing slash, eg ``` http://wpmaster.bknix:8001/civicrm/contribute/transact/?reset=1&id=1 ``` Make it less sensitive. Before ------ URL is OK with default setting `wpBasePage=civicrm` URL fails with setting `wpBasePage=civicrm/` After ----- URL is OK with either setting `wpBasePage=civicrm` or `wpBasePage=civicrm/` Technical Details ----------------- I believe the old symptom arose because of the formula which produces the WP rewrite rules (`'^' . $config->wpBasePage . '/([^?]*)?'`) expects that there is no trailing slash. You could theoretically patch there, but this seems like it'll provide more thorough normalization. When testing, the results affect the WP rewrite rules, so you may need to be particularly aggressive about clearing caches whenever you make a change in code or settings, eg ``` wp cache flush ; cv flush; wp rewrite flush ``` --- CRM/Core/Config/MagicMerge.php | 2 +- CRM/Utils/System/WordPress.php | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CRM/Core/Config/MagicMerge.php b/CRM/Core/Config/MagicMerge.php index 88110b643209..6050c22a2dac 100644 --- a/CRM/Core/Config/MagicMerge.php +++ b/CRM/Core/Config/MagicMerge.php @@ -170,7 +170,6 @@ public static function getPropertyMap() { 'userFrameworkUsersTableName' => ['setting'], 'verpSeparator' => ['setting'], 'wkhtmltopdfPath' => ['setting'], - 'wpBasePage' => ['setting'], 'wpLoadPhp' => ['setting'], // "path" properties are managed via Civi::paths and $civicrm_paths @@ -204,6 +203,7 @@ public static function getPropertyMap() { // @todo remove geocodeMethod. As of Feb 2018, $config->geocodeMethod works but gives a deprecation warning. 'geocodeMethod' => ['callback', 'CRM_Utils_Geocode', 'getProviderClass'], 'defaultCurrencySymbol' => ['callback', 'CRM_Core_BAO_Country', 'getDefaultCurrencySymbol'], + 'wpBasePage' => ['callback', 'CRM_Utils_System_WordPress', 'getBasePage'], ]; } diff --git a/CRM/Utils/System/WordPress.php b/CRM/Utils/System/WordPress.php index 4e86eb89bf9f..61f499bd4bd6 100644 --- a/CRM/Utils/System/WordPress.php +++ b/CRM/Utils/System/WordPress.php @@ -20,6 +20,13 @@ */ class CRM_Utils_System_WordPress extends CRM_Utils_System_Base { + /** + * Get a normalized version of the wpBasePage. + */ + public static function getBasePage() { + return rtrim(Civi::settings()->get('wpBasePage'), '/'); + } + /** */ public function __construct() { From e4ee9e7bf80ff79bf25012b08f38d7e9c24fc4be Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 2 Sep 2020 18:20:30 -0700 Subject: [PATCH 088/834] 5.29.0.md - Last minute addition --- release-notes/5.29.0.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/release-notes/5.29.0.md b/release-notes/5.29.0.md index bae0454eb091..a444e0e9593d 100644 --- a/release-notes/5.29.0.md +++ b/release-notes/5.29.0.md @@ -706,6 +706,9 @@ Released September 2, 2020 ([dev/wordpress#67](https://lab.civicrm.org/dev/wordpress/-/issues/67): [214](https://github.com/civicrm/civicrm-wordpress/pull/214))** +- **Be more forgiving about slash in wpBasePage ([dev/wordpress#73](https://lab.civicrm.org/dev/wordpress/issues/73): + [18332](https://github.com/civicrm/civicrm-core/pull/18332))** + ## Miscellany - **APIv4 - Add BasicEntity helper class From afea69496355e068aa0355b8c178a801e58faec3 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 2 Sep 2020 18:25:10 -0700 Subject: [PATCH 089/834] 5.29.0.md - Small copy-edits --- release-notes/5.29.0.md | 50 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/release-notes/5.29.0.md b/release-notes/5.29.0.md index a444e0e9593d..5548efbe9797 100644 --- a/release-notes/5.29.0.md +++ b/release-notes/5.29.0.md @@ -251,13 +251,13 @@ Released September 2, 2020 Duplicate matching now ignores location type for postal address fields, now consistent with how it has ignored phone and email location types. -- **Installing drupal 8 using civicrm-setup leads to "incorrect resource url" +- **Installing Drupal 8 using civicrm-setup leads to "incorrect resource url" system status check errors ([dev/drupal#114](https://lab.civicrm.org/dev/drupal/-/issues/114) and [dev/core#1647](https://lab.civicrm.org/dev/core/-/issues/1647): [17754](https://github.com/civicrm/civicrm-core/pull/17754))** - Removes the resource url status check. + Removes the resource URL status check. - **API4: 500 error on chain with custom field ([dev/core#1578](https://lab.civicrm.org/dev/core/-/issues/1578): @@ -346,7 +346,7 @@ Released September 2, 2020 since (in Drupal 8 and potentially elsewhere) they may not be within the CiviCRM root folder. -- **custom data insert/update error if using reserved words +- **Custom data insert/update error if using reserved words ([dev/core#1880](https://lab.civicrm.org/dev/core/-/issues/1880): [17848](https://github.com/civicrm/civicrm-core/pull/17848))** @@ -394,7 +394,7 @@ Released September 2, 2020 - **Simplify caching of status checks ([17817](https://github.com/civicrm/civicrm-core/pull/17817))** -- **ensure custom field checkboxes are populated in profiles +- **Ensure custom field checkboxes are populated in profiles ([17977](https://github.com/civicrm/civicrm-core/pull/17977))** - **Upgrade PEAR/mail_mime package to be compliant with PHP7.4 and deploy it @@ -604,7 +604,7 @@ Released September 2, 2020 ### CiviMail -- **Remove url-tracking in mass SMS +- **Remove URL-tracking in mass SMS ([dev/core#1843](https://lab.civicrm.org/dev/core/-/issues/1843): [17700](https://github.com/civicrm/civicrm-core/pull/17700))** @@ -618,7 +618,7 @@ Released September 2, 2020 Ensures contacts are not created when a user without permissions to create contacts sends a test email. -- **Mailing Subscription form does not validate reCaptcha +- **Mailing Subscription form does not validate reCAPTCHA ([dev/core#1755](https://lab.civicrm.org/dev/core/-/issues/1755): [17305](https://github.com/civicrm/civicrm-core/pull/17305))** @@ -635,7 +635,7 @@ Released September 2, 2020 frequently can slow the process down, but it reduces the likelihood that a recipient will be re-emailed when recovering from a stalled mailing. -- **Make new email open and url routes 'public' +- **Mark the new routes for "open.php" and "url.php" as public ([17813](https://github.com/civicrm/civicrm-core/pull/17813))** This denotes the paths for open and URL tracking as "public" pages as they are @@ -668,7 +668,7 @@ Released September 2, 2020 Skip the server variable checks if running in a CLI environment, removing an error when running Drush commands against Drupal 8 and Drupal 9 based sites. -- **Can't find recaptcha in Drupal 8 +- **Can't find reCAPTCHA in Drupal 8 ([dev/core#1871](https://lab.civicrm.org/dev/core/-/issues/1871): [17822](https://github.com/civicrm/civicrm-core/pull/17822))** @@ -677,7 +677,7 @@ Released September 2, 2020 This fixes filters for multi-select fields in Views. -- **fix url for file field +- **Fix URL for file field ([608](https://github.com/civicrm/civicrm-drupal/pull/608))** This fixes the generated URL for file fields in Views. @@ -688,7 +688,7 @@ Released September 2, 2020 ([dev/wordpress#66](https://lab.civicrm.org/dev/wordpress/-/issues/66): [18068](https://github.com/civicrm/civicrm-core/pull/18068))** - Ensures the [civicrm.files] token plays nice for sites with older wordpress + Ensures the [civicrm.files] token plays nice for sites with older WordPress file directory set ups. - **CiviCRM and the WordPress Pods plugin (since version 2.7.13) is incompatible @@ -699,7 +699,7 @@ Released September 2, 2020 Ensures CiviCRM plays nicely with recent versions of Elementor Plugin. -- **Fix PHP notice on wordpress permissions form +- **Fix PHP notice on WordPress permissions form ([17758](https://github.com/civicrm/civicrm-core/pull/17758))** - **Slow down the frequency of WordPress "heartbeat" calls in CiviCRM admin @@ -837,16 +837,16 @@ Released September 2, 2020 - **[REF] - Add helper function for the repetitive task of fetching multilingual ([17650](https://github.com/civicrm/civicrm-core/pull/17650))** -- **[Ref] Unit test attempt to create reported bugs , minor cleanup +- **[REF] Unit test attempt to create reported bugs , minor cleanup ([17560](https://github.com/civicrm/civicrm-core/pull/17560))** -- **REF Extract addToRecentItems from membership create +- **[REF] Extract addToRecentItems from membership create ([17524](https://github.com/civicrm/civicrm-core/pull/17524))** - **[REF] Fix a couple of jQuery errors that have cropped up ([17871](https://github.com/civicrm/civicrm-core/pull/17871))** -- **(REF) regen.sh - Remove unusual handling of `zipcodes.mysql` +- **[REF] regen.sh - Remove unusual handling of `zipcodes.mysql` ([17869](https://github.com/civicrm/civicrm-core/pull/17869))** - **[REF] ScheduledJob cleanup, remove unused var @@ -858,10 +858,10 @@ Released September 2, 2020 - **[REF] Only printOnly once ([17850](https://github.com/civicrm/civicrm-core/pull/17850))** -- **(REF) APIv4 ConformanceTest - Split apart into per-entity sub-tests +- **[REF] APIv4 ConformanceTest - Split apart into per-entity sub-tests ([17845](https://github.com/civicrm/civicrm-core/pull/17845))** -- **(REF) WebsiteTest - Mitigate flaky failures +- **[REF] WebsiteTest - Mitigate flaky failures ([17833](https://github.com/civicrm/civicrm-core/pull/17833))** - **[REF] Follow up cleanup @@ -876,19 +876,19 @@ Released September 2, 2020 - **[REF] Unused interface CRM_Report_Interface ([17767](https://github.com/civicrm/civicrm-core/pull/17767))** -- **REF - Cleanup StatusPreference BAO to be more standard +- **[REF] - Cleanup StatusPreference BAO to be more standard ([17801](https://github.com/civicrm/civicrm-core/pull/17801))** - **[REF] Reduce interaction between dedupe code and createProfileContact ([17920](https://github.com/civicrm/civicrm-core/pull/17920))** -- **[Ref] Simplify field reference +- **[REF] Simplify field reference ([17941](https://github.com/civicrm/civicrm-core/pull/17941))** - **[REF] [Test] Minor simplification on test ([18019](https://github.com/civicrm/civicrm-core/pull/18019))** -- **[Ref] Simplify is_email_receipt in sendMail +- **[REF] Simplify is_email_receipt in sendMail ([18029](https://github.com/civicrm/civicrm-core/pull/18029))** - **[REF] Remove transaction from BaseIPN completeTransaction call @@ -903,7 +903,7 @@ Released September 2, 2020 - **[REF] Remove transaction from completeOrder signature ([18046](https://github.com/civicrm/civicrm-core/pull/18046))** -- **[Ref] Remove transaction instantiation in PaypalPro +- **[REF] Remove transaction instantiation in PaypalPro ([18026](https://github.com/civicrm/civicrm-core/pull/18026))** - **[REF] Stop instantiating transaction in PaypalIPN @@ -915,7 +915,7 @@ Released September 2, 2020 - **[REF] Tighten up function signature for dedupePair ([17923](https://github.com/civicrm/civicrm-core/pull/17923))** -- **[Ref] Move noisily deprecate BaseIPN->sendMail, call api from it rather +- **[REF] Move noisily deprecate BaseIPN->sendMail, call api from it rather than BAO function ([17982](https://github.com/civicrm/civicrm-core/pull/17982))** @@ -993,7 +993,7 @@ Released September 2, 2020 - **[NFC] Fix provider unit test on PHP7.4 ([18073](https://github.com/civicrm/civicrm-core/pull/18073))** -- **NFC - Docblock cleanup +- **[NFC] Docblock cleanup ([610](https://github.com/civicrm/civicrm-drupal/pull/610))** - **[NFC] Update versions file to remove reference to Mail_mime and Mail @@ -1012,7 +1012,7 @@ Released September 2, 2020 - **[NFC] Improve docs for APIv4 Save action ([18004](https://github.com/civicrm/civicrm-core/pull/18004))** -- **NFC - Docblock cleanup +- **[NFC] Docblock cleanup ([17945](https://github.com/civicrm/civicrm-core/pull/17945))** - **[NFC] Update a few doc/wiki links in code comments @@ -1024,7 +1024,7 @@ Released September 2, 2020 - **[Test] Update hook signature in test ([609](https://github.com/civicrm/civicrm-drupal/pull/609))** -- **Test - attempt to replicate #17852 +- **[Test] Attempt to replicate #17852 ([18038](https://github.com/civicrm/civicrm-core/pull/18038))** - **[Test fix] We might need this to ensure really quick test runs don't fail @@ -1056,7 +1056,7 @@ Released September 2, 2020 - **[Test Framework] - Tests for report downloads ([17892](https://github.com/civicrm/civicrm-core/pull/17892))** -- **api_v3_TaxContributionPageTest fix - remove hard coded processor id +- **api_v3_TaxContributionPageTest - Remove hard coded processor id ([17860](https://github.com/civicrm/civicrm-core/pull/17860))** - **API tests - label versions in dataprovider versionThreeAndFour From 39edd3c66783f6fa1b33674f79877037f6a9d35f Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 3 Sep 2020 16:18:36 +1200 Subject: [PATCH 090/834] Remove pass-by-ref in PaypalProIPN::single This is called from 2 places. Neither use the values again & neither receive any pass-by-reference values themselves --- CRM/Core/Payment/PayPalProIPN.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CRM/Core/Payment/PayPalProIPN.php b/CRM/Core/Payment/PayPalProIPN.php index e5c58d3de7ec..024d845b427a 100644 --- a/CRM/Core/Payment/PayPalProIPN.php +++ b/CRM/Core/Payment/PayPalProIPN.php @@ -321,7 +321,7 @@ public function recur($input, $ids, $objects, $first) { * * @return void */ - public function single(&$input, &$ids, &$objects, $recur = FALSE, $first = FALSE) { + public function single($input, $ids, $objects, $recur = FALSE, $first = FALSE) { $contribution = &$objects['contribution']; // make sure the invoice is valid and matches what we have in the contribution record From fbebca7a718b7abe61c559d04a8354b9fd169b04 Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 3 Sep 2020 15:40:47 +1200 Subject: [PATCH 091/834] Add ability to enable query logging per process This allows us to run a command such as env CIVICRM_DEBUG_LOG_QUERY=1 drush cvapi Contact.get and the queries for that process (but not others) are logged. This facilitates a cleaner record of queries as it is just one process and more flexibility (and less risk of leaving it on by mistake) --- CRM/Core/Error.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/CRM/Core/Error.php b/CRM/Core/Error.php index ee418c306b8b..98d1a29ded92 100644 --- a/CRM/Core/Error.php +++ b/CRM/Core/Error.php @@ -600,13 +600,15 @@ public static function debug_log_message($message, $out = FALSE, $prefix = '', $ * @param string $string */ public static function debug_query($string) { - if (defined('CIVICRM_DEBUG_LOG_QUERY')) { - if (CIVICRM_DEBUG_LOG_QUERY === 'backtrace') { - CRM_Core_Error::backtrace($string, TRUE); - } - elseif (CIVICRM_DEBUG_LOG_QUERY) { - CRM_Core_Error::debug_var('Query', $string, TRUE, TRUE, 'sql_log', PEAR_LOG_DEBUG); - } + if (!defined('CIVICRM_DEBUG_LOG_QUERY')) { + // TODO: When its updated to support getenv(), call CRM_Utils_Constant::value('CIVICRM_DEBUG_LOG_QUERY', FALSE) + define('CIVICRM_DEBUG_LOG_QUERY', getenv('CIVICRM_DEBUG_LOG_QUERY')); + } + if (CIVICRM_DEBUG_LOG_QUERY === 'backtrace') { + CRM_Core_Error::backtrace($string, TRUE); + } + elseif (CIVICRM_DEBUG_LOG_QUERY) { + CRM_Core_Error::debug_var('Query', $string, TRUE, TRUE, 'sql_log', PEAR_LOG_DEBUG); } } From a2b357d9094c780ab97d6673ef9e88f24f4c273a Mon Sep 17 00:00:00 2001 From: Justin Freeman Date: Thu, 3 Sep 2020 18:03:28 +1000 Subject: [PATCH 092/834] CIVICRM-1555 Change wording on the Opt Out and Unsubscribe pages --- CRM/Mailing/Form/Optout.php | 6 +++--- CRM/Mailing/Form/Unsubscribe.php | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CRM/Mailing/Form/Optout.php b/CRM/Mailing/Form/Optout.php index 8514612e8153..e809d497b76f 100644 --- a/CRM/Mailing/Form/Optout.php +++ b/CRM/Mailing/Form/Optout.php @@ -47,7 +47,7 @@ public function preProcess() { public function buildQuickForm() { CRM_Utils_System::addHTMLHead(''); - CRM_Utils_System::setTitle(ts('Please Confirm Your Opt Out')); + CRM_Utils_System::setTitle(ts('Opt Out Confirmation')); $this->add('text', 'email_confirm', ts('Verify email address to opt out:')); $this->addRule('email_confirm', ts('Email address is required to opt out.'), 'required'); @@ -89,7 +89,7 @@ public function postProcess() { CRM_Mailing_Event_BAO_Unsubscribe::send_unsub_response($queue_id, NULL, TRUE, $job_id); } - $statusMsg = ts('Email: %1 has been successfully opted out', + $statusMsg = ts('%1 opt out confirmed.', [1 => $values['email_confirm']] ); @@ -97,7 +97,7 @@ public function postProcess() { } elseif ($result == FALSE) { // Email address not verified - $statusMsg = ts('The email address: %1 you have entered does not match the email associated with this opt out request.', + $statusMsg = ts('%1 is not associated with this opt out request.', [1 => $values['email_confirm']] ); diff --git a/CRM/Mailing/Form/Unsubscribe.php b/CRM/Mailing/Form/Unsubscribe.php index 95a675965955..c63263ba0b9e 100644 --- a/CRM/Mailing/Form/Unsubscribe.php +++ b/CRM/Mailing/Form/Unsubscribe.php @@ -61,7 +61,7 @@ public function preProcess() { } } if (!$groupExist) { - $statusMsg = ts('Email: %1 has been successfully unsubscribed from this Mailing List/Group.', + $statusMsg = ts('%1 has been unsubscribed.', [1 => $email] ); CRM_Core_Session::setStatus($statusMsg, '', 'error'); @@ -72,7 +72,7 @@ public function preProcess() { public function buildQuickForm() { CRM_Utils_System::addHTMLHead(''); - CRM_Utils_System::setTitle(ts('Please Confirm Your Unsubscribe from this Mailing/Group')); + CRM_Utils_System::setTitle(ts('Unsubscribe Confirmation')); $this->add('text', 'email_confirm', ts('Verify email address to unsubscribe:')); $this->addRule('email_confirm', ts('Email address is required to unsubscribe.'), 'required'); @@ -114,7 +114,7 @@ public function postProcess() { CRM_Mailing_Event_BAO_Unsubscribe::send_unsub_response($queue_id, $groups, FALSE, $job_id); } - $statusMsg = ts('Email: %1 has been successfully unsubscribed from this Mailing List/Group.', + $statusMsg = ts('%1 is unsubscribed.', [1 => $values['email_confirm']] ); @@ -122,7 +122,7 @@ public function postProcess() { } elseif ($result == FALSE) { // Email address not verified - $statusMsg = ts('The email address: %1 you have entered does not match the email associated with this unsubscribe request.', + $statusMsg = ts('%1 is not associated with this unsubscribe request.', [1 => $values['email_confirm']] ); From 878715d231d61d7ba1f34a5200990dd512e456ae Mon Sep 17 00:00:00 2001 From: CiviCRM Date: Thu, 3 Sep 2020 09:05:29 +0000 Subject: [PATCH 093/834] Set version to 5.30.beta1 --- CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl | 1 + sql/civicrm_generated.mysql | 2 +- sql/test_data_second_domain.mysql | 2 +- xml/version.xml | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl diff --git a/CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl b/CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl new file mode 100644 index 000000000000..9c22ccacf110 --- /dev/null +++ b/CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl @@ -0,0 +1 @@ +{* file to handle db changes in 5.30.beta1 during upgrade *} diff --git a/sql/civicrm_generated.mysql b/sql/civicrm_generated.mysql index cdb7b1a269d9..4afb6f03fd1e 100644 --- a/sql/civicrm_generated.mysql +++ b/sql/civicrm_generated.mysql @@ -399,7 +399,7 @@ UNLOCK TABLES; LOCK TABLES `civicrm_domain` WRITE; /*!40000 ALTER TABLE `civicrm_domain` DISABLE KEYS */; -INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,'5.30.alpha1',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}'); +INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,'5.30.beta1',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}'); /*!40000 ALTER TABLE `civicrm_domain` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/test_data_second_domain.mysql b/sql/test_data_second_domain.mysql index fd4896bad472..1e5bc3847436 100644 --- a/sql/test_data_second_domain.mysql +++ b/sql/test_data_second_domain.mysql @@ -963,4 +963,4 @@ INSERT INTO civicrm_navigation VALUES ( @domainID, CONCAT('civicrm/report/instance/', @instanceID,'&reset=1'), 'Mailing Detail Report', 'Mailing Detail Report', 'administer CiviMail', 'OR', @reportlastID, '1', NULL, @instanceID+2 ); UPDATE civicrm_report_instance SET navigation_id = LAST_INSERT_ID() WHERE id = @instanceID; -UPDATE civicrm_domain SET version = '5.30.alpha1'; +UPDATE civicrm_domain SET version = '5.30.beta1'; diff --git a/xml/version.xml b/xml/version.xml index 06ebf2f8ad8e..a59a35af874f 100644 --- a/xml/version.xml +++ b/xml/version.xml @@ -1,4 +1,4 @@ - 5.30.alpha1 + 5.30.beta1 From 29ba40ecb43449a853f8250f7cb60cb0b1b87c6b Mon Sep 17 00:00:00 2001 From: CiviCRM Date: Thu, 3 Sep 2020 09:13:24 +0000 Subject: [PATCH 094/834] Set version to 5.31.alpha1 --- CRM/Upgrade/Incremental/php/FiveThirtyOne.php | 72 +++++++++++++++++++ .../Incremental/sql/5.31.alpha1.mysql.tpl | 1 + sql/civicrm_generated.mysql | 2 +- sql/test_data_second_domain.mysql | 2 +- xml/version.xml | 2 +- 5 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 CRM/Upgrade/Incremental/php/FiveThirtyOne.php create mode 100644 CRM/Upgrade/Incremental/sql/5.31.alpha1.mysql.tpl diff --git a/CRM/Upgrade/Incremental/php/FiveThirtyOne.php b/CRM/Upgrade/Incremental/php/FiveThirtyOne.php new file mode 100644 index 000000000000..cbc3760ac2ce --- /dev/null +++ b/CRM/Upgrade/Incremental/php/FiveThirtyOne.php @@ -0,0 +1,72 @@ +' . ts('A new permission, "%1", has been added. This permission is now used to control access to the Manage Tags screen.', array(1 => ts('manage tags'))) . '

'; + // } + } + + /** + * Compute any messages which should be displayed after upgrade. + * + * @param string $postUpgradeMessage + * alterable. + * @param string $rev + * an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs. + */ + public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) { + // Example: Generate a post-upgrade message. + // if ($rev == '5.12.34') { + // $postUpgradeMessage .= '

' . ts("By default, CiviCRM now disables the ability to import directly from SQL. To use this feature, you must explicitly grant permission 'import SQL datasource'."); + // } + } + + /* + * Important! All upgrade functions MUST add a 'runSql' task. + * Uncomment and use the following template for a new upgrade version + * (change the x in the function name): + */ + + // /** + // * Upgrade function. + // * + // * @param string $rev + // */ + // public function upgrade_5_0_x($rev) { + // $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev); + // $this->addTask('Do the foo change', 'taskFoo', ...); + // // Additional tasks here... + // // Note: do not use ts() in the addTask description because it adds unnecessary strings to transifex. + // // The above is an exception because 'Upgrade DB to %1: SQL' is generic & reusable. + // } + + // public static function taskFoo(CRM_Queue_TaskContext $ctx, ...) { + // return TRUE; + // } + +} diff --git a/CRM/Upgrade/Incremental/sql/5.31.alpha1.mysql.tpl b/CRM/Upgrade/Incremental/sql/5.31.alpha1.mysql.tpl new file mode 100644 index 000000000000..aaf47e53be3f --- /dev/null +++ b/CRM/Upgrade/Incremental/sql/5.31.alpha1.mysql.tpl @@ -0,0 +1 @@ +{* file to handle db changes in 5.31.alpha1 during upgrade *} diff --git a/sql/civicrm_generated.mysql b/sql/civicrm_generated.mysql index 4afb6f03fd1e..90b0b5a929b2 100644 --- a/sql/civicrm_generated.mysql +++ b/sql/civicrm_generated.mysql @@ -399,7 +399,7 @@ UNLOCK TABLES; LOCK TABLES `civicrm_domain` WRITE; /*!40000 ALTER TABLE `civicrm_domain` DISABLE KEYS */; -INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,'5.30.beta1',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}'); +INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,'5.31.alpha1',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}'); /*!40000 ALTER TABLE `civicrm_domain` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/test_data_second_domain.mysql b/sql/test_data_second_domain.mysql index 1e5bc3847436..f87b4519b797 100644 --- a/sql/test_data_second_domain.mysql +++ b/sql/test_data_second_domain.mysql @@ -963,4 +963,4 @@ INSERT INTO civicrm_navigation VALUES ( @domainID, CONCAT('civicrm/report/instance/', @instanceID,'&reset=1'), 'Mailing Detail Report', 'Mailing Detail Report', 'administer CiviMail', 'OR', @reportlastID, '1', NULL, @instanceID+2 ); UPDATE civicrm_report_instance SET navigation_id = LAST_INSERT_ID() WHERE id = @instanceID; -UPDATE civicrm_domain SET version = '5.30.beta1'; +UPDATE civicrm_domain SET version = '5.31.alpha1'; diff --git a/xml/version.xml b/xml/version.xml index a59a35af874f..588d045c3b29 100644 --- a/xml/version.xml +++ b/xml/version.xml @@ -1,4 +1,4 @@ - 5.30.beta1 + 5.31.alpha1 From 799b7660aedf87cc8ea520e22080cf123a79f1b2 Mon Sep 17 00:00:00 2001 From: Andrew Hunt Date: Thu, 3 Sep 2020 14:50:46 -0400 Subject: [PATCH 095/834] 5.30.0 release notes: raw from script --- release-notes/5.30.0.md | 307 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 release-notes/5.30.0.md diff --git a/release-notes/5.30.0.md b/release-notes/5.30.0.md new file mode 100644 index 000000000000..a2e702abae9f --- /dev/null +++ b/release-notes/5.30.0.md @@ -0,0 +1,307 @@ +# CiviCRM 5.30.0 + +Released October 7, 2020; + +- **[Features](#features)** +- **[Bugs resolved](#bugs)** +- **[Miscellany](#misc)** +- **[Credits](#credits)** + +## Features + +### Core CiviCRM + +- **crm- Missing Summary ([18229](https://github.com/civicrm/civicrm-core/pull/18229))** + +## Bugs resolved + +### Core CiviCRM + +- **Add ability to enable query logging per process ([18335](https://github.com/civicrm/civicrm-core/pull/18335))** + +- **5.29 ([18334](https://github.com/civicrm/civicrm-core/pull/18334))** + +- **[REF] Move function to delete merged contacts to the Merger class and include all instances of the pair ([17980](https://github.com/civicrm/civicrm-core/pull/17980))** + +- **dev/core#1956 - Typo in call to nestedGroup on scheduled reminders admin form ([18323](https://github.com/civicrm/civicrm-core/pull/18323))** + +- **5.29 ([18327](https://github.com/civicrm/civicrm-core/pull/18327))** + +- **dev/mail#24, item 1 - Fix spec labels MailingEventSubscribe.create ([18325](https://github.com/civicrm/civicrm-core/pull/18325))** + +- **5.29 ([18320](https://github.com/civicrm/civicrm-core/pull/18320))** + +- **[REF] Remove most interaction with $objects in completeOrder ([18315](https://github.com/civicrm/civicrm-core/pull/18315))** + +- **Test & remove handling for max_related in renewal form ([18295](https://github.com/civicrm/civicrm-core/pull/18295))** + +- **5.29 to master ([18316](https://github.com/civicrm/civicrm-core/pull/18316))** + +- **REF Remove unused passbyreference and var from ipn_process_transaction ([18311](https://github.com/civicrm/civicrm-core/pull/18311))** + +- **[REF] Clean up return variables on updateContributionStatus, updatePendingOnlineContribution ([18303](https://github.com/civicrm/civicrm-core/pull/18303))** + +- **Fix test set up to call Order.create to create the correct line items ([18279](https://github.com/civicrm/civicrm-core/pull/18279))** + +- **[REF] Separate and move line-item specific portion of checkTaxAmount to LineItem api ([18294](https://github.com/civicrm/civicrm-core/pull/18294))** + +- **5.29 ([18305](https://github.com/civicrm/civicrm-core/pull/18305))** + +- **Move LineItem acl handling from v3 api to financialacls core extension ([18293](https://github.com/civicrm/civicrm-core/pull/18293))** + +- **[REF] Fix financial item allocation of negative payments against completed payments ([17810](https://github.com/civicrm/civicrm-core/pull/17810))** + +- **Remove 'hack' that overwrites result of searchColumns hook in mailings list ([18237](https://github.com/civicrm/civicrm-core/pull/18237))** + +- **5.29 to master ([18302](https://github.com/civicrm/civicrm-core/pull/18302))** + +- **event#34: allow negative self-service/transfer time ([18067](https://github.com/civicrm/civicrm-core/pull/18067))** + +- **Add test to check, remove unnecessary lines ([18292](https://github.com/civicrm/civicrm-core/pull/18292))** + +- **Clean money for non-deductible amount ([18300](https://github.com/civicrm/civicrm-core/pull/18300))** + +- **[REF] [test] Slight increase in test cover & make functions more re-usable ([18291](https://github.com/civicrm/civicrm-core/pull/18291))** + +- **[REF] simplify retrieval of existing membership on membership renewal form ([18296](https://github.com/civicrm/civicrm-core/pull/18296))** + +- **Improve robustness of api/class.api.php ([18283](https://github.com/civicrm/civicrm-core/pull/18283))** + +- **add serialize data to actionschedule ([18289](https://github.com/civicrm/civicrm-core/pull/18289))** + +- **dev/event#37 Add CONTAINS operator for APIv4 & Search ([18285](https://github.com/civicrm/civicrm-core/pull/18285))** + +- **[REF] Extract function to get locations to merge, rename 'operation' … ([17991](https://github.com/civicrm/civicrm-core/pull/17991))** + +- **dev/core#1980 Move (now tested) delete acl check from v3 api to pre delete hook ([18275](https://github.com/civicrm/civicrm-core/pull/18275))** + +- **remove dropped tables for activity target and assignment ([18280](https://github.com/civicrm/civicrm-core/pull/18280))** + +- **Add line item acl tests ([18274](https://github.com/civicrm/civicrm-core/pull/18274))** + +- **5.29 ([18276](https://github.com/civicrm/civicrm-core/pull/18276))** + +- **report#47 Report Bookkeeping add time field for date filter. ([18268](https://github.com/civicrm/civicrm-core/pull/18268))** + +- **Fix regen and update civicrm_generated ([18273](https://github.com/civicrm/civicrm-core/pull/18273))** + +- **[REF] Move Auto DSN Switching into a core function ([18270](https://github.com/civicrm/civicrm-core/pull/18270))** + +- **Shell Financial ACLs extension ([18269](https://github.com/civicrm/civicrm-core/pull/18269))** + +- **dev/core#912 update payment instrument of main contribution record ([18266](https://github.com/civicrm/civicrm-core/pull/18266))** + +- **Use already determined value for contributionRecurID ([18265](https://github.com/civicrm/civicrm-core/pull/18265))** + +- **Remove old dropped items from the schema xml ([18244](https://github.com/civicrm/civicrm-core/pull/18244))** + +- **Remove 1 of 2 remaining places where relatedObjects is called in completeOrder ([18257](https://github.com/civicrm/civicrm-core/pull/18257))** + +- **dev/core#1971 Caching of domain-specific option values ([18252](https://github.com/civicrm/civicrm-core/pull/18252))** + +- **Remove last pass-by-reference in completeOrder signature ([18258](https://github.com/civicrm/civicrm-core/pull/18258))** + +- **Support more table names for utf8mb4 conversions and database name ([18249](https://github.com/civicrm/civicrm-core/pull/18249))** + +- **[REF] Ensure that the form param _id is set when adding a new group w… ([18250](https://github.com/civicrm/civicrm-core/pull/18250))** + +- **Optimise proximity custom search, by reducing addGeocodingData fn call ([18248](https://github.com/civicrm/civicrm-core/pull/18248))** + +- **Expose field label to APIv4 and Search creaor ([18255](https://github.com/civicrm/civicrm-core/pull/18255))** + +- **Separate titles from labels in exportable fields ([18256](https://github.com/civicrm/civicrm-core/pull/18256))** + +- **Fix js for case type status change on load ([18254](https://github.com/civicrm/civicrm-core/pull/18254))** + +- **Batch Update via Profile does not supply data for editing when custom fields created after participant added to event ([18235](https://github.com/civicrm/civicrm-core/pull/18235))** + +- **Wrap "not you" message in crmRegion ([18236](https://github.com/civicrm/civicrm-core/pull/18236))** + +- **Do not pass by reference for createRelatedMemberships ([18243](https://github.com/civicrm/civicrm-core/pull/18243))** + +- **Remove 2019 deprecated function ([18242](https://github.com/civicrm/civicrm-core/pull/18242))** + +- **Add disable_smarty option to MessageTemplate.send API ([18118](https://github.com/civicrm/civicrm-core/pull/18118))** + +- **dev/core#1950 Update help text and description for the profile add to groups setting ([18153](https://github.com/civicrm/civicrm-core/pull/18153))** + +- **Remove ref to logging time ([18221](https://github.com/civicrm/civicrm-core/pull/18221))** + +- **Replace deprecated Drupal 8 constants ([18240](https://github.com/civicrm/civicrm-core/pull/18240))** + +- **trim white space when checking dedupe ([18234](https://github.com/civicrm/civicrm-core/pull/18234))** + +- **Spelling mistake "separator" not "seperator" ([18238](https://github.com/civicrm/civicrm-core/pull/18238))** + +- **[NFC] Some DAO checksums not updated ([18239](https://github.com/civicrm/civicrm-core/pull/18239))** + +- **5.29 ([18231](https://github.com/civicrm/civicrm-core/pull/18231))** + +- **5.29 ([18226](https://github.com/civicrm/civicrm-core/pull/18226))** + +- **5.29 to master ([18222](https://github.com/civicrm/civicrm-core/pull/18222))** + +- **[Test framework] - Remove test for deprecated way of calling hook invoke ([18136](https://github.com/civicrm/civicrm-core/pull/18136))** + +- **Cleanup contactID variables in event/contribution register buildQuickForm ([18208](https://github.com/civicrm/civicrm-core/pull/18208))** + +- **Remove unused variables from repeatTransaction ([18209](https://github.com/civicrm/civicrm-core/pull/18209))** + +- **Metadata fix in activity search ([18216](https://github.com/civicrm/civicrm-core/pull/18216))** + +- **Minor code cleanup to Order API ([18217](https://github.com/civicrm/civicrm-core/pull/18217))** + +- **5.29 ([18215](https://github.com/civicrm/civicrm-core/pull/18215))** + +- **(NFC) Add some tests to the `resources` group ([18211](https://github.com/civicrm/civicrm-core/pull/18211))** + +- **5.29 ([18210](https://github.com/civicrm/civicrm-core/pull/18210))** + +- **5.29 ([18203](https://github.com/civicrm/civicrm-core/pull/18203))** + +- **APIv4 - Fix output of CustomValue create/save/update ([18195](https://github.com/civicrm/civicrm-core/pull/18195))** + +- **5.29 ([18199](https://github.com/civicrm/civicrm-core/pull/18199))** + +- **Remove first_contribution key from repeattransaction ([18197](https://github.com/civicrm/civicrm-core/pull/18197))** + +- **Remove unnecessary transformation of upgrade msg severity ([18182](https://github.com/civicrm/civicrm-core/pull/18182))** + +- **Metadata fix - phone_type_id, location_type_id, gender_id ([18114](https://github.com/civicrm/civicrm-core/pull/18114))** + +- **Check if $post defined before trying to use its field values. ([18168](https://github.com/civicrm/civicrm-core/pull/18168))** + +- **5.29 to master ([18193](https://github.com/civicrm/civicrm-core/pull/18193))** + +- **Put mysql back in civicrm.settings.php template ([18188](https://github.com/civicrm/civicrm-core/pull/18188))** + +- **E_NOTICE on contribution page widget tab ([18189](https://github.com/civicrm/civicrm-core/pull/18189))** + +- **CRM_Utils_VersionCheck - respect force param ([18183](https://github.com/civicrm/civicrm-core/pull/18183))** + +- **5.29 ([18187](https://github.com/civicrm/civicrm-core/pull/18187))** + +- **(NFC) Update various DAO checksums ([18184](https://github.com/civicrm/civicrm-core/pull/18184))** + +- **E_WARNING when saving event fees admin page if there's no discounts set ([18169](https://github.com/civicrm/civicrm-core/pull/18169))** + +- **Upgrade screen - show success instead of error if already upgraded ([18181](https://github.com/civicrm/civicrm-core/pull/18181))** + +- **Processors - remove gross_amount param from processors ([18177](https://github.com/civicrm/civicrm-core/pull/18177))** + +- **5.29 ([18173](https://github.com/civicrm/civicrm-core/pull/18173))** + +- **{contribution.receipt_date} token does not use any CiviCRM date formatter, output in YYYY-MM-DD HH:MM:SS format and {contribution.receive_date} also uses a non-standard format ([18176](https://github.com/civicrm/civicrm-core/pull/18176))** + +- **Minor cleanup in test class ([18170](https://github.com/civicrm/civicrm-core/pull/18170))** + +- **[REF] Deprecate unused SQL temptable functions ([18171](https://github.com/civicrm/civicrm-core/pull/18171))** + +- **Mass SMS: Limit group selection to mailing groups ([18154](https://github.com/civicrm/civicrm-core/pull/18154))** + +- **[REF] Update recent replacement for CRM_Utils_Array::value ([18172](https://github.com/civicrm/civicrm-core/pull/18172))** + +- **5.29 ([18165](https://github.com/civicrm/civicrm-core/pull/18165))** + +- **Replace CRM_Utils_Array::value in contribution/confirm and bao/membership ([18157](https://github.com/civicrm/civicrm-core/pull/18157))** + +- **5.29 ([18159](https://github.com/civicrm/civicrm-core/pull/18159))** + +- **Remove legacy handling for 'fixing' line_item.entity_id ([18155](https://github.com/civicrm/civicrm-core/pull/18155))** + +- **Remove deprecated code ids['userId'] ([18156](https://github.com/civicrm/civicrm-core/pull/18156))** + +- **[REF] Remove usages of CRM_Utils_Money::format that pass in a blank s… ([18142](https://github.com/civicrm/civicrm-core/pull/18142))** + +- **5.29 ([18148](https://github.com/civicrm/civicrm-core/pull/18148))** + +- **Use more appropriate money format function ([18151](https://github.com/civicrm/civicrm-core/pull/18151))** + +- **API - Add upgrade-safe checks to ensure table exists before reading ([18135](https://github.com/civicrm/civicrm-core/pull/18135))** + +- **Deprecate 'trapException' in DAO::executeQuery ([18138](https://github.com/civicrm/civicrm-core/pull/18138))** + +- **(REF) CRM_Core_Region - Remove unused bits ([18139](https://github.com/civicrm/civicrm-core/pull/18139))** + +- **dev/core#1661 Allow phones with types longer than 16 chars to export ([17956](https://github.com/civicrm/civicrm-core/pull/17956))** + +- **[REF] Move isSSLDSN() function to avoid potential problems ([18131](https://github.com/civicrm/civicrm-core/pull/18131))** + +- **Deprecate/remove unused checkVersion() functions ([18134](https://github.com/civicrm/civicrm-core/pull/18134))** + +- **(dev/core#1944) Add new columns to mailing summary report ([18132](https://github.com/civicrm/civicrm-core/pull/18132))** + +- **CodeGen - Add use statement to extensions DAO files ([18094](https://github.com/civicrm/civicrm-core/pull/18094))** + +- **5.29 ([18128](https://github.com/civicrm/civicrm-core/pull/18128))** + +- **dev/core#1926 - Towards supporting SSL for mysql connections - remove DB::connect that doesn't add anything ([18095](https://github.com/civicrm/civicrm-core/pull/18095))** + +- **5.29 ([18122](https://github.com/civicrm/civicrm-core/pull/18122))** + +- **Fix PHP notice on contribution page ([18116](https://github.com/civicrm/civicrm-core/pull/18116))** + +- **[NFC] Add code comments about hidden but still used buttons ([18119](https://github.com/civicrm/civicrm-core/pull/18119))** + +- **dev/core#1943 add functionality to add civicrm log into Drupal access log ([18115](https://github.com/civicrm/civicrm-core/pull/18115))** + +- **5.29 to master ([18117](https://github.com/civicrm/civicrm-core/pull/18117))** + +- **[NFC] [Test] Initial testing on event payment forms. ([18112](https://github.com/civicrm/civicrm-core/pull/18112))** + +- **Add Serialize key to payment_processor field for Event & Contribution Page ([18110](https://github.com/civicrm/civicrm-core/pull/18110))** + +- **[NFC] Array formatting ([18109](https://github.com/civicrm/civicrm-core/pull/18109))** + +- **Remove unused variable ([18108](https://github.com/civicrm/civicrm-core/pull/18108))** + +- **5.29 ([18104](https://github.com/civicrm/civicrm-core/pull/18104))** + +- **5.29 ([18100](https://github.com/civicrm/civicrm-core/pull/18100))** + +- **There is no "I" in mysqli - oh wait there is ([18097](https://github.com/civicrm/civicrm-core/pull/18097))** + +- **5.29 to master ([18098](https://github.com/civicrm/civicrm-core/pull/18098))** + +- **[NFC] Update locale over-ride documentation to mention that it is mos… ([17919](https://github.com/civicrm/civicrm-core/pull/17919))** + +- **APIv4 Explorer - Fix possible undefined index ([18093](https://github.com/civicrm/civicrm-core/pull/18093))** + +- **[REF] Update subtypeInfo function to leverage getAllContactTypes ([17934](https://github.com/civicrm/civicrm-core/pull/17934))** + +- **[REF] Fix metadata label for contribution_page_id ([18047](https://github.com/civicrm/civicrm-core/pull/18047))** + +- **5.29 ([18090](https://github.com/civicrm/civicrm-core/pull/18090))** + +- **APIv4 Explorer: Generate short cv code ([18089](https://github.com/civicrm/civicrm-core/pull/18089))** + +- **[REF] Deploy Pear DB package using composer ([18027](https://github.com/civicrm/civicrm-core/pull/18027))** + +- **Allow for the setting of an additional constant CIVICRM_DRUSH_DSN ([613](https://github.com/civicrm/civicrm-drupal/pull/613))** + +- **7.x 5.29 ([621](https://github.com/civicrm/civicrm-drupal/pull/621))** + +- **7.x 5.29 ([616](https://github.com/civicrm/civicrm-drupal/pull/616))** + +- **[NFC] IDE cleanup - arrays, single quotes ([614](https://github.com/civicrm/civicrm-drupal/pull/614))** + +- **Remove handling for 4.2 DBs ([612](https://github.com/civicrm/civicrm-drupal/pull/612))** + +- **dev/wordpress#37 - Switch unambiguously to new installer UI ([215](https://github.com/civicrm/civicrm-wordpress/pull/215))** + +- **DB_DataObject - Read CIVICRM_DEBUG_LOG_QUERY correctly ([305](https://github.com/civicrm/civicrm-packages/pull/305))** + +- **[REF] Remove DB Package as it is now deployed using composer ([302](https://github.com/civicrm/civicrm-packages/pull/302))** + +## Miscellany + +## Credits + +This release was developed by the following code authors: + +AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman, Pengyi Zhang; Circle Interactive - Pradeep Nayak; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Sunil Pawar, Yashodha Chaku; Dave D; Flinders University of South Australia - Tom Anderson; JMA Consulting - Monish Deb, Seamus Lee; John Kingsnorth; Lemniscus - Noah Miller; Lighthouse Consulting and Design - Brian Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MillerTech - Chamil Wijesooriya; MJW Consulting - Matthew Wire; Squiffle Consulting - Aidan Saunders; Wikimedia Foundation - Eileen McNaughton, Maggie Epps; Wildsight - Lars Sanders-Green + +Most authors also reviewed code for this release; in addition, the following +reviewers contributed their comments: + +Artful Robot - Rich Lott; Christian Wach; civibot[bot]; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; Dave D; Fuzion - Jitendra Purohit; Greenpeace Central and Eastern Europe - Patrick Figel; JMA Consulting - Seamus Lee; John Kingsnorth; Lighthouse Consulting and Design - Brian Shaughnessy; MarshCastle; Megaphone Technology Consulting - Jon Goldberg; MJCO - Mikey O'Toole; MJW Consulting - Matthew Wire; Semper IT - Karin Gerritsen; Squiffle Consulting - Aidan Saunders; Tadpole Collective - Kevin Cristiano; Wikimedia Foundation - Eileen McNaughton, Maggie Epps; Wildsight - Lars Sanders-Green \ No newline at end of file From 9aec4f344cda1b5bffa41f3183133674e46a57ae Mon Sep 17 00:00:00 2001 From: Andrew Hunt Date: Thu, 3 Sep 2020 14:53:04 -0400 Subject: [PATCH 096/834] 5.30.0 release notes: added boilerplate --- release-notes.md | 11 +++++++++++ release-notes/5.30.0.md | 24 ++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/release-notes.md b/release-notes.md index d2d501d26a72..0171d9a5981c 100644 --- a/release-notes.md +++ b/release-notes.md @@ -15,6 +15,17 @@ Other resources for identifying changes are: * https://github.com/civicrm/civicrm-joomla * https://github.com/civicrm/civicrm-wordpress +## CiviCRM 5.30.0 + +Released October 7, 2020 + +- **[Synopsis](release-notes/5.30.0.md#synopsis)** +- **[Features](release-notes/5.30.0.md#features)** +- **[Bugs resolved](release-notes/5.30.0.md#bugs)** +- **[Miscellany](release-notes/5.30.0.md#misc)** +- **[Credits](release-notes/5.30.0.md#credits)** +- **[Feedback](release-notes/5.30.0.md#feedback)** + ## CiviCRM 5.29.0 Released September 2, 2020 diff --git a/release-notes/5.30.0.md b/release-notes/5.30.0.md index a2e702abae9f..476686b17a87 100644 --- a/release-notes/5.30.0.md +++ b/release-notes/5.30.0.md @@ -1,11 +1,25 @@ # CiviCRM 5.30.0 -Released October 7, 2020; +Released October 7, 2020 +- **[Synopsis](#synopsis)** - **[Features](#features)** - **[Bugs resolved](#bugs)** - **[Miscellany](#misc)** - **[Credits](#credits)** +- **[Feedback](#feedback)** + +## Synopsis + +| *Does this version...?* | | +|:--------------------------------------------------------------- |:-------:| +| Fix security vulnerabilities? | | +| Change the database schema? | | +| Alter the API? | | +| Require attention to configuration options? | | +| Fix problems installing or upgrading to a previous version? | | +| Introduce features? | | +| Fix bugs? | | ## Features @@ -304,4 +318,10 @@ AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman, Pengyi Z Most authors also reviewed code for this release; in addition, the following reviewers contributed their comments: -Artful Robot - Rich Lott; Christian Wach; civibot[bot]; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; Dave D; Fuzion - Jitendra Purohit; Greenpeace Central and Eastern Europe - Patrick Figel; JMA Consulting - Seamus Lee; John Kingsnorth; Lighthouse Consulting and Design - Brian Shaughnessy; MarshCastle; Megaphone Technology Consulting - Jon Goldberg; MJCO - Mikey O'Toole; MJW Consulting - Matthew Wire; Semper IT - Karin Gerritsen; Squiffle Consulting - Aidan Saunders; Tadpole Collective - Kevin Cristiano; Wikimedia Foundation - Eileen McNaughton, Maggie Epps; Wildsight - Lars Sanders-Green \ No newline at end of file +Artful Robot - Rich Lott; Christian Wach; civibot[bot]; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; Dave D; Fuzion - Jitendra Purohit; Greenpeace Central and Eastern Europe - Patrick Figel; JMA Consulting - Seamus Lee; John Kingsnorth; Lighthouse Consulting and Design - Brian Shaughnessy; MarshCastle; Megaphone Technology Consulting - Jon Goldberg; MJCO - Mikey O'Toole; MJW Consulting - Matthew Wire; Semper IT - Karin Gerritsen; Squiffle Consulting - Aidan Saunders; Tadpole Collective - Kevin Cristiano; Wikimedia Foundation - Eileen McNaughton, Maggie Epps; Wildsight - Lars Sanders-Green + +## Feedback + +These release notes are edited by Alice Frumin and Andrew Hunt. If you'd like +to provide feedback on them, please log in to https://chat.civicrm.org/civicrm +and contact `@agh1`. From aeee327d2b9b004b8beca10f8d190263352d87c6 Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 3 Sep 2020 20:13:01 +1200 Subject: [PATCH 097/834] Move ACls on LineItem create to financialacls core extension --- CRM/Price/BAO/LineItem.php | 13 +----------- ext/financialacls/financialacls.php | 9 +++++---- .../tests/phpunit/LineItemTest.php | 20 ++++++++++++++++++- tests/phpunit/api/v3/FinancialTypeACLTest.php | 2 +- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/CRM/Price/BAO/LineItem.php b/CRM/Price/BAO/LineItem.php index b25c02c37c51..0067fef0a6ed 100644 --- a/CRM/Price/BAO/LineItem.php +++ b/CRM/Price/BAO/LineItem.php @@ -35,11 +35,9 @@ public static function create(&$params) { $id = $params['id'] ?? NULL; if ($id) { CRM_Utils_Hook::pre('edit', 'LineItem', $id, $params); - $op = CRM_Core_Action::UPDATE; } else { CRM_Utils_Hook::pre('create', 'LineItem', $params['entity_id'], $params); - $op = CRM_Core_Action::ADD; } // unset entity table and entity id in $params @@ -54,21 +52,12 @@ public static function create(&$params) { $params['unit_price'] = 0; } } - if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus() && !empty($params['check_permissions'])) { - if (empty($params['financial_type_id'])) { - throw new Exception('Mandatory key(s) missing from params array: financial_type_id'); - } - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types, $op); - if (!in_array($params['financial_type_id'], array_keys($types))) { - throw new Exception('You do not have permission to create this line item'); - } - } $lineItemBAO = new CRM_Price_BAO_LineItem(); $lineItemBAO->copyValues($params); $return = $lineItemBAO->save(); - if ($lineItemBAO->entity_table == 'civicrm_membership' && $lineItemBAO->contribution_id && $lineItemBAO->entity_id) { + if ($lineItemBAO->entity_table === 'civicrm_membership' && $lineItemBAO->contribution_id && $lineItemBAO->entity_id) { $membershipPaymentParams = [ 'membership_id' => $lineItemBAO->entity_id, 'contribution_id' => $lineItemBAO->contribution_id, diff --git a/ext/financialacls/financialacls.php b/ext/financialacls/financialacls.php index b079114d9f6f..c0edae5e58c1 100644 --- a/ext/financialacls/financialacls.php +++ b/ext/financialacls/financialacls.php @@ -146,7 +146,7 @@ function financialacls_civicrm_themes(&$themes) { /** * Intervene to prevent deletion, where permissions block it. * - * @param \CRM_Core_DAO $op + * @param string $op * @param string $objectName * @param int|null $id * @param array $params @@ -155,14 +155,15 @@ function financialacls_civicrm_themes(&$themes) { * @throws \CRM_Core_Exception */ function financialacls_civicrm_pre($op, $objectName, $id, &$params) { - if ($objectName === 'LineItem' && $op === 'delete' && !empty($params['check_permissions'])) { + if ($objectName === 'LineItem' && !empty($params['check_permissions'])) { if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) { - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types, CRM_Core_Action::DELETE); + $operationMap = ['delete' => CRM_Core_Action::DELETE, 'edit' => CRM_Core_Action::UPDATE, 'create' => CRM_Core_Action::ADD]; + CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types, $operationMap[$op]); if (empty($params['financial_type_id'])) { $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_LineItem', $params['id'], 'financial_type_id'); } if (!in_array($params['financial_type_id'], array_keys($types))) { - throw new API_Exception('You do not have permission to delete this line item'); + throw new API_Exception('You do not have permission to ' . $op . ' this line item'); } } } diff --git a/ext/financialacls/tests/phpunit/LineItemTest.php b/ext/financialacls/tests/phpunit/LineItemTest.php index 71eb6380c4d0..573847299487 100644 --- a/ext/financialacls/tests/phpunit/LineItemTest.php +++ b/ext/financialacls/tests/phpunit/LineItemTest.php @@ -43,7 +43,7 @@ public function setUpHeadless() { public function testLineItemApiPermissions() { $contact1 = $this->individualCreate(); $defaultPriceFieldID = $this->getDefaultPriceFieldID(); - $this->callAPISuccess('Order', 'create', [ + $order = $this->callAPISuccess('Order', 'create', [ 'financial_type_id' => 'Donation', 'contact_id' => $contact1, 'line_items' => [ @@ -73,6 +73,8 @@ public function testLineItemApiPermissions() { 'delete in CiviContribute', 'view contributions of type Donation', 'delete contributions of type Donation', + 'add contributions of type Donation', + 'edit contributions of type Donation', ]); Civi::settings()->set('acl_financial_type', TRUE); $this->createLoggedInUser(); @@ -83,6 +85,22 @@ public function testLineItemApiPermissions() { $this->callAPISuccess('LineItem', 'Delete', ['check_permissions' => TRUE, 'id' => $lineItems[0]['id']]); $this->callAPIFailure('LineItem', 'Delete', ['check_permissions' => TRUE, 'id' => $lineItems[1]['id']]); + $lineParams = [ + 'entity_id' => $order['id'], + 'entity_table' => 'civicrm_contribution', + 'line_total' => 20, + 'unit_price' => 20, + 'price_field_id' => $defaultPriceFieldID, + 'qty' => 1, + 'financial_type_id' => 'Donation', + 'check_permissions' => TRUE, + ]; + $line = $this->callAPISuccess('LineItem', 'Create', $lineParams); + $lineParams['financial_type_id'] = 'Event Fee'; + $this->callAPIFailure('LineItem', 'Create', $lineParams); + + $this->callAPIFailure('LineItem', 'Create', ['id' => $line['id'], 'check_permissions' => TRUE, 'financial_type_id' => 'Event Fee']); + $this->callAPISuccess('LineItem', 'Create', ['id' => $line['id'], 'check_permissions' => TRUE, 'financial_type_id' => 'Donation']); } /** diff --git a/tests/phpunit/api/v3/FinancialTypeACLTest.php b/tests/phpunit/api/v3/FinancialTypeACLTest.php index 7aa516ebd471..036960b09009 100644 --- a/tests/phpunit/api/v3/FinancialTypeACLTest.php +++ b/tests/phpunit/api/v3/FinancialTypeACLTest.php @@ -230,7 +230,7 @@ public function testCreateACLContributionChainedLineItems() { 'add contributions of type Donation', 'delete contributions of type Donation', ]); - $this->callAPIFailure('contribution', 'create', $params, 'Error in call to LineItem_create : You do not have permission to create this line item'); + $this->callAPIFailure('Contribution', 'create', $params, 'Error in call to LineItem_create : You do not have permission to create this line item'); // Check that the entire contribution has rolled back. $contribution = $this->callAPISuccess('contribution', 'get', []); From eed14abbc29fe6b02e3168e65d5e1bffc5e0a614 Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 3 Sep 2020 12:08:50 +1200 Subject: [PATCH 098/834] Stop passing / using object when all we need is the id Rather than set id on the contribution object just to be able to access it via contribution->id let's name the param we keep using & use that. Note I'm still getting contribution->id from contribution here but I think this makes it clear that the object is mostly only used in addActivity now. The sligtly larger change is in updateMembershipBasedOnCompletionOfContribution where there is an instantiation of 'self()' since we no longer have the object --- CRM/Contribute/BAO/Contribution.php | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 238c4f196d3c..6990d69c37f8 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -444,7 +444,7 @@ public static function getPaymentProcessorReadyAddressParams($params, $billingLo * * @return int */ - public function getNumTermsByContributionAndMembershipType($membershipTypeID, $contributionID) { + public static function getNumTermsByContributionAndMembershipType($membershipTypeID, $contributionID) { $numTerms = CRM_Core_DAO::singleValueQuery(" SELECT membership_num_terms FROM civicrm_line_item li LEFT JOIN civicrm_price_field_value v ON li.price_field_value_id = v.id @@ -4509,11 +4509,12 @@ public static function completeOrder($input, $ids, $objects, $isPostPaymentCreat $changeDate = CRM_Utils_Array::value('trxn_date', $input, date('YmdHis')); $contributionResult = self::repeatTransaction($contribution, $input, $contributionParams); + $contributionID = (int) $contribution->id; if ($input['component'] == 'contribute') { if ($contributionParams['contribution_status_id'] === $completedContributionStatusID) { self::updateMembershipBasedOnCompletionOfContribution( - $contribution, + $contributionID, $changeDate ); } @@ -4526,7 +4527,7 @@ public static function completeOrder($input, $ids, $objects, $isPostPaymentCreat } } - $contributionParams['id'] = $contribution->id; + $contributionParams['id'] = $contributionID; $contributionParams['is_post_payment_create'] = $isPostPaymentCreate; if (!$contributionResult) { @@ -4535,7 +4536,7 @@ public static function completeOrder($input, $ids, $objects, $isPostPaymentCreat // Add new soft credit against current $contribution. if ($recurringContributionID) { - CRM_Contribute_BAO_ContributionRecur::addrecurSoftCredit($recurringContributionID, $contribution->id); + CRM_Contribute_BAO_ContributionRecur::addrecurSoftCredit($recurringContributionID, $contributionID); } $contribution->contribution_status_id = $contributionParams['contribution_status_id']; @@ -4544,7 +4545,7 @@ public static function completeOrder($input, $ids, $objects, $isPostPaymentCreat $transaction->commit(); // @todo - check if Contribution::create does this, test, remove. - CRM_Contribute_BAO_ContributionRecur::updateRecurLinkedPledge($contribution->id, $recurringContributionID, + CRM_Contribute_BAO_ContributionRecur::updateRecurLinkedPledge($contributionID, $recurringContributionID, $contributionParams['contribution_status_id'], $input['amount']); // create an activity record @@ -4561,7 +4562,7 @@ public static function completeOrder($input, $ids, $objects, $isPostPaymentCreat if (self::isEmailReceipt($input, $contribution->contribution_page_id, $recurringContributionID)) { civicrm_api3('Contribution', 'sendconfirmation', [ - 'id' => $contribution->id, + 'id' => $contributionID, 'payment_processor_id' => $paymentProcessorId, ]); CRM_Core_Error::debug_log_message("Receipt sent"); @@ -5157,14 +5158,14 @@ protected static function isPaymentInstrumentChange(&$params, $pendingStatuses) * Note that the way in which $memberships are loaded as objects is pretty messy & I think we could just * load them in this function. Code clean up would compensate for any minor performance implication. * - * @param \CRM_Contribute_BAO_Contribution $contribution + * @param int $contributionID * @param string $changeDate * * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public static function updateMembershipBasedOnCompletionOfContribution($contribution, $changeDate) { - $memberships = self::getRelatedMemberships($contribution->id); + public static function updateMembershipBasedOnCompletionOfContribution($contributionID, $changeDate) { + $memberships = self::getRelatedMemberships($contributionID); foreach ($memberships as $membership) { $membershipParams = [ 'id' => $membership['id'], @@ -5202,9 +5203,9 @@ public static function updateMembershipBasedOnCompletionOfContribution($contribu // The api assumes num_terms is a special sauce for 'is_renewal' so we need to not pass it when updating a pending to completed. // ... except testCompleteTransactionMembershipPriceSetTwoTerms hits this line so the above is obviously not true.... // @todo once apiv4 ships with core switch to that & find sanity. - $membershipParams['num_terms'] = $contribution->getNumTermsByContributionAndMembershipType( + $membershipParams['num_terms'] = self::getNumTermsByContributionAndMembershipType( $membershipParams['membership_type_id'], - $contribution->id + $contributionID ); } // @todo remove all this stuff in favour of letting the api call further down handle in From 80ece7a5cbb1fad7e06f182c60cd4373cbd20165 Mon Sep 17 00:00:00 2001 From: eileen Date: Fri, 4 Sep 2020 08:22:43 +1200 Subject: [PATCH 099/834] Minor code cleanup - this is only ever called from one place so component is alwasy event --- CRM/Event/Form/Task/Batch.php | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/CRM/Event/Form/Task/Batch.php b/CRM/Event/Form/Task/Batch.php index a9835a08cf68..163c830cf135 100644 --- a/CRM/Event/Form/Task/Batch.php +++ b/CRM/Event/Form/Task/Batch.php @@ -295,7 +295,6 @@ public static function updatePendingOnlineContribution($participantId, $statusId $params = [ 'component_id' => $participantId, - 'componentName' => 'Event', 'contribution_id' => $contributionId, 'contribution_status_id' => $contributionStatusId, 'IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved' => 1, @@ -323,13 +322,13 @@ public static function updateContributionStatus($params) { // get minimum required values. $statusId = $params['contribution_status_id'] ?? NULL; $componentId = $params['component_id'] ?? NULL; - $componentName = $params['componentName'] ?? NULL; $contributionId = $params['contribution_id'] ?? NULL; $input = $ids = $objects = []; //get the required ids. $ids['contribution'] = $contributionId; + $ids['participant'] = $params['component_id']; if (!$ids['contact'] = CRM_Utils_Array::value('contact_id', $params)) { $ids['contact'] = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', @@ -338,25 +337,14 @@ public static function updateContributionStatus($params) { ); } - if ($componentName === 'Event') { - $name = 'event'; - $ids['participant'] = $componentId; - - if (!$ids['event'] = CRM_Utils_Array::value('event_id', $params)) { - $ids['event'] = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant', - $componentId, - 'event_id' - ); - } + if (!$ids['event'] = CRM_Utils_Array::value('event_id', $params)) { + $ids['event'] = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant', + $componentId, + 'event_id' + ); } - if ($componentName === 'Membership') { - $name = 'contribute'; - $ids['membership'] = $componentId; - } - $ids['contributionPage'] = NULL; - $ids['contributionRecur'] = NULL; - $input['component'] = $name; + $input['component'] = 'event'; $baseIPN = new CRM_Core_Payment_BaseIPN(); From bbcf0f467c3cd83f1513a55112f373b2a33bf08f Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 5 Aug 2020 06:49:01 -0700 Subject: [PATCH 100/834] (REF) CRM_Core_Resources_Strings - Move instance into container --- CRM/Core/Resources.php | 6 +++--- Civi/Core/Container.php | 7 ++++++- tests/phpunit/CRM/Core/ResourcesTest.php | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CRM/Core/Resources.php b/CRM/Core/Resources.php index 265112d8f994..3e736d3c0521 100644 --- a/CRM/Core/Resources.php +++ b/CRM/Core/Resources.php @@ -135,13 +135,13 @@ public static function singleton(CRM_Core_Resources $instance = NULL) { * * @param CRM_Extension_Mapper $extMapper * Map extension names to their base path or URLs. - * @param CRM_Utils_Cache_Interface $cache + * @param CRM_Core_Resources_Strings $strings * JS-localization cache. * @param string|null $cacheCodeKey Random code to append to resource URLs; changing the code forces clients to reload resources */ - public function __construct($extMapper, $cache, $cacheCodeKey = NULL) { + public function __construct($extMapper, $strings, $cacheCodeKey = NULL) { $this->extMapper = $extMapper; - $this->strings = new CRM_Core_Resources_Strings($cache); + $this->strings = $strings; $this->cacheCodeKey = $cacheCodeKey; if ($cacheCodeKey !== NULL) { $this->cacheCode = Civi::settings()->get($cacheCodeKey); diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index 5b96e40ef08e..ee8b214e7fd1 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -237,6 +237,11 @@ public function createContainer() { [new Reference('service_container')] ))->setFactory([new Reference(self::SELF), 'createResources'])->setPublic(TRUE); + $container->setDefinition('resources.js_strings', new Definition( + 'CRM_Core_Resources_Strings', + [new Reference('cache.js_strings')] + ))->setPublic(TRUE); + $container->setDefinition('prevnext', new Definition( 'CRM_Core_PrevNextCache_Interface', [new Reference('service_container')] @@ -450,7 +455,7 @@ public static function createResources($container) { $sys = \CRM_Extension_System::singleton(); return new \CRM_Core_Resources( $sys->getMapper(), - $container->get('cache.js_strings'), + new \CRM_Core_Resources_Strings($container->get('cache.js_strings')), \CRM_Core_Config::isUpgradeMode() ? NULL : 'resCacheCode' ); } diff --git a/tests/phpunit/CRM/Core/ResourcesTest.php b/tests/phpunit/CRM/Core/ResourcesTest.php index 6c1fc7dd1fa4..629ba4f80320 100644 --- a/tests/phpunit/CRM/Core/ResourcesTest.php +++ b/tests/phpunit/CRM/Core/ResourcesTest.php @@ -40,7 +40,7 @@ public function setUp() { list ($this->basedir, $this->container, $this->mapper) = $this->_createMapper(); $cache = new CRM_Utils_Cache_Arraycache([]); - $this->res = new CRM_Core_Resources($this->mapper, $cache, NULL); + $this->res = new CRM_Core_Resources($this->mapper, new CRM_Core_Resources_Strings($cache), NULL); $this->res->setCacheCode('resTest'); CRM_Core_Resources::singleton($this->res); From 832eb60a0db787e3d70caa44e8574e811247e008 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 5 Aug 2020 02:18:21 -0700 Subject: [PATCH 101/834] CRM_Core_Resources - Convert 'settings' to a type of snippet --- CRM/Core/Region.php | 25 ++++--- CRM/Core/Resources.php | 87 ++++++++++-------------- tests/phpunit/CRM/Core/ResourcesTest.php | 2 +- 3 files changed, 54 insertions(+), 60 deletions(-) diff --git a/CRM/Core/Region.php b/CRM/Core/Region.php index 06cece6bc9e7..d87664019dc9 100644 --- a/CRM/Core/Region.php +++ b/CRM/Core/Region.php @@ -4,7 +4,6 @@ * Maintain a set of markup/templates to inject inside various regions */ class CRM_Core_Region { - static private $_instances = NULL; /** * Obtain the content for a given region. @@ -15,10 +14,10 @@ class CRM_Core_Region { * @return CRM_Core_Region */ public static function &instance($name, $autocreate = TRUE) { - if ($autocreate && !isset(self::$_instances[$name])) { - self::$_instances[$name] = new CRM_Core_Region($name); + if ($autocreate && !isset(Civi::$statics[__CLASS__][$name])) { + Civi::$statics[__CLASS__][$name] = new CRM_Core_Region($name); } - return self::$_instances[$name]; + return Civi::$statics[__CLASS__][$name]; } /** @@ -95,13 +94,14 @@ public function __construct($name) { * - script: string, Javascript code * - scriptUrl: string, URL of a Javascript file * - jquery: string, Javascript code which runs inside a jQuery(function($){...}); block + * - settings: array, list of static values to convey. * - style: string, CSS code * - styleUrl: string, URL of a CSS file * * @return array */ public function add($snippet) { - static $types = ['markup', 'template', 'callback', 'scriptUrl', 'script', 'jquery', 'style', 'styleUrl']; + static $types = ['markup', 'template', 'callback', 'scriptUrl', 'script', 'jquery', 'settings', 'style', 'styleUrl']; $defaults = [ 'region' => $this->_name, 'weight' => 1, @@ -139,11 +139,10 @@ public function update($name, $snippet) { * Get snippet. * * @param string $name - * - * @return mixed + * @return array|NULL */ - public function get($name) { - return !empty($this->_snippets[$name]) ? $this->_snippets[$name] : NULL; + public function &get($name) { + return $this->_snippets[$name]; } /** @@ -218,6 +217,14 @@ public function render($default, $allowCmsOverride = TRUE) { } break; + case 'settings': + $settingsData = json_encode(Civi::resources()->getSettings($this->_name), JSON_UNESCAPED_SLASHES); + $js = "(function(vars) { + if (window.CRM) CRM.$.extend(true, CRM, vars); else window.CRM = vars; + })($settingsData)"; + $html .= sprintf("\n", $js); + break; + default: throw new CRM_Core_Exception(ts('Snippet type %1 is unrecognized', [1 => $snippet['type']])); diff --git a/CRM/Core/Resources.php b/CRM/Core/Resources.php index 3e736d3c0521..0d086f40c908 100644 --- a/CRM/Core/Resources.php +++ b/CRM/Core/Resources.php @@ -45,20 +45,6 @@ class CRM_Core_Resources { */ private $strings = NULL; - /** - * Settings in free-form data tree. - * - * @var array - */ - protected $settings = []; - - /** - * Setting factories. - * - * @var callable[] - */ - protected $settingsFactories = []; - /** * Added core resources. * @@ -269,9 +255,11 @@ public function addScript($code, $weight = self::DEFAULT_WEIGHT, $region = self: * @return CRM_Core_Resources */ public function addVars($nameSpace, $vars, $region = NULL) { - $existing = CRM_Utils_Array::value($nameSpace, CRM_Utils_Array::value('vars', $this->settings), []); - $vars = $this->mergeSettings($existing, $vars); - $this->addSetting(['vars' => [$nameSpace => $vars]], $region); + $s = &$this->findCreateSettingSnippet($region); + $s['settings']['vars'][$nameSpace] = $this->mergeSettings( + $s['settings']['vars'][$nameSpace] ?? [], + $vars + ); return $this; } @@ -287,22 +275,8 @@ public function addVars($nameSpace, $vars, $region = NULL) { * @return CRM_Core_Resources */ public function addSetting($settings, $region = NULL) { - if (!$region) { - $region = self::isAjaxMode() ? 'ajax-snippet' : 'html-header'; - } - $this->settings = $this->mergeSettings($this->settings, $settings); - if (isset($this->addedSettings[$region])) { - return $this; - } - $resources = $this; - $settingsResource = [ - 'callback' => function (&$snippet, &$html) use ($resources, $region) { - $html .= "\n" . $resources->renderSetting($region); - }, - 'weight' => -100000, - ]; - CRM_Core_Region::instance($region)->add($settingsResource); - $this->addedSettings[$region] = TRUE; + $s = &$this->findCreateSettingSnippet($region); + $s['settings'] = $this->mergeSettings($s['settings'], $settings); return $this; } @@ -310,21 +284,23 @@ public function addSetting($settings, $region = NULL) { * Add JavaScript variables to the global CRM object via a callback function. * * @param callable $callable + * @param string|NULL $region * @return CRM_Core_Resources */ - public function addSettingsFactory($callable) { - // Make sure our callback has been registered - $this->addSetting([]); - $this->settingsFactories[] = $callable; + public function addSettingsFactory($callable, $region = NULL) { + $s = &$this->findCreateSettingSnippet($region); + $s['settingsFactories'][] = $callable; return $this; } /** * Helper fn for addSettingsFactory. + * @deprecated */ - public function getSettings() { - $result = $this->settings; - foreach ($this->settingsFactories as $callable) { + public function getSettings($region = NULL) { + $s = &$this->findCreateSettingSnippet($region); + $result = $s['settings']; + foreach ($s['settingsFactories'] as $callable) { $result = $this->mergeSettings($result, $callable()); } CRM_Utils_Hook::alterResourceSettings($result); @@ -348,17 +324,28 @@ protected function mergeSettings($settings, $additions) { } /** - * Helper fn for addSetting. - * Render JavaScript variables for the global CRM object. - * - * @return string + * @param string $regionName + * @return array */ - public function renderSetting($region = NULL) { - $vars = json_encode($this->getSettings(), JSON_UNESCAPED_SLASHES); - $js = "(function(vars) { - if (window.CRM) CRM.$.extend(true, CRM, vars); else window.CRM = vars; -})($vars)"; - return sprintf("\n", $js); + private function &findCreateSettingSnippet($regionName) { + if (!$regionName) { + $regionName = self::isAjaxMode() ? 'ajax-snippet' : 'html-header'; + } + + $region = CRM_Core_Region::instance($regionName); + $snippet = &$region->get('settings'); + if ($snippet !== NULL) { + return $snippet; + } + + $region->add([ + 'name' => 'settings', + 'type' => 'settings', + 'settings' => [], + 'settingsFactories' => [], + 'weight' => -100000, + ]); + return $region->get('settings'); } /** diff --git a/tests/phpunit/CRM/Core/ResourcesTest.php b/tests/phpunit/CRM/Core/ResourcesTest.php index 629ba4f80320..f39a7a329fd6 100644 --- a/tests/phpunit/CRM/Core/ResourcesTest.php +++ b/tests/phpunit/CRM/Core/ResourcesTest.php @@ -150,7 +150,7 @@ public function testAddSetting() { ['fruit' => ['yours' => 'orange', 'mine' => 'apple']], $this->res->getSettings() ); - $actual = $this->res->renderSetting(); + $actual = CRM_Core_Region::instance('html-header')->render(''); $expected = json_encode(['fruit' => ['yours' => 'orange', 'mine' => 'apple']]); $this->assertTrue(strpos($actual, $expected) !== FALSE); } From 8dbd7691a10621ae45b9113f9d79946cdd4fd2b5 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 5 Aug 2020 03:51:14 -0700 Subject: [PATCH 102/834] CollectionTrait - Create baseline trait based on CRM_Core_Region --- CRM/Core/Region.php | 143 ++------------------- CRM/Core/Resources/CollectionTrait.php | 168 +++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 133 deletions(-) create mode 100644 CRM/Core/Resources/CollectionTrait.php diff --git a/CRM/Core/Region.php b/CRM/Core/Region.php index d87664019dc9..0d6cfc63301d 100644 --- a/CRM/Core/Region.php +++ b/CRM/Core/Region.php @@ -20,6 +20,10 @@ public static function &instance($name, $autocreate = TRUE) { return Civi::$statics[__CLASS__][$name]; } + use CRM_Core_Resources_CollectionTrait { + CRM_Core_Resources_CollectionTrait::add as _add; + } + /** * Symbolic name of this region * @@ -27,28 +31,13 @@ public static function &instance($name, $autocreate = TRUE) { */ public $_name; - /** - * List of snippets to inject within region. - * - * e.g. $this->_snippets[3]['type'] = 'template'; - * - * @var array - */ - public $_snippets; - - /** - * Whether the snippets array has been sorted - * - * @var bool - */ - public $_isSorted; - /** * @param string $name */ public function __construct($name) { $this->_name = $name; - $this->_snippets = []; + $this->types = ['markup', 'template', 'callback', 'scriptUrl', 'script', 'jquery', 'settings', 'style', 'styleUrl']; + $this->defaults['region'] = $name; // Placeholder which represents any of the default content generated by the main Smarty template $this->add([ @@ -57,92 +46,6 @@ public function __construct($name) { 'markup' => '', 'weight' => 0, ]); - $this->_isSorted = TRUE; - } - - /** - * Add a snippet of content to a region. - * - * ``` - * CRM_Core_Region::instance('page-header')->add(array( - * 'markup' => '
Hello!
', - * )); - * CRM_Core_Region::instance('page-header')->add(array( - * 'script' => 'alert("Hello");', - * )); - * CRM_Core_Region::instance('page-header')->add(array( - * 'template' => 'CRM/Myextension/Extra.tpl', - * )); - * CRM_Core_Region::instance('page-header')->add(array( - * 'callback' => 'myextension_callback_function', - * )); - * ``` - * - * Note: This function does not perform any extra encoding of markup, script code, or etc. If - * you're passing in user-data, you must clean it yourself. - * - * @param array $snippet - * Array; keys:. - * - type: string (auto-detected for markup, template, callback, script, scriptUrl, jquery, style, styleUrl) - * - name: string, optional - * - weight: int, optional; default=1 - * - disabled: int, optional; default=0 - * - markup: string, HTML; required (for type==markup) - * - template: string, path; required (for type==template) - * - callback: mixed; required (for type==callback) - * - arguments: array, optional (for type==callback) - * - script: string, Javascript code - * - scriptUrl: string, URL of a Javascript file - * - jquery: string, Javascript code which runs inside a jQuery(function($){...}); block - * - settings: array, list of static values to convey. - * - style: string, CSS code - * - styleUrl: string, URL of a CSS file - * - * @return array - */ - public function add($snippet) { - static $types = ['markup', 'template', 'callback', 'scriptUrl', 'script', 'jquery', 'settings', 'style', 'styleUrl']; - $defaults = [ - 'region' => $this->_name, - 'weight' => 1, - 'disabled' => FALSE, - ]; - $snippet += $defaults; - if (!isset($snippet['type'])) { - foreach ($types as $type) { - // auto-detect - if (isset($snippet[$type])) { - $snippet['type'] = $type; - break; - } - } - } - if (!isset($snippet['name'])) { - $snippet['name'] = count($this->_snippets); - } - - $this->_snippets[$snippet['name']] = $snippet; - $this->_isSorted = FALSE; - return $snippet; - } - - /** - * @param string $name - * @param $snippet - */ - public function update($name, $snippet) { - $this->_snippets[$name] = array_merge($this->_snippets[$name], $snippet); - $this->_isSorted = FALSE; - } - - /** - * Get snippet. - * - * @param string $name - * @return array|NULL - */ - public function &get($name) { - return $this->_snippets[$name]; } /** @@ -156,20 +59,17 @@ public function &get($name) { */ public function render($default, $allowCmsOverride = TRUE) { // $default is just another part of the region - if (is_array($this->_snippets['default'])) { - $this->_snippets['default']['markup'] = $default; + if (is_array($this->snippets['default'])) { + $this->snippets['default']['markup'] = $default; } // We hand as much of the work off to the CMS as possible $cms = CRM_Core_Config::singleton()->userSystem; - if (!$this->_isSorted) { - uasort($this->_snippets, ['CRM_Core_Region', '_cmpSnippet']); - $this->_isSorted = TRUE; - } + $this->sort(); $smarty = CRM_Core_Smarty::singleton(); $html = ''; - foreach ($this->_snippets as $snippet) { + foreach ($this->snippets as $snippet) { if ($snippet['disabled']) { continue; } @@ -233,27 +133,4 @@ public function render($default, $allowCmsOverride = TRUE) { return $html; } - /** - * @param $a - * @param $b - * - * @return int - */ - public static function _cmpSnippet($a, $b) { - if ($a['weight'] < $b['weight']) { - return -1; - } - if ($a['weight'] > $b['weight']) { - return 1; - } - // fallback to name sort; don't really want to do this, but it makes results more stable - if ($a['name'] < $b['name']) { - return -1; - } - if ($a['name'] > $b['name']) { - return 1; - } - return 0; - } - } diff --git a/CRM/Core/Resources/CollectionTrait.php b/CRM/Core/Resources/CollectionTrait.php new file mode 100644 index 000000000000..3cb245ca1748 --- /dev/null +++ b/CRM/Core/Resources/CollectionTrait.php @@ -0,0 +1,168 @@ + 1, 'disabled' => FALSE]; + + /** + * List of snippets to inject within region. + * + * e.g. $this->_snippets[3]['type'] = 'template'; + * + * @var array + */ + protected $snippets = []; + + /** + * Whether the snippets array has been sorted + * + * @var bool + */ + protected $isSorted = TRUE; + + /** + * Whitelist of supported types. + * + * @var array + */ + protected $types = []; + + /** + * Add an item to the collection. For example, when working with 'page-header' collection: + * + * ``` + * CRM_Core_Region::instance('page-header')->add(array( + * 'markup' => '
Hello!
', + * )); + * CRM_Core_Region::instance('page-header')->add(array( + * 'script' => 'alert("Hello");', + * )); + * CRM_Core_Region::instance('page-header')->add(array( + * 'template' => 'CRM/Myextension/Extra.tpl', + * )); + * CRM_Core_Region::instance('page-header')->add(array( + * 'callback' => 'myextension_callback_function', + * )); + * ``` + * + * Note: This function does not perform any extra encoding of markup, script code, or etc. If + * you're passing in user-data, you must clean it yourself. + * + * @param array $snippet + * Array; keys:. + * - type: string (auto-detected for markup, template, callback, script, scriptUrl, jquery, style, styleUrl) + * - name: string, optional + * - weight: int, optional; default=1 + * - disabled: int, optional; default=0 + * - markup: string, HTML; required (for type==markup) + * - template: string, path; required (for type==template) + * - callback: mixed; required (for type==callback) + * - arguments: array, optional (for type==callback) + * - script: string, Javascript code + * - scriptUrl: string, URL of a Javascript file + * - jquery: string, Javascript code which runs inside a jQuery(function($){...}); block + * - settings: array, list of static values to convey. + * - style: string, CSS code + * - styleUrl: string, URL of a CSS file + * + * @return array + * The full/computed snippet (with defaults applied). + */ + public function add($snippet) { + $snippet = array_merge($this->defaults, $snippet); + if (!isset($snippet['type'])) { + foreach ($this->types as $type) { + // auto-detect + if (isset($snippet[$type])) { + $snippet['type'] = $type; + break; + } + } + } + elseif (!in_array($snippet['type'], $this->types)) { + throw new \RuntimeException("Unsupported snippet type: " . $snippet['type']); + } + if (!isset($snippet['name'])) { + $snippet['name'] = count($this->snippets); + } + + $this->snippets[$snippet['name']] = $snippet; + $this->isSorted = FALSE; + return $snippet; + } + + /** + * @param string $name + * @param $snippet + */ + public function update($name, $snippet) { + $this->snippets[$name] = array_merge($this->snippets[$name], $snippet); + $this->isSorted = FALSE; + } + + /** + * Get snippet. + * + * @param string $name + * @return array|NULL + */ + public function &get($name) { + return $this->snippets[$name]; + } + + /** + * Ensure that the collection is sorted. + * + * @return static + */ + protected function sort() { + if (!$this->isSorted) { + uasort($this->snippets, [__CLASS__, '_cmpSnippet']); + $this->isSorted = TRUE; + } + return $this; + } + + /** + * @param $a + * @param $b + * + * @return int + */ + public static function _cmpSnippet($a, $b) { + if ($a['weight'] < $b['weight']) { + return -1; + } + if ($a['weight'] > $b['weight']) { + return 1; + } + // fallback to name sort; don't really want to do this, but it makes results more stable + if ($a['name'] < $b['name']) { + return -1; + } + if ($a['name'] > $b['name']) { + return 1; + } + return 0; + } + +} From b41dbde6bfe916bc4084557ec9163ea446dcccfd Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 5 Aug 2020 05:44:00 -0700 Subject: [PATCH 103/834] (REF) CRM_Core_Region - Extract $renderSnippet() This makes it possible for renderSnippet to work recursively. For example, the current `jquery` type can build on the `script` type, and the hypothetical `scriptFile` type could build on the `scriptUrl` type. --- CRM/Core/Region.php | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/CRM/Core/Region.php b/CRM/Core/Region.php index 0d6cfc63301d..6c5da95e102d 100644 --- a/CRM/Core/Region.php +++ b/CRM/Core/Region.php @@ -62,17 +62,14 @@ public function render($default, $allowCmsOverride = TRUE) { if (is_array($this->snippets['default'])) { $this->snippets['default']['markup'] = $default; } - // We hand as much of the work off to the CMS as possible - $cms = CRM_Core_Config::singleton()->userSystem; $this->sort(); + $cms = CRM_Core_Config::singleton()->userSystem; $smarty = CRM_Core_Smarty::singleton(); $html = ''; - foreach ($this->snippets as $snippet) { - if ($snippet['disabled']) { - continue; - } + + $renderSnippet = function($snippet) use (&$html, $smarty, $cms, $allowCmsOverride, &$renderSnippet) { switch ($snippet['type']) { case 'markup': $html .= $snippet['markup']; @@ -97,8 +94,12 @@ public function render($default, $allowCmsOverride = TRUE) { break; case 'jquery': - $snippet['script'] = sprintf("CRM.\$(function(\$) {\n%s\n});", $snippet['jquery']); - // no break - continue processing as script + $renderSnippet([ + 'type' => 'script', + 'script' => sprintf("CRM.\$(function(\$) {\n%s\n});", $snippet['jquery']), + ]); + break; + case 'script': if (!$allowCmsOverride || !$cms->addScript($snippet['script'], $this->_name)) { $html .= sprintf("\n", $snippet['script']); @@ -129,6 +130,12 @@ public function render($default, $allowCmsOverride = TRUE) { throw new CRM_Core_Exception(ts('Snippet type %1 is unrecognized', [1 => $snippet['type']])); } + }; + + foreach ($this->snippets as $snippet) { + if (empty($snippet['disabled'])) { + $renderSnippet($snippet); + } } return $html; } From 04615f53c081944706ae87e16746a1209b9abc12 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 5 Aug 2020 04:47:34 -0700 Subject: [PATCH 104/834] CollectionTrait - Support getAll(), filter(), find(), and clear() --- CRM/Core/Resources/CollectionTrait.php | 72 ++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/CRM/Core/Resources/CollectionTrait.php b/CRM/Core/Resources/CollectionTrait.php index 3cb245ca1748..8b09a1cb4090 100644 --- a/CRM/Core/Resources/CollectionTrait.php +++ b/CRM/Core/Resources/CollectionTrait.php @@ -119,6 +119,17 @@ public function update($name, $snippet) { $this->isSorted = FALSE; } + /** + * Remove all snippets. + * + * @return static + */ + public function clear() { + $this->snippets = []; + $this->isSorted = TRUE; + return $this; + } + /** * Get snippet. * @@ -129,6 +140,67 @@ public function &get($name) { return $this->snippets[$name]; } + /** + * Get a list of all snippets in this collection. + * + * @return iterable + */ + public function getAll(): iterable { + $this->sort(); + return $this->snippets; + } + + /** + * Alter the contents of the collection. + * + * @param callable $callback + * The callback is invoked once for each member in the collection. + * The callback may return one of three values: + * - TRUE: The item is OK and belongs in the collection. + * - FALSE: The item is not OK and should be omitted from the collection. + * - Array: The item should be revised (using the returned value). + * @return static + */ + public function filter($callback) { + $this->sort(); + $names = array_keys($this->snippets); + foreach ($names as $name) { + $ret = $callback($this->snippets[$name]); + if ($ret === TRUE) { + // OK + } + elseif ($ret === FALSE) { + unset($this->snippets[$name]); + } + elseif (is_array($ret)) { + $this->snippets[$name] = $ret; + $this->isSorted = FALSE; + } + else { + throw new \RuntimeException("CollectionTrait::filter() - Callback returned invalid value"); + } + } + return $this; + } + + /** + * Find all snippets which match the given criterion. + * + * @param callable $callback + * @return iterable + * List of matching snippets. + */ + public function find($callback): iterable { + $r = []; + $this->sort(); + foreach ($this->snippets as $name => $snippet) { + if ($callback($snippet)) { + $r[$name] = $snippet; + } + } + return $r; + } + /** * Ensure that the collection is sorted. * From 007b7d354d82d5e0267a76904850543f8a905bc4 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 5 Aug 2020 04:11:25 -0700 Subject: [PATCH 105/834] CollectionTrait - Support addScript(), addScriptUrl(), addStyle(), addStyleUrl() --- CRM/Core/Resources.php | 32 ++--------- CRM/Core/Resources/CollectionTrait.php | 75 +++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 29 deletions(-) diff --git a/CRM/Core/Resources.php b/CRM/Core/Resources.php index 0d086f40c908..a0d9d0ca991e 100644 --- a/CRM/Core/Resources.php +++ b/CRM/Core/Resources.php @@ -203,13 +203,7 @@ public function addScriptFile($ext, $file, $weight = self::DEFAULT_WEIGHT, $regi * @return CRM_Core_Resources */ public function addScriptUrl($url, $weight = self::DEFAULT_WEIGHT, $region = self::DEFAULT_REGION) { - CRM_Core_Region::instance($region)->add([ - 'name' => $url, - 'type' => 'scriptUrl', - 'scriptUrl' => $url, - 'weight' => $weight, - 'region' => $region, - ]); + CRM_Core_Region::instance($region)->add(['scriptUrl' => $url, 'weight' => $weight]); return $this; } @@ -225,13 +219,7 @@ public function addScriptUrl($url, $weight = self::DEFAULT_WEIGHT, $region = sel * @return CRM_Core_Resources */ public function addScript($code, $weight = self::DEFAULT_WEIGHT, $region = self::DEFAULT_REGION) { - CRM_Core_Region::instance($region)->add([ - // 'name' => automatic - 'type' => 'script', - 'script' => $code, - 'weight' => $weight, - 'region' => $region, - ]); + CRM_Core_Region::instance($region)->add(['script' => $code, 'weight' => $weight]); return $this; } @@ -430,13 +418,7 @@ public function addStyleFile($ext, $file, $weight = self::DEFAULT_WEIGHT, $regio * @return CRM_Core_Resources */ public function addStyleUrl($url, $weight = self::DEFAULT_WEIGHT, $region = self::DEFAULT_REGION) { - CRM_Core_Region::instance($region)->add([ - 'name' => $url, - 'type' => 'styleUrl', - 'styleUrl' => $url, - 'weight' => $weight, - 'region' => $region, - ]); + CRM_Core_Region::instance($region)->add(['styleUrl' => $url, 'weight' => $weight]); return $this; } @@ -452,13 +434,7 @@ public function addStyleUrl($url, $weight = self::DEFAULT_WEIGHT, $region = self * @return CRM_Core_Resources */ public function addStyle($code, $weight = self::DEFAULT_WEIGHT, $region = self::DEFAULT_REGION) { - CRM_Core_Region::instance($region)->add([ - // 'name' => automatic - 'type' => 'style', - 'style' => $code, - 'weight' => $weight, - 'region' => $region, - ]); + CRM_Core_Region::instance($region)->add(['style' => $code, 'weight' => $weight]); return $this; } diff --git a/CRM/Core/Resources/CollectionTrait.php b/CRM/Core/Resources/CollectionTrait.php index 8b09a1cb4090..cdf07da9c676 100644 --- a/CRM/Core/Resources/CollectionTrait.php +++ b/CRM/Core/Resources/CollectionTrait.php @@ -13,6 +13,10 @@ * Class CRM_Core_Resources_CollectionTrait * * This is a building-block for creating classes which maintain a list of resources. + * + * The class is generally organized in two sections: First, we have core + * bit that manages a list of '$snippets'. Second, we have a set of helper + * functions which add some syntactic sugar for the snippets. */ trait CRM_Core_Resources_CollectionTrait { @@ -102,7 +106,16 @@ public function add($snippet) { throw new \RuntimeException("Unsupported snippet type: " . $snippet['type']); } if (!isset($snippet['name'])) { - $snippet['name'] = count($this->snippets); + switch ($snippet['type']) { + case 'scriptUrl': + case 'styleUrl': + $snippet['name'] = $snippet[$snippet['type']]; + break; + + default: + $snippet['name'] = count($this->snippets); + break; + } } $this->snippets[$snippet['name']] = $snippet; @@ -237,4 +250,64 @@ public static function _cmpSnippet($a, $b) { return 0; } + // ----------------------------------------------- + + /** + * Add a JavaScript file to the current page using \n", $snippet['script']); } break; + case 'styleFile': + foreach ($snippet['styleFileUrls'] as $url) { + $html .= $renderSnippet(['type' => 'styleUrl', 'styleUrl' => $url] + $snippet); + } + break; + case 'styleUrl': if (!$allowCmsOverride || !$cms->addStyleUrl($snippet['styleUrl'], $this->_name)) { $html .= sprintf("\n", $snippet['styleUrl']); diff --git a/CRM/Core/Resources/CollectionTrait.php b/CRM/Core/Resources/CollectionTrait.php index 35147c48b737..38d259bc7337 100644 --- a/CRM/Core/Resources/CollectionTrait.php +++ b/CRM/Core/Resources/CollectionTrait.php @@ -102,27 +102,55 @@ public function add($snippet) { } } } - elseif (!in_array($snippet['type'], $this->types)) { - throw new \RuntimeException("Unsupported snippet type: " . $snippet['type']); + if (!in_array($snippet['type'] ?? NULL, $this->types)) { + $typeExpr = $snippet['type'] ?? '(' . implode(',', array_keys($snippet)) . ')'; + throw new \RuntimeException("Unsupported snippet type: $typeExpr"); } // Traditional behavior: sort by (1) weight and (2) either name or natural position. This second thing is called 'sortId'. if (isset($snippet['name'])) { $snippet['sortId'] = $snippet['name']; } else { - $snippet['sortId'] = $this->nextId(); switch ($snippet['type']) { case 'scriptUrl': case 'styleUrl': + $snippet['sortId'] = $this->nextId(); $snippet['name'] = $snippet[$snippet['type']]; break; + case 'scriptFile': + case 'styleFile': + $snippet['sortId'] = $this->nextId(); + $snippet['name'] = implode(':', $snippet[$snippet['type']]); + break; + default: + $snippet['sortId'] = $this->nextId(); $snippet['name'] = $snippet['sortId']; break; } } + if ($snippet['type'] === 'scriptFile' && !isset($snippet['scriptFileUrls'])) { + $res = Civi::resources(); + list ($ext, $file) = $snippet['scriptFile']; + + $snippet['translate'] = $snippet['translate'] ?? TRUE; + if ($snippet['translate']) { + $domain = ($snippet['translate'] === TRUE) ? $ext : $snippet['translate']; + // Is this too early? + $this->addString(Civi::service('resources.js_strings')->get($domain, $res->getPath($ext, $file), 'text/javascript'), $domain); + } + $snippet['scriptFileUrls'] = [$res->getUrl($ext, $res->filterMinify($ext, $file), TRUE)]; + } + + if ($snippet['type'] === 'styleFile' && !isset($snippet['styleFileUrls'])) { + /** @var Civi\Core\Themes $theme */ + $theme = Civi::service('themes'); + list ($ext, $file) = $snippet['styleFile']; + $snippet['styleFileUrls'] = $theme->resolveUrls($theme->getActiveThemeKey(), $ext, $file); + } + $this->snippets[$snippet['name']] = $snippet; $this->isSorted = FALSE; return $snippet; @@ -361,18 +389,7 @@ public function addScript(string $code, array $options = []) { * @throws \CRM_Core_Exception */ public function addScriptFile(string $ext, string $file, array $options = []) { - // TODO: Maybe this should be its own resource type to allow smarter management? - - $res = Civi::resources(); - - $translate = $options['translate'] ?? TRUE; - unset($options['translate']); - if ($translate) { - $domain = ($translate === TRUE) ? $ext : $translate; - $this->addString(Civi::service('resources.js_strings')->get($domain, $res->getPath($ext, $file), 'text/javascript'), $domain); - } - $url = $res->getUrl($ext, $res->filterMinify($ext, $file), TRUE); - $this->add($options + ['scriptUrl' => $url, 'name' => "$ext:$file"]); + $this->add($options + ['scriptFile' => [$ext, $file]]); return $this; } @@ -469,13 +486,7 @@ public function addStyle(string $code, array $options = []) { * @return static */ public function addStyleFile(string $ext, string $file, array $options = []) { - // TODO: Maybe this should be its own resource type to allow smarter management? - - /** @var Civi\Core\Themes $theme */ - $theme = Civi::service('themes'); - foreach ($theme->resolveUrls($theme->getActiveThemeKey(), $ext, $file) as $url) { - $this->add($options + ['styleUrl' => $url, 'name' => "$ext:$file"]); - } + $this->add($options + ['styleFile' => [$ext, $file]]); return $this; } From fcf926ad4ab1df90dd9d553a75f7385124420220 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Sun, 23 Aug 2020 18:49:44 -0700 Subject: [PATCH 110/834] Add "Bundle" support - ie an unattached, mixable list of resources --- CRM/Core/Resources.php | 57 ++++++++++++++++++++++++++ CRM/Core/Resources/Bundle.php | 38 +++++++++++++++++ CRM/Core/Resources/CollectionTrait.php | 21 ++++++++++ 3 files changed, 116 insertions(+) create mode 100644 CRM/Core/Resources/Bundle.php diff --git a/CRM/Core/Resources.php b/CRM/Core/Resources.php index 9b694bb03a19..4265fee1cf14 100644 --- a/CRM/Core/Resources.php +++ b/CRM/Core/Resources.php @@ -45,6 +45,15 @@ class CRM_Core_Resources { */ private $strings = NULL; + /** + * Any bundles that have been added. + * + * Format is ($bundleName => bool). + * + * @var array + */ + protected $addedBundles = []; + /** * Added core resources. * @@ -139,6 +148,54 @@ public function __construct($extMapper, $strings, $cacheCodeKey = NULL) { $this->paths = Civi::paths(); } + /** + * Assimilate all the resources listed in a bundle. + * + * @param iterable|string|\CRM_Core_Resources_Bundle $bundle + * Either bundle object, or the symbolic name of a bundle, or a list of budnles. + * Note: For symbolic names, the bundle must be a container service ('bundle.FOO'). + * @return static + */ + public function addBundle($bundle) { + if (is_iterable($bundle)) { + foreach ($bundle as $b) { + $this->addBundle($b); + return $this; + } + } + + if (is_string($bundle)) { + $bundle = Civi::service('bundle.' . $bundle); + } + + if (isset($this->addedBundles[$bundle->name])) { + return $this; + } + $this->addedBundles[$bundle->name] = TRUE; + + // If an item is already assigned to a region, we'll respect that. + // Otherwise, we'll use defaults. + $pickRegion = function ($snippet) { + if (isset($snippet['settings'])) { + return $this->getSettingRegion($snippet['region'] ?? NULL)->_name; + } + else { + return $snippet['region'] ?? self::DEFAULT_REGION; + } + }; + + $byRegion = []; + foreach ($bundle->getAll() as $snippet) { + $snippet['region'] = $pickRegion($snippet); + $byRegion[$snippet['region']][$snippet['name']] = $snippet; + } + + foreach ($byRegion as $regionName => $snippets) { + CRM_Core_Region::instance($regionName)->merge($snippets); + } + return $this; + } + /** * Export permission data to the client to enable smarter GUIs. * diff --git a/CRM/Core/Resources/Bundle.php b/CRM/Core/Resources/Bundle.php new file mode 100644 index 000000000000..311dc2a05d39 --- /dev/null +++ b/CRM/Core/Resources/Bundle.php @@ -0,0 +1,38 @@ +name = $name; + $this->types = ['script', 'scriptFile', 'scriptUrl', 'settings', 'style', 'styleFile', 'styleUrl']; + } + +} diff --git a/CRM/Core/Resources/CollectionTrait.php b/CRM/Core/Resources/CollectionTrait.php index 38d259bc7337..b402e61ea797 100644 --- a/CRM/Core/Resources/CollectionTrait.php +++ b/CRM/Core/Resources/CollectionTrait.php @@ -328,6 +328,27 @@ public static function _cmpSnippet($a, $b) { // ----------------------------------------------- + /** + * Assimilate all the resources listed in a bundle. + * + * @param iterable|string|\CRM_Core_Resources_Bundle $bundle + * Either bundle object, or the symbolic name of a bundle. + * Note: For symbolic names, the bundle must be a container service ('bundle.FOO'). + * @return static + */ + public function addBundle($bundle) { + if (is_iterable($bundle)) { + foreach ($bundle as $b) { + $this->addBundle($b); + return $this; + } + } + if (is_string($bundle)) { + $bundle = Civi::service('bundle.' . $bundle); + } + return $this->merge($bundle->getAll()); + } + /** * Export permission data to the client to enable smarter GUIs. * From 060617e9df668cd7d422b6b32276364cd3368310 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Mon, 17 Aug 2020 20:45:29 -0700 Subject: [PATCH 111/834] CollectionTrait - Use "splats". Split out "adders". Define interfaces. In the prior commits, the signatures for `addScriptFile()`, `addScriptUrl()`, etc are not strictly interoperable between `CRM_Core_Resources` and `CollectionTrait`. This is because they use key-value options instead of positional options. This makes it easier disregard positional options that don't make sense (e.g. when calling `CRM_Core_Region::addScriptFile()`, it's silly to reserve a positional argument for the `$region` option). The signatures *can* be unified by using "splats" (ie `...$options`) to accept either key-value options or backward-compatible positional options. The ultimate is hope is that: * `CRM_Core_Resources`, `CRM_Core_Region`, and `CRM_Core_Bundle` all implement the `CollectionAdderInterface`. * `CRM_Core_Resources`, `CRM_Core_Region`, and `CRM_Core_Bundle` all accept options in either format (key-value or positional). * The positional format will fade-away. The methods in CollectionTrait are newer terrain, so it's safer to change those signatures, so we do that first. Note that CollectionTrait formally builds on CollectionAdderTrait. This ensures that IDE navigation to (eg) `CRM_Core_Region::add()` and `CRM_Core_Resources_Bundle::add()` works as expected. --- CRM/Core/Region.php | 6 +- .../Resources/CollectionAdderInterface.php | 208 ++++++++++ CRM/Core/Resources/CollectionAdderTrait.php | 382 ++++++++++++++++++ CRM/Core/Resources/CollectionInterface.php | 133 ++++++ CRM/Core/Resources/CollectionTrait.php | 249 +----------- 5 files changed, 729 insertions(+), 249 deletions(-) create mode 100644 CRM/Core/Resources/CollectionAdderInterface.php create mode 100644 CRM/Core/Resources/CollectionAdderTrait.php create mode 100644 CRM/Core/Resources/CollectionInterface.php diff --git a/CRM/Core/Region.php b/CRM/Core/Region.php index 7a31455005ff..c7eab5c58418 100644 --- a/CRM/Core/Region.php +++ b/CRM/Core/Region.php @@ -3,7 +3,7 @@ /** * Maintain a set of markup/templates to inject inside various regions */ -class CRM_Core_Region { +class CRM_Core_Region implements CRM_Core_Resources_CollectionInterface, CRM_Core_Resources_CollectionAdderInterface { /** * Obtain the content for a given region. @@ -20,9 +20,7 @@ public static function &instance($name, $autocreate = TRUE) { return Civi::$statics[__CLASS__][$name]; } - use CRM_Core_Resources_CollectionTrait { - CRM_Core_Resources_CollectionTrait::add as _add; - } + use CRM_Core_Resources_CollectionTrait; /** * Symbolic name of this region diff --git a/CRM/Core/Resources/CollectionAdderInterface.php b/CRM/Core/Resources/CollectionAdderInterface.php new file mode 100644 index 000000000000..5f435ce1b840 --- /dev/null +++ b/CRM/Core/Resources/CollectionAdderInterface.php @@ -0,0 +1,208 @@ +. + * + * @param string $code + * JavaScript source code. + * @param array $options + * Open-ended list of options (per add()) + * Ex: ['weight' => 123] + * @return static + */ + public function addScript(string $code, ...$options); + + /** + * Add a JavaScript file to the current page using From 09e6f54112d5091826792bd29dc36d0e196d79fd Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 31 Aug 2020 18:28:20 +1200 Subject: [PATCH 204/834] dev/core#1603 fix tangental bug on form handling of long options This fixes the bug that derailed the fix for https://lab.civicrm.org/dev/core/-/issues/1603 On further testing I agree with Jitendra that the price field form was mis-saving the longer values (I only tested with Euro style decimal separators but maybe on both) and it was bad data caused by this that made the merged fix look like a regression. I also observed that the Money functions intended to round only appeared to since it was rounding just fine to 2 places, as tested, but it was still rounding to 2 places when we wanted more. This fixes both the New Price field screen (when options are entered) and the edit options screen --- CRM/Price/Form/Field.php | 3 +++ CRM/Price/Form/Option.php | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CRM/Price/Form/Field.php b/CRM/Price/Form/Field.php index 571ab77be16e..023066db68b5 100644 --- a/CRM/Price/Form/Field.php +++ b/CRM/Price/Form/Field.php @@ -642,6 +642,9 @@ public function postProcess() { // store the submitted values in an array $params = $this->controller->exportValues('Field'); $params['price'] = CRM_Utils_Rule::cleanMoney($params['price']); + foreach ($params['option_amount'] as $key => $amount) { + $params['option_amount'][$key] = CRM_Utils_Rule::cleanMoney($amount); + } $params['is_display_amounts'] = CRM_Utils_Array::value('is_display_amounts', $params, FALSE); $params['is_required'] = CRM_Utils_Array::value('is_required', $params, FALSE); diff --git a/CRM/Price/Form/Option.php b/CRM/Price/Form/Option.php index e5f7fc1aaac8..adf22e19a862 100644 --- a/CRM/Price/Form/Option.php +++ b/CRM/Price/Form/Option.php @@ -75,6 +75,9 @@ public function setDefaultValues() { // fix the display of the monetary value, CRM-4038 foreach ($this->_moneyFields as $field) { + // @todo use formatLocaleNumericRoundedByOptionalPrecision - but note that there is an issue where php doesn't handle + // money strings beyond a certain total length - per https://github.com/civicrm/civicrm-core/pull/18409 + // & https://github.com/civicrm/civicrm-core/pull/18409 $defaults[$field] = CRM_Utils_Money::format(CRM_Utils_Array::value($field, $defaults), NULL, '%a'); } } @@ -326,7 +329,6 @@ public function postProcess() { return NULL; } else { - $params = $ids = []; $params = $this->controller->exportValues('Option'); $fieldLabel = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $this->_fid, 'label'); From 3741f351db785a5ed1f1459962b7e40696713996 Mon Sep 17 00:00:00 2001 From: Jon Goldberg Date: Thu, 18 Jun 2020 14:27:17 -0400 Subject: [PATCH 205/834] core#1590: Don't send reminders to add'l recipients on deleted/inactive/template events support reminders with event types/templates and multiple event/type/template IDs fix --- CRM/Activity/ActionMapping.php | 8 +++++ CRM/Contact/ActionMapping.php | 8 +++++ CRM/Contribute/ActionMapping/ByPage.php | 8 +++++ CRM/Contribute/ActionMapping/ByType.php | 8 +++++ CRM/Event/ActionMapping.php | 46 ++++++++++++++++++++++++ CRM/Member/ActionMapping.php | 8 +++++ Civi/ActionSchedule/Mapping.php | 6 ++++ Civi/ActionSchedule/MappingInterface.php | 6 ++++ Civi/ActionSchedule/RecipientBuilder.php | 25 ++----------- tests/phpunit/api/v3/JobTest.php | 35 ++++++++++++++++++ 10 files changed, 135 insertions(+), 23 deletions(-) diff --git a/CRM/Activity/ActionMapping.php b/CRM/Activity/ActionMapping.php index 05abcf9e24c9..9f73592644b7 100644 --- a/CRM/Activity/ActionMapping.php +++ b/CRM/Activity/ActionMapping.php @@ -118,4 +118,12 @@ public function createQuery($schedule, $phase, $defaultParams) { return $query; } + /** + * Determine whether a schedule based on this mapping should + * send to additional contacts. + */ + public function sendToAdditional($entityId): bool { + return TRUE; + } + } diff --git a/CRM/Contact/ActionMapping.php b/CRM/Contact/ActionMapping.php index f4aee154e830..ea96b5da7863 100644 --- a/CRM/Contact/ActionMapping.php +++ b/CRM/Contact/ActionMapping.php @@ -139,4 +139,12 @@ public function createQuery($schedule, $phase, $defaultParams) { return $query; } + /** + * Determine whether a schedule based on this mapping should + * send to additional contacts. + */ + public function sendToAdditional($entityId): bool { + return TRUE; + } + } diff --git a/CRM/Contribute/ActionMapping/ByPage.php b/CRM/Contribute/ActionMapping/ByPage.php index 8c09c81cc274..6dcab6e2a7ae 100644 --- a/CRM/Contribute/ActionMapping/ByPage.php +++ b/CRM/Contribute/ActionMapping/ByPage.php @@ -216,4 +216,12 @@ public function resetOnTriggerDateChange($schedule) { return FALSE; } + /** + * Determine whether a schedule based on this mapping should + * send to additional contacts. + */ + public function sendToAdditional($entityId): bool { + return TRUE; + } + } diff --git a/CRM/Contribute/ActionMapping/ByType.php b/CRM/Contribute/ActionMapping/ByType.php index ea259901a569..88d60e49c767 100644 --- a/CRM/Contribute/ActionMapping/ByType.php +++ b/CRM/Contribute/ActionMapping/ByType.php @@ -235,4 +235,12 @@ public function resetOnTriggerDateChange($schedule) { return FALSE; } + /** + * Determine whether a schedule based on this mapping should + * send to additional contacts. + */ + public function sendToAdditional($entityId): bool { + return TRUE; + } + } diff --git a/CRM/Event/ActionMapping.php b/CRM/Event/ActionMapping.php index 77fa8d77edfb..9c8aaae7a507 100644 --- a/CRM/Event/ActionMapping.php +++ b/CRM/Event/ActionMapping.php @@ -159,6 +159,7 @@ public function createQuery($schedule, $phase, $defaultParams) { } // build where clause + // FIXME: This handles scheduled reminder of type "Event Name" and "Event Type", gives incorrect result on "Event Template". if (!empty($selectedValues)) { $valueField = ($this->id == \CRM_Event_ActionMapping::EVENT_TYPE_MAPPING_ID) ? 'event_type_id' : 'id'; $query->where("r.{$valueField} IN (@selectedValues)") @@ -186,4 +187,49 @@ public function createQuery($schedule, $phase, $defaultParams) { return $query; } + /** + * Determine whether a schedule based on this mapping should + * send to additional contacts. + * + * @param string $entityId Either an event ID/event type ID, or a set of event IDs/types separated + * by the separation character. + */ + public function sendToAdditional($entityId): bool { + $selectedValues = (array) \CRM_Utils_Array::explodePadded($entityId); + switch ($this->id) { + case self::EVENT_TYPE_MAPPING_ID: + $valueTable = 'e'; + $valueField = 'event_type_id'; + $templateReminder = FALSE; + break; + + case self::EVENT_NAME_MAPPING_ID: + $valueTable = 'e'; + $valueField = 'id'; + $templateReminder = FALSE; + break; + + case self::EVENT_TPL_MAPPING_ID: + $valueTable = 't'; + $valueField = 'id'; + $templateReminder = TRUE; + break; + } + // Don't send to additional recipients if this event is deleted or a template. + $query = new \CRM_Utils_SQL_Select('civicrm_event e'); + $query + ->select('e.id') + ->where("e.is_template = 0") + ->where("e.is_active = 1"); + if ($templateReminder) { + $query->join('r', 'INNER JOIN civicrm_event t ON e.template_title = t.template_title AND t.is_template = 1'); + } + $sql = $query + ->where("{$valueTable}.{$valueField} IN (@selectedValues)") + ->param('selectedValues', $selectedValues) + ->toSQL(); + $dao = \CRM_Core_DAO::executeQuery($sql); + return (bool) $dao->N; + } + } diff --git a/CRM/Member/ActionMapping.php b/CRM/Member/ActionMapping.php index 56db7eef4148..e42add868298 100644 --- a/CRM/Member/ActionMapping.php +++ b/CRM/Member/ActionMapping.php @@ -168,4 +168,12 @@ public function resetOnTriggerDateChange($schedule) { } } + /** + * Determine whether a schedule based on this mapping should + * send to additional contacts. + */ + public function sendToAdditional($entityId): bool { + return TRUE; + } + } diff --git a/Civi/ActionSchedule/Mapping.php b/Civi/ActionSchedule/Mapping.php index f6c1450d5644..fc0e667b4d85 100644 --- a/Civi/ActionSchedule/Mapping.php +++ b/Civi/ActionSchedule/Mapping.php @@ -337,4 +337,10 @@ public function resetOnTriggerDateChange($schedule) { return FALSE; } + /** + * Determine whether a schedule based on this mapping should + * send to additional contacts. + */ + abstract public function sendToAdditional($entityId): bool; + } diff --git a/Civi/ActionSchedule/MappingInterface.php b/Civi/ActionSchedule/MappingInterface.php index 3dcd04fea133..f551fc11c196 100644 --- a/Civi/ActionSchedule/MappingInterface.php +++ b/Civi/ActionSchedule/MappingInterface.php @@ -139,4 +139,10 @@ public function createQuery($schedule, $phase, $defaultParams); */ public function resetOnTriggerDateChange($schedule); + /** + * Determine whether a schedule based on this mapping should + * send to additional contacts. + */ + public function sendToAdditional($entityId): bool; + } diff --git a/Civi/ActionSchedule/RecipientBuilder.php b/Civi/ActionSchedule/RecipientBuilder.php index c8a32dd97e37..c52f64adbb65 100644 --- a/Civi/ActionSchedule/RecipientBuilder.php +++ b/Civi/ActionSchedule/RecipientBuilder.php @@ -138,7 +138,7 @@ public function __construct($now, $actionSchedule, $mapping) { public function build() { $this->buildRelFirstPass(); - if ($this->prepareAddlFilter('c.id') && $this->notTemplate()) { + if ($this->prepareAddlFilter('c.id') && $this->mapping->sendToAdditional($this->actionSchedule->entity_value)) { $this->buildAddlFirstPass(); } @@ -146,7 +146,7 @@ public function build() { $this->buildRelRepeatPass(); } - if ($this->actionSchedule->is_repeat && $this->prepareAddlFilter('c.id')) { + if ($this->actionSchedule->is_repeat && $this->prepareAddlFilter('c.id') && $this->mapping->sendToAdditional($this->actionSchedule->entity_value)) { $this->buildAddlRepeatPass(); } } @@ -603,25 +603,4 @@ protected function resetOnTriggerDateChange() { return $this->mapping->resetOnTriggerDateChange($this->actionSchedule); } - /** - * Confirm this object isn't attached to a template. - * Returns TRUE if this action schedule isn't attached to a template. - * Templates are (currently) unique to events, so we only evaluate those. - * - * @return bool; - */ - private function notTemplate() { - if ($this->mapping->getEntity() === 'civicrm_participant') { - $entityId = $this->actionSchedule->entity_value; - $query = new \CRM_Utils_SQL_Select('civicrm_event e'); - $sql = $query - ->select('is_template') - ->where("e.id = {$entityId}") - ->toSQL(); - $dao = \CRM_Core_DAO::executeQuery($sql); - return !(bool) $dao->fetchValue(); - } - return TRUE; - } - } diff --git a/tests/phpunit/api/v3/JobTest.php b/tests/phpunit/api/v3/JobTest.php index 444714204ab9..08a0188f6ef6 100644 --- a/tests/phpunit/api/v3/JobTest.php +++ b/tests/phpunit/api/v3/JobTest.php @@ -330,6 +330,41 @@ public function testTemplateRemindAddlContacts() { $this->assertEquals(0, $successfulCronCount); } + /** + * Deleted events should not send reminders to additional contacts. + * + * @throws \CRM_Core_Exception + */ + public function testDeletedEventRemindAddlContacts() { + $contactId = $this->individualCreate(); + $groupId = $this->groupCreate(['name' => 'Additional Contacts', 'title' => 'Additional Contacts']); + $this->callAPISuccess('GroupContact', 'create', [ + 'contact_id' => $contactId, + 'group_id' => $groupId, + ]); + $event = $this->eventCreate(['title' => 'delete this event']); + $eventId = $event['id']; + + $this->callAPISuccess('action_schedule', 'create', [ + 'title' => 'Do not send me', + 'subject' => 'I am a reminder attached to a (soon to be) deleted event.', + 'entity_value' => $eventId, + 'mapping_id' => CRM_Event_ActionMapping::EVENT_NAME_MAPPING_ID, + 'start_action_date' => 'start_date', + 'start_action_offset' => 1, + 'start_action_condition' => 'before', + 'start_action_unit' => 'day', + 'group_id' => $groupId, + 'limit_to' => FALSE, + 'mode' => 'Email', + ]); + $this->callAPISuccess('event', 'delete', ['id' => $eventId]); + + $this->callAPISuccess('job', 'send_reminder', []); + $successfulCronCount = CRM_Core_DAO::singleValueQuery('SELECT count(*) FROM civicrm_action_log'); + $this->assertEquals(0, $successfulCronCount); + } + /** * Test scheduled reminders respect limit to (since above identified addition_to handling issue). * From a475741355ec2dc1ae7ffbcc5cdc507ba42a99c5 Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 9 Sep 2020 16:18:45 +1200 Subject: [PATCH 206/834] [REF] Minor readability fix None of the table names in this query are localisable so we don't need to use variables to refer to them. Note the legacy dao->query() would require it if they were localisable whereas CRM_Core_DAO::executeQuery() does not --- CRM/ACL/BAO/ACL.php | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/CRM/ACL/BAO/ACL.php b/CRM/ACL/BAO/ACL.php index 988340c94d42..873e6d55b7a7 100644 --- a/CRM/ACL/BAO/ACL.php +++ b/CRM/ACL/BAO/ACL.php @@ -206,30 +206,25 @@ protected static function getGroupACLRoles($contact_id) { $rule = new CRM_ACL_BAO_ACL(); - $aclRole = 'civicrm_acl_role'; - - $aclER = CRM_ACL_DAO_EntityRole::getTableName(); - $c2g = CRM_Contact_BAO_GroupContact::getTableName(); - $query = " SELECT acl.* FROM civicrm_acl acl INNER JOIN civicrm_option_group og ON og.name = 'acl_role' INNER JOIN civicrm_option_value ov - ON acl.entity_table = '$aclRole' + ON acl.entity_table = 'civicrm_acl_role' AND ov.option_group_id = og.id AND acl.entity_id = ov.value AND ov.is_active = 1 - INNER JOIN $aclER - ON $aclER.acl_role_id = acl.entity_id - AND $aclER.is_active = 1 - INNER JOIN $c2g - ON $aclER.entity_id = $c2g.group_id - AND $aclER.entity_table = 'civicrm_group' - WHERE acl.entity_table = '$aclRole' + INNER JOIN civicrm_acl_entity_role acl_entity_role + ON acl_entity_role.acl_role_id = acl.entity_id + AND acl_entity_role.is_active = 1 + INNER JOIN civicrm_group_contact group_contact + ON acl_entity_role.entity_id = group_contact.group_id + AND acl_entity_role.entity_table = 'civicrm_group' + WHERE acl.entity_table = 'civicrm_acl_role' AND acl.is_active = 1 - AND $c2g.contact_id = $contact_id - AND $c2g.status = 'Added'"; + AND group_contact.contact_id = $contact_id + AND group_contact.status = 'Added'"; $results = []; From 0931f8018a05ff2c60e8f1a05d21ef717ea8aa2c Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 9 Sep 2020 14:29:47 +1200 Subject: [PATCH 207/834] Membership form test cleanup, date cleanup on form This converts the test set up to use the api and also does some mild date clean up on the form. Supports https://github.com/civicrm/civicrm-core/pull/18395 --- CRM/Member/Form/Membership.php | 16 +- .../CRM/Member/Form/MembershipTest.php | 168 +++++++++++------- .../phpunit/CRM/Member/Form/dataset/data.xml | 81 --------- 3 files changed, 110 insertions(+), 155 deletions(-) delete mode 100644 tests/phpunit/CRM/Member/Form/dataset/data.xml diff --git a/CRM/Member/Form/Membership.php b/CRM/Member/Form/Membership.php index 3c98b8570b9f..733cafbc37d2 100644 --- a/CRM/Member/Form/Membership.php +++ b/CRM/Member/Form/Membership.php @@ -1058,7 +1058,7 @@ public function submit() { $isTest = ($this->_mode === 'test') ? 1 : 0; $this->storeContactFields($this->_params); $this->beginPostProcess(); - $joinDate = $startDate = $endDate = NULL; + $endDate = NULL; $membershipTypes = $membership = $calcDate = []; $membershipType = NULL; $paymentInstrumentID = $this->_paymentProcessor['object']->getPaymentInstrumentID(); @@ -1177,15 +1177,9 @@ public function submit() { $params['exclude_is_admin'] = TRUE; } - // process date params to mysql date format. - $dateTypes = [ - 'join_date' => 'joinDate', - 'start_date' => 'startDate', - 'end_date' => 'endDate', - ]; - foreach ($dateTypes as $dateField => $dateVariable) { - $$dateVariable = CRM_Utils_Date::processDate($formValues[$dateField]); - } + $joinDate = $formValues['join_date']; + $startDate = $formValues['start_date']; + $endDate = $formValues['end_date']; $memTypeNumTerms = empty($termsByType) ? CRM_Utils_Array::value('num_terms', $formValues) : NULL; @@ -1200,7 +1194,7 @@ public function submit() { } foreach ($calcDates as $memType => $calcDate) { - foreach (array_keys($dateTypes) as $d) { + foreach (['join_date', 'start_date', 'end_date'] as $d) { //first give priority to form values then calDates. $date = $formValues[$d] ?? NULL; if (!$date) { diff --git a/tests/phpunit/CRM/Member/Form/MembershipTest.php b/tests/phpunit/CRM/Member/Form/MembershipTest.php index 593efc1152a6..0f28c333720c 100644 --- a/tests/phpunit/CRM/Member/Form/MembershipTest.php +++ b/tests/phpunit/CRM/Member/Form/MembershipTest.php @@ -81,24 +81,66 @@ public function setUp() { $this->_individualId = $this->individualCreate(); $this->_paymentProcessorID = $this->processorCreate(); - $this->loadXMLDataSet(__DIR__ . '/dataset/data.xml'); - - $membershipTypeAnnualFixed = $this->callAPISuccess('membership_type', 'create', [ + $this->ids['contact']['organization'] = $this->organizationCreate(); + $this->ids['contact']['organization2'] = $this->organizationCreate(); + $this->ids['relationship_type']['member'] = $this->callAPISuccess('RelationshipType', 'create', [ + 'name_a_b' => 'Member of', + 'label_a_b' => 'Member of', + 'name_b_a' => 'Member is', + 'label_b_a' => 'Member is', + 'contact_type_a' => 'Individual', + 'contact_type_b' => 'Organization', + ])['id']; + $membershipTypeAnnualFixed = $this->callAPISuccess('MembershipType', 'create', [ 'domain_id' => 1, 'name' => 'AnnualFixed', - 'member_of_contact_id' => 23, + 'member_of_contact_id' => $this->ids['contact']['organization'], 'duration_unit' => 'year', 'minimum_fee' => 50, 'duration_interval' => 1, 'period_type' => 'fixed', 'fixed_period_start_day' => '101', 'fixed_period_rollover_day' => '1231', - 'relationship_type_id' => 20, + 'relationship_type_id' => [$this->ids['relationship_type']['member']], + 'relationship_direction' => ['b_a'], 'financial_type_id' => 2, ]); $this->membershipTypeAnnualFixedID = $membershipTypeAnnualFixed['id']; - $instruments = $this->callAPISuccess('contribution', 'getoptions', ['field' => 'payment_instrument_id']); + $this->ids['membership_type']['AnnualRolling'] = $this->callAPISuccess('MembershipType', 'create', [ + 'name' => 'AnnualRolling', + 'member_of_contact_id' => $this->ids['contact']['organization'], + 'duration_unit' => 'year', + 'duration_interval' => 1, + 'period_type' => 'rolling', + 'relationship_type_id' => [$this->ids['relationship_type']['member']], + 'relationship_direction' => ['b_a'], + 'financial_type_id' => 'Member Dues', + ])['id']; + + $this->ids['membership_type']['AnnualRollingOrg2'] = $this->callAPISuccess('MembershipType', 'create', [ + 'name' => 'AnnualRolling1', + 'member_of_contact_id' => $this->ids['contact']['organization2'], + 'duration_unit' => 'year', + 'duration_interval' => 1, + 'period_type' => 'rolling', + 'relationship_type_id' => [$this->ids['relationship_type']['member']], + 'relationship_direction' => ['b_a'], + 'financial_type_id' => 'Member Dues', + ])['id']; + + $this->ids['membership_type']['lifetime'] = $this->callAPISuccess('MembershipType', 'create', [ + 'name' => 'Lifetime', + 'member_of_contact_id' => $this->ids['contact']['organization'], + 'duration_unit' => 'lifetime', + 'duration_interval' => 1, + 'relationship_type_id' => $this->ids['relationship_type']['member'], + 'relationship_direction' => 'b_a', + 'financial_type_id' => 'Member Dues', + 'period_type' => 'rolling', + ])['id']; + + $instruments = $this->callAPISuccess('Contribution', 'getoptions', ['field' => 'payment_instrument_id']); $this->paymentInstruments = $instruments['values']; } @@ -118,10 +160,8 @@ public function tearDown() { 'civicrm_email', ] ); - foreach ([17, 18, 23, 32] as $contactID) { - $this->callAPISuccess('contact', 'delete', ['id' => $contactID, 'skip_undelete' => TRUE]); - } - $this->callAPISuccess('relationship_type', 'delete', ['id' => 20]); + $this->callAPISuccess('Contact', 'delete', ['id' => $this->ids['contact']['organization'], 'skip_undelete' => TRUE]); + $this->callAPISuccess('RelationshipType', 'delete', ['id' => $this->ids['relationship_type']['member']]); } /** @@ -129,6 +169,7 @@ public function tearDown() { * that has an empty contact_select_id value * * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception */ public function testFormRuleEmptyContact() { $params = [ @@ -162,7 +203,7 @@ public function testFormRuleRollingEarlyStart() { 'join_date' => date('Y-m-d'), 'start_date' => $ymdYesterday, 'end_date' => '', - 'membership_type_id' => ['23', '15'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']], ]; $files = []; $obj = new CRM_Member_Form_Membership(); @@ -186,7 +227,7 @@ public function testFormRuleRollingEarlyEnd() { 'join_date' => date('Y-m-d'), 'start_date' => date('Y-m-d'), 'end_date' => $ymdYesterday, - 'membership_type_id' => ['23', '15'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']], ]; $files = []; $obj = new CRM_Member_Form_Membership(); @@ -206,11 +247,11 @@ public function testFormRuleRollingEndNoStart() { 'join_date' => date('Y-m-d'), 'start_date' => '', 'end_date' => $ymdYearFromNow, - 'membership_type_id' => ['23', '15'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']], ]; $files = []; $obj = new CRM_Member_Form_Membership(); - $rc = $obj->formRule($params, $files, $obj); + $rc = $obj::formRule($params, $files, $obj); $this->assertType('array', $rc); $this->assertTrue(array_key_exists('start_date', $rc)); } @@ -228,11 +269,11 @@ public function testFormRuleRollingLifetimeEnd() { 'end_date' => date('Y-m-d', $unixYearFromNow ), - 'membership_type_id' => ['23', '25'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['lifetime']], ]; $files = []; $obj = new CRM_Member_Form_Membership(); - $rc = $obj->formRule($params, $files, $obj); + $rc = $obj::formRule($params, $files, $obj); $this->assertType('array', $rc); $this->assertTrue(array_key_exists('status_id', $rc)); } @@ -242,16 +283,17 @@ public function testFormRuleRollingLifetimeEnd() { * that has permanent override and no status * * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception */ public function testFormRulePermanentOverrideWithNoStatus() { $params = [ 'join_date' => date('Y-m-d'), - 'membership_type_id' => ['23', '25'], + 'membership_type_id' => [$this->ids['contact']['organization'], '$this->membershipTypeAnnualFixedID'], 'is_override' => TRUE, ]; $files = []; $obj = new CRM_Member_Form_Membership(); - $rc = $obj->formRule($params, $files, $obj); + $rc = $obj::formRule($params, $files, $obj); $this->assertType('array', $rc); $this->assertTrue(array_key_exists('status_id', $rc)); } @@ -259,7 +301,7 @@ public function testFormRulePermanentOverrideWithNoStatus() { public function testFormRuleUntilDateOverrideWithValidOverrideEndDate() { $params = [ 'join_date' => date('Y-m-d'), - 'membership_type_id' => ['23', '25'], + 'membership_type_id' => [$this->ids['contact']['organization'], '$this->membershipTypeAnnualFixedID'], 'is_override' => TRUE, 'status_id' => 1, 'status_override_end_date' => date('Y-m-d'), @@ -273,7 +315,7 @@ public function testFormRuleUntilDateOverrideWithValidOverrideEndDate() { public function testFormRuleUntilDateOverrideWithNoOverrideEndDate() { $params = [ 'join_date' => date('Y-m-d'), - 'membership_type_id' => ['23', '25'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->membershipTypeAnnualFixedID], 'is_override' => CRM_Member_StatusOverrideTypes::UNTIL_DATE, 'status_id' => 1, ]; @@ -295,11 +337,11 @@ public function testFormRuleRollingJoin1MonthFromNow() { 'join_date' => date('Y-m-d', $unix1MFmNow), 'start_date' => '', 'end_date' => '', - 'membership_type_id' => ['23', '15'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']], ]; $files = []; $obj = new CRM_Member_Form_Membership(); - $rc = $obj->formRule($params, $files, $obj); + $rc = $obj::formRule($params, $files, $obj); // Should have found no valid membership status. $this->assertType('array', $rc); @@ -314,11 +356,11 @@ public function testFormRuleRollingJoinToday() { 'join_date' => date('Y-m-d'), 'start_date' => '', 'end_date' => '', - 'membership_type_id' => ['23', '15'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']], ]; $files = []; $obj = new CRM_Member_Form_Membership(); - $rc = $obj->formRule($params, $files, $obj); + $rc = $obj::formRule($params, $files, $obj); // Should have found New membership status $this->assertTrue($rc); @@ -335,11 +377,11 @@ public function testFormRuleRollingJoin1MonthAgo() { 'join_date' => date('Y-m-d', $unix1MAgo), 'start_date' => '', 'end_date' => '', - 'membership_type_id' => ['23', '15'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']], ]; $files = []; $obj = new CRM_Member_Form_Membership(); - $rc = $obj->formRule($params, $files, $obj); + $rc = $obj::formRule($params, $files, $obj); // Should have found New membership status. $this->assertTrue($rc); @@ -355,11 +397,11 @@ public function testFormRuleRollingJoin6MonthsAgo() { 'join_date' => date('Y-m-d', $unix6MAgo), 'start_date' => '', 'end_date' => '', - 'membership_type_id' => ['23', '15'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']], ]; $files = []; $obj = new CRM_Member_Form_Membership(); - $rc = $obj->formRule($params, $files, $obj); + $rc = $obj::formRule($params, $files, $obj); // Should have found Current membership status. $this->assertTrue($rc); @@ -376,11 +418,11 @@ public function testFormRuleRollingJoin1YearAgo() { 'join_date' => date('Y-m-d', $unix1YAgo), 'start_date' => '', 'end_date' => '', - 'membership_type_id' => ['23', '15'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']], ]; $files = []; $obj = new CRM_Member_Form_Membership(); - $rc = $obj->formRule($params, $files, $obj); + $rc = $obj::formRule($params, $files, $obj); // Should have found Grace membership status $this->assertTrue($rc); @@ -397,11 +439,11 @@ public function testFormRuleRollingJoin2YearsAgo() { 'join_date' => date('Y-m-d', $unix2YAgo), 'start_date' => '', 'end_date' => '', - 'membership_type_id' => ['23', '15'], + 'membership_type_id' => [$this->ids['contact']['organization'], $this->ids['membership_type']['AnnualRolling']], ]; $files = []; $obj = new CRM_Member_Form_Membership(); - $rc = $obj->formRule($params, $files, $obj); + $rc = $obj::formRule($params, $files, $obj); // Should have found Expired membership status $this->assertTrue($rc); @@ -418,11 +460,11 @@ public function testFormRuleFixedJoin6MonthsAgo() { 'join_date' => date('Y-m-d', $unix6MAgo), 'start_date' => '', 'end_date' => '', - 'membership_type_id' => ['23', '7'], + 'membership_type_id' => [$this->ids['contact']['organization'], '7'], ]; $files = []; $obj = new CRM_Member_Form_Membership(); - $rc = $obj->formRule($params, $files, $obj); + $rc = $obj::formRule($params, $files, $obj); // Should have found Current membership status $this->assertTrue($rc); @@ -449,8 +491,8 @@ public function testSubmit($thousandSeparator) { 'join_date' => date('2/d/Y'), 'start_date' => '', 'end_date' => '', - // This format reflects the 23 being the organisation & the 25 being the type. - 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], + // This format reflects the organisation & then the type. + 'membership_type_id' => [$this->ids['contact']['organization'], $this->membershipTypeAnnualFixedID], 'auto_renew' => '0', 'max_related' => '', 'num_terms' => '1', @@ -552,8 +594,8 @@ public function testContributionUpdateOnMembershipTypeChange() { 'join_date' => date('Y-m-d'), 'start_date' => '', 'end_date' => '', - // This format reflects the 23 being the organisation & the 25 being the type. - 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], + // This format reflects the first being the organisation & the $this->membershipTypeAnnualFixedID being the type. + 'membership_type_id' => [$this->ids['contact']['organization'], $this->membershipTypeAnnualFixedID], 'record_contribution' => 1, 'total_amount' => 50, 'receive_date' => date('Y-m-d', time()) . ' 20:36:00', @@ -579,7 +621,7 @@ public function testContributionUpdateOnMembershipTypeChange() { $secondMembershipType = $this->callAPISuccess('membership_type', 'create', [ 'domain_id' => 1, 'name' => 'Second Test Membership', - 'member_of_contact_id' => 23, + 'member_of_contact_id' => $this->ids['contact']['organization'], 'duration_unit' => 'month', 'minimum_fee' => 25, 'duration_interval' => 1, @@ -601,8 +643,8 @@ public function testContributionUpdateOnMembershipTypeChange() { 'join_date' => date('Y-m-d'), 'start_date' => '', 'end_date' => '', - // This format reflects the 23 being the organisation & the 25 being the type. - 'membership_type_id' => [23, $secondMembershipType['id']], + // This format reflects the first number being the organisation & the 25 being the type. + 'membership_type_id' => [$this->ids['contact']['organization'], $secondMembershipType['id']], 'record_contribution' => 1, 'status_id' => 1, 'total_amount' => 25, @@ -619,8 +661,8 @@ public function testContributionUpdateOnMembershipTypeChange() { $contribution = $this->callAPISuccessGetSingle('Contribution', [ 'contact_id' => $this->_individualId, ]); - $payment = CRM_Contribute_BAO_Contribution::getPaymentInfo($membership['id'], 'membership', FALSE, TRUE); - // Check the contribution status on membership type change whose minimum fee was less than earlier memebership + $payment = CRM_Contribute_BAO_Contribution::getPaymentInfo($membership['id'], 'membership', FALSE); + // Check the contribution status on membership type change whose minimum fee was less than earlier membership $this->assertEquals('Pending refund', $contribution['contribution_status']); // Earlier paid amount $this->assertEquals(50, $payment['paid']); @@ -655,8 +697,8 @@ public function testSubmitPartialPayment($thousandSeparator) { 'join_date' => date('Y-m-d'), 'start_date' => '', 'end_date' => '', - // This format reflects the 23 being the organisation & the 25 being the type. - 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], + // This format reflects the first number being the organisation & the second being the type. + 'membership_type_id' => [$this->ids['contact']['organization'], $this->membershipTypeAnnualFixedID], 'receive_date' => date('Y-m-d', time()) . ' 20:36:00', 'record_contribution' => 1, 'total_amount' => $this->formatMoneyInput(50), @@ -827,8 +869,8 @@ public function testSubmitPayLaterWithBilling() { 'join_date' => date('Y-m-d'), 'start_date' => '', 'end_date' => '', - // This format reflects the 23 being the organisation & the 25 being the type. - 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], + // This format reflects the first number being the organisation & the second being the type. + 'membership_type_id' => [$this->ids['contact']['organization'], $this->membershipTypeAnnualFixedID], 'auto_renew' => '0', 'max_related' => '', 'num_terms' => '2', @@ -1029,10 +1071,10 @@ public function testFormWithFailedContribution() { public function testTwoInheritedMembershipsViaPriceSetInBackend() { // Create an organization and give it a "Member of" relationship to $this->_individualId. $orgID = $this->organizationCreate(); - $relationship = $this->callAPISuccess('relationship', 'create', [ + $relationship = $this->callAPISuccess('Relationship', 'create', [ 'contact_id_a' => $this->_individualId, 'contact_id_b' => $orgID, - 'relationship_type_id' => 20, + 'relationship_type_id' => $this->ids['relationship_type']['member'], 'is_active' => 1, ]); @@ -1043,7 +1085,7 @@ public function testTwoInheritedMembershipsViaPriceSetInBackend() { $orgMembershipResult = $this->callAPISuccess('membership', 'get', [ 'contact_id' => $orgID, ]); - $this->assertEquals(2, $orgMembershipResult['count'], "2 primary memberships should have been created on the organization."); + $this->assertEquals(2, $orgMembershipResult['count'], '2 primary memberships should have been created on the organization.'); $primaryMembershipIds = []; foreach ($orgMembershipResult['values'] as $membership) { $primaryMembershipIds[] = $membership['id']; @@ -1140,7 +1182,7 @@ public function buildAmountMembershipDiscount($pageType, &$form, &$amount) { foreach ($amount as $id => $priceField) { if (is_array($priceField['options'])) { foreach ($priceField['options'] as $optionId => $option) { - if ($option['membership_type_id'] == 15) { + if ($option['membership_type_id'] == $this->ids['membership_type']['AnnualRolling']) { // Long Haired Goat membership discount. $amount[$id]['options'][$optionId]['amount'] = $option['amount'] * 0.75; $amount[$id]['options'][$optionId]['label'] = $option['label'] . ' - one leg free!'; @@ -1179,8 +1221,8 @@ protected function getBaseSubmitParams() { 'start_date' => '', 'end_date' => '', 'campaign_id' => '', - // This format reflects the 23 being the organisation & the 25 being the type. - 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], + // This format reflects the first number being the organisation & the second being the type. + 'membership_type_id' => [$this->ids['contact']['organization'], $this->membershipTypeAnnualFixedID], 'auto_renew' => '1', 'is_recur' => 1, 'max_related' => 0, @@ -1249,7 +1291,7 @@ protected function createTwoMembershipsViaPriceSetInBackEnd($contactId) { 'label' => 'Long Haired Goat', 'amount' => 20, 'financial_type_id' => 'Donation', - 'membership_type_id' => 15, + 'membership_type_id' => $this->ids['membership_type']['AnnualRolling'], 'membership_num_terms' => 1, ]); $pfvIDs = [$priceFieldValue['id'] => 1]; @@ -1259,7 +1301,7 @@ protected function createTwoMembershipsViaPriceSetInBackEnd($contactId) { 'label' => 'Shoe-eating Goat', 'amount' => 10, 'financial_type_id' => 'Donation', - 'membership_type_id' => 35, + 'membership_type_id' => $this->ids['membership_type']['AnnualRollingOrg2'], 'membership_num_terms' => 2, ]); $pfvIDs[$priceFieldValue['id']] = 1; @@ -1272,7 +1314,7 @@ protected function createTwoMembershipsViaPriceSetInBackEnd($contactId) { 'end_date' => '', // This format reflects the 23 being the organisation & the 25 being the type. "price_$priceFieldID" => $pfvIDs, - "price_set_id" => $priceSetID, + 'price_set_id' => $priceSetID, 'membership_type_id' => [1 => 0], 'auto_renew' => '0', 'max_related' => '', @@ -1367,7 +1409,7 @@ public function testLineItemAmountOnSalesTax() { $priceSet = $this->callAPISuccess('PriceSet', 'Get', ['extends' => 'CiviMember']); $form->set('priceSetId', $priceSet['id']); // we are simulating the creation of a Price Set in Administer -> CiviContribute -> Manage Price Sets so set is_quick_config = 0 - $this->callAPISuccess('PriceSet', 'Create', ["id" => $priceSet['id'], 'is_quick_config' => 0]); + $this->callAPISuccess('PriceSet', 'Create', ['id' => $priceSet['id'], 'is_quick_config' => 0]); // clean the price options static variable to repopulate the options, in order to fetch tax information \Civi::$statics['CRM_Price_BAO_PriceField']['priceOptions'] = NULL; CRM_Price_BAO_PriceSet::buildPriceSet($form); @@ -1378,8 +1420,8 @@ public function testLineItemAmountOnSalesTax() { 'join_date' => date('Y-m-d'), 'start_date' => '', 'end_date' => '', - // This format reflects the 23 being the organisation & the 25 being the type. - 'membership_type_id' => [23, $this->membershipTypeAnnualFixedID], + // This format reflects the first number being the organisation & the second being the type. + 'membership_type_id' => [$this->ids['contact']['organization'], $this->membershipTypeAnnualFixedID], 'record_contribution' => 1, 'total_amount' => 55, 'receive_date' => date('Y-m-d') . ' 20:36:00', @@ -1452,11 +1494,11 @@ public function testCreatePendingWithMultipleTerms() { $membershipTypeAnnualRolling = $this->callAPISuccess('membership_type', 'create', [ 'domain_id' => 1, 'name' => 'AnnualRollingNew', - 'member_of_contact_id' => 23, - 'duration_unit' => "year", + 'member_of_contact_id' => $this->ids['contact']['organization'], + 'duration_unit' => 'year', 'minimum_fee' => 50, 'duration_interval' => 1, - 'period_type' => "rolling", + 'period_type' => 'rolling', 'relationship_type_id' => 20, 'relationship_direction' => 'b_a', 'financial_type_id' => 2, @@ -1467,7 +1509,7 @@ public function testCreatePendingWithMultipleTerms() { 'start_date' => '', 'end_date' => '', 'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'), - 'membership_type_id' => [23, $membershipTypeAnnualRolling['id']], + 'membership_type_id' => [$this->ids['contact']['organization'], $membershipTypeAnnualRolling['id']], 'max_related' => '', 'num_terms' => '3', 'record_contribution' => 1, diff --git a/tests/phpunit/CRM/Member/Form/dataset/data.xml b/tests/phpunit/CRM/Member/Form/dataset/data.xml deleted file mode 100644 index 852719316b80..000000000000 --- a/tests/phpunit/CRM/Member/Form/dataset/data.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - From f961a8cde5f2c4d696315b8ebe2126b86969f5f6 Mon Sep 17 00:00:00 2001 From: Seamus Lee Date: Wed, 9 Sep 2020 13:08:52 +1000 Subject: [PATCH 208/834] [REF] Refactor price field form to allow for unit testing of the form Set the assert equals to be an actual float number --- CRM/Price/Form/Field.php | 44 +++++++++++++--------- tests/phpunit/CRM/Price/Form/FieldTest.php | 30 +++++++++++++++ 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/CRM/Price/Form/Field.php b/CRM/Price/Form/Field.php index 023066db68b5..de3b607e3b98 100644 --- a/CRM/Price/Form/Field.php +++ b/CRM/Price/Form/Field.php @@ -77,6 +77,13 @@ public function getEntityId() { */ protected $_useForMember; + /** + * Set the price Set Id (only used in tests) + */ + public function setPriceSetId($priceSetId) { + $this->_sid = $priceSetId; + } + /** * Set variables up before form is built. */ @@ -641,6 +648,25 @@ public static function formRule($fields, $files, $form) { public function postProcess() { // store the submitted values in an array $params = $this->controller->exportValues('Field'); + $params['id'] = $this->getEntityId(); + $priceField = $this->submit($params); + if (!is_a($priceField, 'CRM_Core_Error')) { + // Required by extensions implementing the postProcess hook (to get the ID of new entities) + $this->setEntityId($priceField->id); + CRM_Core_Session::setStatus(ts('Price Field \'%1\' has been saved.', [1 => $priceField->label]), ts('Saved'), 'success'); + } + $buttonName = $this->controller->getButtonName(); + $session = CRM_Core_Session::singleton(); + if ($buttonName == $this->getButtonName('next', 'new')) { + CRM_Core_Session::setStatus(ts(' You can add another price set field.'), '', 'info'); + $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/price/field', 'reset=1&action=add&sid=' . $this->_sid)); + } + else { + $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/price/field', 'reset=1&action=browse&sid=' . $this->_sid)); + } + } + + public function submit($params) { $params['price'] = CRM_Utils_Rule::cleanMoney($params['price']); foreach ($params['option_amount'] as $key => $amount) { $params['option_amount'][$key] = CRM_Utils_Rule::cleanMoney($amount); @@ -686,26 +712,10 @@ public function postProcess() { $params['option_visibility_id'] = [1 => CRM_Utils_Array::value('visibility_id', $params)]; } - $params['id'] = $this->getEntityId(); - $params['membership_num_terms'] = (!empty($params['membership_type_id'])) ? CRM_Utils_Array::value('membership_num_terms', $params, 1) : NULL; $priceField = CRM_Price_BAO_PriceField::create($params); - - if (!is_a($priceField, 'CRM_Core_Error')) { - // Required by extensions implementing the postProcess hook (to get the ID of new entities) - $this->setEntityId($priceField->id); - CRM_Core_Session::setStatus(ts('Price Field \'%1\' has been saved.', [1 => $priceField->label]), ts('Saved'), 'success'); - } - $buttonName = $this->controller->getButtonName(); - $session = CRM_Core_Session::singleton(); - if ($buttonName == $this->getButtonName('next', 'new')) { - CRM_Core_Session::setStatus(ts(' You can add another price set field.'), '', 'info'); - $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/price/field', 'reset=1&action=add&sid=' . $this->_sid)); - } - else { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/price/field', 'reset=1&action=browse&sid=' . $this->_sid)); - } + return $priceField; } } diff --git a/tests/phpunit/CRM/Price/Form/FieldTest.php b/tests/phpunit/CRM/Price/Form/FieldTest.php index f7ba89f11d17..e4c71d881900 100644 --- a/tests/phpunit/CRM/Price/Form/FieldTest.php +++ b/tests/phpunit/CRM/Price/Form/FieldTest.php @@ -57,6 +57,35 @@ public function testAdminFieldDoesNotAllowPublicOptions() { $this->assertTrue(array_key_exists('visibility_id', $validationResult)); } + /** + * Test submitting a large float value is stored correctly in the db. + * + * @param string $thousandSeparator + * punctuation used to refer to thousands. + * + * @dataProvider getThousandSeparators + */ + public function testLargeFloatOptionValue($thousandSeparator) { + $this->setCurrencySeparators($thousandSeparator); + $thousands = Civi::settings()->get('monetaryThousandSeparator'); + $decimal = Civi::settings()->get('monetaryDecimalPoint'); + $paramsSet['title'] = 'Price Set' . substr(sha1(rand()), 0, 7); + $paramsSet['name'] = CRM_Utils_String::titleToVar($paramsSet['title']); + $paramsSet['is_active'] = TRUE; + $paramsSet['financial_type_id'] = 'Event Fee'; + $paramsSet['extends'] = 1; + $priceSet = $this->callAPISuccess('price_set', 'create', $paramsSet); + $form = new CRM_Price_Form_Field(); + $form->_action = CRM_Core_Action::ADD; + $form->setPriceSetId($priceSet['id']); + $this->publicFieldParams['option_label'][1] = 'Large Float'; + $this->publicFieldParams['option_amount'][1] = '123' . $thousands . '456' . $thousands . '789' . $decimal . '987654321'; + $this->publicFieldParams['option_visibility_id'][1] = $this->visibilityOptionsKeys['public']; + $priceField = $form->submit($this->publicFieldParams); + $priceOptions = $this->callAPISuccess('PriceFieldValue', 'get', ['price_field_id' => $priceField->id]); + $this->assertEquals(123456789.987654321, $priceOptions['values'][$priceOptions['id']]['amount']); + } + private function initializeFieldParameters($params) { $defaultParams = [ 'label' => 'Price Field', @@ -68,6 +97,7 @@ private function initializeFieldParameters($params) { 'is_enter_qty' => 1, 'financial_type_id' => $this->getFinancialTypeId('Event Fee'), 'visibility_id' => $this->visibilityOptionsKeys['public'], + 'price' => 10, ]; for ($index = 1; $index <= CRM_Price_Form_Field::NUM_OPTION; $index++) { From 4cbe461a4d4c66220fd91c5609945ac042c9fcfa Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 9 Sep 2020 18:15:18 +1200 Subject: [PATCH 209/834] dev/financial#86 Make 'Record Payment' & 'Record Refund' visible regardless of whether the balance 'requires' one This was agreed about a year ago - might as well do it --- CRM/Contribute/BAO/Contribution.php | 34 +++++++++++------------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 3de7326172e1..018e99b27a31 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -5201,35 +5201,27 @@ protected static function getContributionPaymentLinks($id, $balance, $contributi 'title' => ts('Record Payment'), ]; - if ((int) $balance > 0) { - // @todo - this should be possible even if not > 0 - test & remove this if. - // it is possible to 'overpay' in the real world & we honor that. - if (CRM_Core_Config::isEnabledBackOfficeCreditCardPayments()) { - $actionLinks[] = [ - 'url' => CRM_Utils_System::url('civicrm/payment', [ - 'action' => 'add', - 'reset' => 1, - 'is_refund' => 0, - 'id' => $id, - 'mode' => 'live', - ]), - 'title' => ts('Submit Credit Card payment'), - ]; - } - } - elseif ((int) $balance < 0) { - // @todo - in the future remove this IF - OK to refund money even when not due since - // ... life. + if (CRM_Core_Config::isEnabledBackOfficeCreditCardPayments()) { $actionLinks[] = [ 'url' => CRM_Utils_System::url('civicrm/payment', [ 'action' => 'add', 'reset' => 1, + 'is_refund' => 0, 'id' => $id, - 'is_refund' => 1, + 'mode' => 'live', ]), - 'title' => ts('Record Refund'), + 'title' => ts('Submit Credit Card payment'), ]; } + $actionLinks[] = [ + 'url' => CRM_Utils_System::url('civicrm/payment', [ + 'action' => 'add', + 'reset' => 1, + 'id' => $id, + 'is_refund' => 1, + ]), + 'title' => ts('Record Refund'), + ]; return $actionLinks; } From 0dcc88fc1b54e4e36ee685782c0fa93feccd8d35 Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 9 Sep 2020 19:24:52 +1200 Subject: [PATCH 210/834] Extract failContribution code Basic extraction --- CRM/Contribute/BAO/Contribution.php | 118 +++++++++++++++++----------- 1 file changed, 73 insertions(+), 45 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 62f101260e6b..fa268614ffb2 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -1350,6 +1350,78 @@ protected static function isEmailReceipt(array $input, $contributionPageID, $rec return TRUE; } + /** + * Process failed contribution. + * + * @param $processContributionObject + * @param $memberships + * @param $contributionId + * @param array $membershipStatuses + * @param array $updateResult + * @param $participant + * @param $pledgePayment + * @param $pledgeID + * @param array $pledgePaymentIDs + * @param $contributionStatusId + * + * @return array + * @throws \CRM_Core_Exception + */ + protected static function processFail($processContributionObject, $memberships, $contributionId, array $membershipStatuses, array $updateResult, $participant, $pledgePayment, $pledgeID, array $pledgePaymentIDs, $contributionStatusId): array { + $processContribution = FALSE; + if (is_array($memberships)) { + foreach ($memberships as $membership) { + $update = TRUE; + //Update Membership status if there is no other completed contribution associated with the membership. + $relatedContributions = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id, TRUE); + foreach ($relatedContributions as $contriId) { + if ($contriId == $contributionId) { + continue; + } + $statusId = CRM_Core_DAO::getFieldValue('CRM_Contribute_BAO_Contribution', $contriId, 'contribution_status_id'); + if (CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $statusId) === 'Completed') { + $update = FALSE; + } + } + if ($membership && $update) { + $membership->status_id = array_search('Expired', $membershipStatuses); + $membership->is_override = TRUE; + $membership->status_override_end_date = 'null'; + $membership->save(); + + $updateResult['updatedComponents']['CiviMember'] = $membership->status_id; + if ($processContributionObject) { + $processContribution = TRUE; + } + } + } + } + if ($participant) { + $oldStatus = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant', + $participant->id, + 'status_id' + ); + $participantStatuses = CRM_Event_PseudoConstant::participantStatus(); + $updatedStatusId = array_search('Cancelled', $participantStatuses); + CRM_Event_BAO_Participant::updateParticipantStatus($participant->id, $oldStatus, $updatedStatusId, TRUE); + + $updateResult['updatedComponents']['CiviEvent'] = $updatedStatusId; + if ($processContributionObject) { + $processContribution = TRUE; + } + } + + if ($pledgePayment) { + CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($pledgeID, $pledgePaymentIDs, $contributionStatusId); + + $updateResult['updatedComponents']['CiviPledge'] = $contributionStatusId; + if ($processContributionObject) { + $processContribution = TRUE; + } + } + return [$updateResult, $processContribution]; + } + /** * @inheritDoc */ @@ -2161,51 +2233,7 @@ public static function transitionComponents($params, $processContributionObject list($updateResult, $processContribution) = self::cancel($processContributionObject, $memberships, $contributionId, $membershipStatuses, $updateResult, $participant, $oldStatus, $pledgePayment, $pledgeID, $pledgePaymentIDs, $contributionStatusId); } elseif ($contributionStatusId == array_search('Failed', $contributionStatuses)) { - if (is_array($memberships)) { - foreach ($memberships as $membership) { - $update = TRUE; - //Update Membership status if there is no other completed contribution associated with the membership. - $relatedContributions = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id, TRUE); - foreach ($relatedContributions as $contriId) { - if ($contriId == $contributionId) { - continue; - } - $statusId = CRM_Core_DAO::getFieldValue('CRM_Contribute_BAO_Contribution', $contriId, 'contribution_status_id'); - if (CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $statusId) === 'Completed') { - $update = FALSE; - } - } - if ($membership && $update) { - $membership->status_id = array_search('Expired', $membershipStatuses); - $membership->is_override = TRUE; - $membership->status_override_end_date = 'null'; - $membership->save(); - - $updateResult['updatedComponents']['CiviMember'] = $membership->status_id; - if ($processContributionObject) { - $processContribution = TRUE; - } - } - } - } - if ($participant) { - $updatedStatusId = array_search('Cancelled', $participantStatuses); - CRM_Event_BAO_Participant::updateParticipantStatus($participant->id, $oldStatus, $updatedStatusId, TRUE); - - $updateResult['updatedComponents']['CiviEvent'] = $updatedStatusId; - if ($processContributionObject) { - $processContribution = TRUE; - } - } - - if ($pledgePayment) { - CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($pledgeID, $pledgePaymentIDs, $contributionStatusId); - - $updateResult['updatedComponents']['CiviPledge'] = $contributionStatusId; - if ($processContributionObject) { - $processContribution = TRUE; - } - } + list($updateResult, $processContribution) = self::processFail($processContributionObject, $memberships, $contributionId, $membershipStatuses, $updateResult, $participant, $pledgePayment, $pledgeID, $pledgePaymentIDs, $contributionStatusId); } elseif ($contributionStatusId == array_search('Completed', $contributionStatuses)) { From 8d30e9c75b9342171784d06b703e7b7e9365701f Mon Sep 17 00:00:00 2001 From: Jon Goldberg Date: Wed, 9 Sep 2020 15:48:32 -0400 Subject: [PATCH 211/834] add custom fields to contribution details --- CRM/Report/Form/Member/ContributionDetail.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CRM/Report/Form/Member/ContributionDetail.php b/CRM/Report/Form/Member/ContributionDetail.php index 69dd87b8c872..882e80d903fa 100644 --- a/CRM/Report/Form/Member/ContributionDetail.php +++ b/CRM/Report/Form/Member/ContributionDetail.php @@ -22,6 +22,9 @@ class CRM_Report_Form_Member_ContributionDetail extends CRM_Report_Form { 'Contribution', 'Membership', 'Contact', + 'Individual', + 'Household', + 'Organization', ]; /** From 0524826d4001746250211eb772f6412c5266311a Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 7 Sep 2020 14:03:50 +1200 Subject: [PATCH 212/834] Make period_type mandatory for MembershipType We have a test ensuring period_type is mandatory at the api level. However, it isn't for api v4 - the error is a different issue (handling of pseudoconstants when mapping v3 to v4). This fixes that separate issue & declares required to re-fix for v4 --- CRM/Core/DAO/CustomField.php | 2 +- CRM/Member/DAO/MembershipType.php | 3 +- .../Incremental/sql/5.31.alpha1.mysql.tpl | 5 +++ Civi/Test/Api3TestTrait.php | 3 ++ tests/phpunit/CiviTest/CiviUnitTestCase.php | 24 ++++++------ tests/phpunit/api/v3/MembershipTypeTest.php | 2 +- .../api/v3/PaymentProcessorTypeTest.php | 38 +++++++++++++------ xml/schema/Member/MembershipType.xml | 1 + 8 files changed, 50 insertions(+), 28 deletions(-) diff --git a/CRM/Core/DAO/CustomField.php b/CRM/Core/DAO/CustomField.php index 6cae23bea076..698d34a4956d 100644 --- a/CRM/Core/DAO/CustomField.php +++ b/CRM/Core/DAO/CustomField.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/CustomField.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1acb9b3538bd3005b99e6af6d9ec062f) + * (GenCodeChecksum:4ded3c0d1a8e34502a5957ee74c4480a) */ /** diff --git a/CRM/Member/DAO/MembershipType.php b/CRM/Member/DAO/MembershipType.php index 4b1d6ce509eb..4f5cad0e296a 100644 --- a/CRM/Member/DAO/MembershipType.php +++ b/CRM/Member/DAO/MembershipType.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Member/MembershipType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:713057d2c1a6dcb6cbd6449b8934d28c) + * (GenCodeChecksum:7c5220444512f1aa499454e977f27814) */ /** @@ -376,6 +376,7 @@ public static function &fields() { 'type' => CRM_Utils_Type::T_STRING, 'title' => ts('Membership Type Plan'), 'description' => ts('Rolling membership period starts on signup date. Fixed membership periods start on fixed_period_start_day.'), + 'required' => TRUE, 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, 'where' => 'civicrm_membership_type.period_type', diff --git a/CRM/Upgrade/Incremental/sql/5.31.alpha1.mysql.tpl b/CRM/Upgrade/Incremental/sql/5.31.alpha1.mysql.tpl index 663ae49a2678..e87793f05151 100644 --- a/CRM/Upgrade/Incremental/sql/5.31.alpha1.mysql.tpl +++ b/CRM/Upgrade/Incremental/sql/5.31.alpha1.mysql.tpl @@ -3,3 +3,8 @@ {* Remove Country & State special select fields *} UPDATE civicrm_custom_field SET html_type = 'Select' WHERE html_type IN ('Select Country', 'Select State/Province'); + +{* make period_type required - it already is so the update is precautionary *} +UPDATE civicrm_membership_type SET period_type = 'rolling' WHERE period_type IS NULL; +ALTER TABLE civicrm_membership_type MODIFY `period_type` varchar(8) NOT NULL COMMENT 'Rolling membership period starts on signup date. Fixed membership periods start on fixed_period_start_day.' + diff --git a/Civi/Test/Api3TestTrait.php b/Civi/Test/Api3TestTrait.php index ed3557babe96..2ce8d75d0e58 100644 --- a/Civi/Test/Api3TestTrait.php +++ b/Civi/Test/Api3TestTrait.php @@ -405,6 +405,9 @@ public function runApi4Legacy($v3Entity, $v3Action, $v3Params = []) { $v3Params['option_group.name'] = $v3Params['option_group_id']; unset($v3Params['option_group_id']); } + if (isset($field['pseudoconstant'], $v3Params[$name]) && $field['type'] === \CRM_Utils_Type::T_INT && !is_numeric($v3Params[$name])) { + $v3Params[$name] = \CRM_Core_PseudoConstant::getKey(\CRM_Core_DAO_AllCoreTables::getFullName($v3Entity), $name, $v3Params[$name]); + } } switch ($v3Action) { diff --git a/tests/phpunit/CiviTest/CiviUnitTestCase.php b/tests/phpunit/CiviTest/CiviUnitTestCase.php index cf2f3fbffb14..0930fb4f1e50 100644 --- a/tests/phpunit/CiviTest/CiviUnitTestCase.php +++ b/tests/phpunit/CiviTest/CiviUnitTestCase.php @@ -727,19 +727,17 @@ public function relationshipTypeDelete($relationshipTypeID) { * @return mixed * @throws \CRM_Core_Exception */ - public function paymentProcessorTypeCreate($params = NULL) { - if (is_null($params)) { - $params = [ - 'name' => 'API_Test_PP', - 'title' => 'API Test Payment Processor', - 'class_name' => 'CRM_Core_Payment_APITest', - 'billing_mode' => 'form', - 'is_recur' => 0, - 'is_reserved' => 1, - 'is_active' => 1, - ]; - } - $result = $this->callAPISuccess('payment_processor_type', 'create', $params); + public function paymentProcessorTypeCreate($params = []) { + $params = array_merge([ + 'name' => 'API_Test_PP', + 'title' => 'API Test Payment Processor', + 'class_name' => 'CRM_Core_Payment_APITest', + 'billing_mode' => 'form', + 'is_recur' => 0, + 'is_reserved' => 1, + 'is_active' => 1, + ], $params); + $result = $this->callAPISuccess('PaymentProcessorType', 'create', $params); CRM_Core_PseudoConstant::flush('paymentProcessorType'); diff --git a/tests/phpunit/api/v3/MembershipTypeTest.php b/tests/phpunit/api/v3/MembershipTypeTest.php index d20a639b6f16..cb25518fc8c4 100644 --- a/tests/phpunit/api/v3/MembershipTypeTest.php +++ b/tests/phpunit/api/v3/MembershipTypeTest.php @@ -171,7 +171,7 @@ public function testCreateWithoutDomainId($version) { * * @param bool $version */ - public function testMemberTypePeriodiTypeRequired($version) { + public function testMemberTypePeriodTypeRequired($version) { $this->_apiversion = $version; $this->callAPIFailure('MembershipType', 'create', [ 'domain_id' => 'Default Domain Name', diff --git a/tests/phpunit/api/v3/PaymentProcessorTypeTest.php b/tests/phpunit/api/v3/PaymentProcessorTypeTest.php index 13b0362a5114..cd5760a5ac73 100644 --- a/tests/phpunit/api/v3/PaymentProcessorTypeTest.php +++ b/tests/phpunit/api/v3/PaymentProcessorTypeTest.php @@ -48,7 +48,10 @@ public function testPaymentProcessorTypeCreateWithoutName($version) { /** * Create payment processor type. + * * @dataProvider versionThreeAndFour + * + * @param int $version */ public function testPaymentProcessorTypeCreate($version) { $this->_apiversion = $version; @@ -60,7 +63,7 @@ public function testPaymentProcessorTypeCreate($version) { 'billing_mode' => 'form', 'is_recur' => 0, ]; - $result = $this->callAPIAndDocument('payment_processor_type', 'create', $params, __FUNCTION__, __FILE__); + $result = $this->callAPIAndDocument('PaymentProcessorType', 'create', $params, __FUNCTION__, __FILE__); $this->assertNotNull($result['values'][0]['id']); // mutate $params to match expected return value @@ -84,17 +87,22 @@ public function testPaymentProcessorTypeCreateExample() { /** * Check with empty array. + * * @dataProvider versionThreeAndFour + * + * @param int $version */ public function testPaymentProcessorTypeDeleteEmpty($version) { $this->_apiversion = $version; - $params = []; - $result = $this->callAPIFailure('payment_processor_type', 'delete', $params); + $this->callAPIFailure('PaymentProcessorType', 'delete', []); } /** * Check if required fields are not passed. + * * @dataProvider versionThreeAndFour + * + * @param int $version */ public function testPaymentProcessorTypeDeleteWithoutRequired($version) { $this->_apiversion = $version; @@ -110,37 +118,43 @@ public function testPaymentProcessorTypeDeleteWithoutRequired($version) { /** * Check with incorrect required fields. + * * @dataProvider versionThreeAndFour + * + * @param int $version */ public function testPaymentProcessorTypeDeleteWithIncorrectData($version) { $this->_apiversion = $version; - $result = $this->callAPIFailure('payment_processor_type', 'delete', ['id' => 'abcd']); + $this->callAPIFailure('payment_processor_type', 'delete', ['id' => 'abcd']); } /** * Check payment processor type delete. + * * @dataProvider versionThreeAndFour + * + * @param $version + * + * @throws \CRM_Core_Exception */ public function testPaymentProcessorTypeDelete($version) { $this->_apiversion = $version; - $payProcType = $this->paymentProcessorTypeCreate(); - $params = [ - 'id' => $payProcType, - ]; - - $result = $this->callAPIAndDocument('payment_processor_type', 'delete', $params, __FUNCTION__, __FILE__); + $this->callAPIAndDocument('PaymentProcessorType', 'delete', ['id' => $this->paymentProcessorTypeCreate()], __FUNCTION__, __FILE__); } ///////////////// civicrm_payment_processor_type_update /** * Check with empty array. + * * @dataProvider versionThreeAndFour + * + * @param int $version */ public function testPaymentProcessorTypeUpdateEmpty($version) { $this->_apiversion = $version; $params = []; - $result = $this->callAPIFailure('payment_processor_type', 'create', $params); + $result = $this->callAPIFailure('PaymentProcessorType', 'create', $params); $this->assertContains('name, title, class_name, billing_mode', $result['error_message']); } @@ -151,7 +165,7 @@ public function testPaymentProcessorTypeUpdateEmpty($version) { public function testPaymentProcessorTypeUpdate($version) { $this->_apiversion = $version; // create sample payment processor type. - $this->_ppTypeID = $this->paymentProcessorTypeCreate(NULL); + $this->_ppTypeID = $this->paymentProcessorTypeCreate(); $params = [ 'id' => $this->_ppTypeID, diff --git a/xml/schema/Member/MembershipType.xml b/xml/schema/Member/MembershipType.xml index cd528adaf26f..fbae25e8d494 100644 --- a/xml/schema/Member/MembershipType.xml +++ b/xml/schema/Member/MembershipType.xml @@ -145,6 +145,7 @@ Membership Type Plan varchar 8 + true Rolling membership period starts on signup date. Fixed membership periods start on fixed_period_start_day. Select From 508511d1a7096084f96d5ff365869c2570e1827a Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 10 Sep 2020 08:53:26 +1200 Subject: [PATCH 213/834] dev/core#1921 Remove more instances of civi 4.2 date handling Per https://lab.civicrm.org/dev/core/-/issues/1921 --- CRM/Member/BAO/Membership.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/CRM/Member/BAO/Membership.php b/CRM/Member/BAO/Membership.php index 3ccda33cf0a3..0022d417d367 100644 --- a/CRM/Member/BAO/Membership.php +++ b/CRM/Member/BAO/Membership.php @@ -1161,9 +1161,6 @@ public static function fixMembershipStatusBeforeRenew(&$currentMembership, $chan $memberDAO->find(TRUE); $memberDAO->status_id = $status['id']; - $memberDAO->join_date = CRM_Utils_Date::isoToMysql($memberDAO->join_date); - $memberDAO->start_date = CRM_Utils_Date::isoToMysql($memberDAO->start_date); - $memberDAO->end_date = CRM_Utils_Date::isoToMysql($memberDAO->end_date); $memberDAO->save(); CRM_Core_DAO::storeValues($memberDAO, $currentMembership); From 0a81c1832a4bb34921cf7d7dafc586ad40b2c0a0 Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 10 Sep 2020 09:48:59 +1200 Subject: [PATCH 214/834] Remove CRM_Contact_BAO_Contact::getPrimaryOpenId It seems it's only ever called from aa test now --- CRM/Contact/BAO/Contact.php | 27 ------------------- tests/phpunit/CRM/Contact/BAO/ContactTest.php | 24 ----------------- 2 files changed, 51 deletions(-) diff --git a/CRM/Contact/BAO/Contact.php b/CRM/Contact/BAO/Contact.php index a0e84b6b6de0..138b9569bb8d 100644 --- a/CRM/Contact/BAO/Contact.php +++ b/CRM/Contact/BAO/Contact.php @@ -2568,33 +2568,6 @@ public static function getPrimaryEmail($contactID, $polite = FALSE) { return $email; } - /** - * Function to get primary OpenID of the contact. - * - * @param int $contactID - * Contact id. - * - * @return string - * >openid OpenID if present else null - */ - public static function getPrimaryOpenId($contactID) { - // fetch the primary OpenID - $query = " -SELECT civicrm_openid.openid as openid -FROM civicrm_contact -LEFT JOIN civicrm_openid ON ( civicrm_contact.id = civicrm_openid.contact_id ) -WHERE civicrm_contact.id = %1 -AND civicrm_openid.is_primary = 1"; - $p = [1 => [$contactID, 'Integer']]; - $dao = CRM_Core_DAO::executeQuery($query, $p); - - $openId = NULL; - if ($dao->fetch()) { - $openId = $dao->openid; - } - return $openId; - } - /** * Fetch the object and store the values in the values array. * diff --git a/tests/phpunit/CRM/Contact/BAO/ContactTest.php b/tests/phpunit/CRM/Contact/BAO/ContactTest.php index a4948fcf47b9..8c6e925b064a 100644 --- a/tests/phpunit/CRM/Contact/BAO/ContactTest.php +++ b/tests/phpunit/CRM/Contact/BAO/ContactTest.php @@ -1186,30 +1186,6 @@ public function testGetPrimaryEmail() { $this->quickCleanup(['civicrm_contact']); } - /** - * Test case for getPrimaryOpenId( ). - */ - public function testGetPrimaryOpenId() { - //get the contact params - $params = $this->contactParams(); - $params['openid'][2] = $params['openid'][1]; - $params['openid'][2]['location_type_id'] = 2; - $params['openid'][2]['openid'] = 'http://primaryopenid.org/'; - unset($params['openid'][1]['is_primary']); - - //create contact - $contact = CRM_Contact_BAO_Contact::create($params); - $contactId = $contact->id; - //get the primary openid - $openID = CRM_Contact_BAO_Contact::getPrimaryOpenId($contactId); - - //Now check the primary openid - $this->assertEquals($openID, strtolower($params['openid'][2]['openid']), 'Check Primary OpenID'); - - //cleanup DB by deleting the contact - $this->contactDelete($contactId); - } - /** * Test case for matchContactOnEmail( ). */ From 2cb6497039fde605f5d64b2fe4fa0548cd5d1e07 Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 10 Sep 2020 08:24:40 +1200 Subject: [PATCH 215/834] Minor code simplification on date handling in getMembershipStatusByDate This deprecates 'today' as a synonym for 'now' as the latter is more standard and slightly simplifies the date handling --- CRM/Contribute/BAO/Contribution.php | 4 ++-- CRM/Member/BAO/Membership.php | 8 ++++---- CRM/Member/BAO/MembershipStatus.php | 12 ++++++------ CRM/Member/Form/Membership.php | 2 +- CRM/Member/Import/Parser/Membership.php | 4 ++-- api/v3/MembershipStatus.php | 2 +- .../phpunit/CRM/Member/BAO/MembershipStatusTest.php | 2 +- tests/phpunit/CRM/Member/Form/MembershipTest.php | 1 + 8 files changed, 18 insertions(+), 17 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index fa268614ffb2..fbee1bb38662 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -2328,7 +2328,7 @@ public static function transitionComponents($params, $processContributionObject $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($dates['start_date'], $dates['end_date'], $dates['join_date'], - 'today', + 'now', TRUE, $membership->membership_type_id, (array) $membership @@ -5180,7 +5180,7 @@ public static function updateMembershipBasedOnCompletionOfContribution($contribu $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($dates['start_date'], $dates['end_date'], $dates['join_date'], - 'today', + 'now', TRUE, $membershipParams['membership_type_id'], $membershipParams diff --git a/CRM/Member/BAO/Membership.php b/CRM/Member/BAO/Membership.php index 3ccda33cf0a3..925b70744a41 100644 --- a/CRM/Member/BAO/Membership.php +++ b/CRM/Member/BAO/Membership.php @@ -265,7 +265,7 @@ public static function create(&$params, &$ids = []) { } $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($params['start_date'], $params['end_date'], $params['join_date'], - 'today', $excludeIsAdmin, $params['membership_type_id'] ?? NULL, $params + 'now', $excludeIsAdmin, $params['membership_type_id'] ?? NULL, $params ); if (empty($calcStatus)) { throw new CRM_Core_Exception(ts("The membership cannot be saved because the status cannot be calculated for start_date: {$params['start_date']} end_date {$params['end_date']} join_date {$params['join_date']} as at " . date('Y-m-d H:i:s'))); @@ -1133,7 +1133,7 @@ public static function updateRecurMembership(CRM_Member_DAO_Membership $membersh * @throws \CRM_Core_Exception */ public static function fixMembershipStatusBeforeRenew(&$currentMembership, $changeToday) { - $today = NULL; + $today = 'now'; if ($changeToday) { $today = CRM_Utils_Date::processDate($changeToday, NULL, FALSE, 'Y-m-d'); } @@ -1915,7 +1915,7 @@ public static function processMembership($contactID, $membershipTypeID, $is_test CRM_Utils_Date::customFormat($dates['join_date'], $statusFormat ), - 'today', + 'now', TRUE, $membershipTypeID, $memParams @@ -2675,7 +2675,7 @@ public static function mergeMemberships($mainContactID, $otherContactID, &$sqlQu $updates["start_date"] ?? $newMembership->start_date, $updates["end_date"] ?? $newMembership->end_date, $updates["join_date"] ?? $newMembership->join_date, - 'today', + 'now', FALSE, $newMembershipId, $newMembership diff --git a/CRM/Member/BAO/MembershipStatus.php b/CRM/Member/BAO/MembershipStatus.php index fa4a7665f8d5..6dd327e5b076 100644 --- a/CRM/Member/BAO/MembershipStatus.php +++ b/CRM/Member/BAO/MembershipStatus.php @@ -225,17 +225,17 @@ public static function del($membershipStatusId) { */ public static function getMembershipStatusByDate( $startDate, $endDate, $joinDate, - $statusDate = 'today', $excludeIsAdmin = FALSE, $membershipTypeID = NULL, $membership = [] + $statusDate = 'now', $excludeIsAdmin = FALSE, $membershipTypeID = NULL, $membership = [] ) { $membershipDetails = []; - if (!$statusDate || $statusDate == 'today') { - $statusDate = date('Ymd'); - } - else { - $statusDate = CRM_Utils_Date::customFormat($statusDate, '%Y%m%d'); + if (!$statusDate || $statusDate === 'today') { + $statusDate = 'now'; + CRM_Core_Error::deprecatedFunctionWarning('pass now rather than today in'); } + $statusDate = date('Ymd', strtotime($statusDate)); + //fix for CRM-3570, if we have statuses with is_admin=1, //exclude these statuses from calculatation during import. $where = "is_active = 1"; diff --git a/CRM/Member/Form/Membership.php b/CRM/Member/Form/Membership.php index 733cafbc37d2..3537d0c83348 100644 --- a/CRM/Member/Form/Membership.php +++ b/CRM/Member/Form/Membership.php @@ -822,7 +822,7 @@ public static function formRule($params, $files, $self) { $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($startDate, $endDate, $joinDate, - 'today', + 'now', TRUE, $memType, $params diff --git a/CRM/Member/Import/Parser/Membership.php b/CRM/Member/Import/Parser/Membership.php index 50e75e19ee31..5acd0d716691 100644 --- a/CRM/Member/Import/Parser/Membership.php +++ b/CRM/Member/Import/Parser/Membership.php @@ -428,7 +428,7 @@ public function import($onDuplicate, &$values) { $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($startDate, $endDate, $joinDate, - 'today', + 'now', $excludeIsAdmin, $formatted['membership_type_id'], $formatted @@ -518,7 +518,7 @@ public function import($onDuplicate, &$values) { $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($startDate, $endDate, $joinDate, - 'today', + 'now', $excludeIsAdmin, $formatted['membership_type_id'], $formatted diff --git a/api/v3/MembershipStatus.php b/api/v3/MembershipStatus.php index f49c269435f8..906d9f364340 100644 --- a/api/v3/MembershipStatus.php +++ b/api/v3/MembershipStatus.php @@ -150,7 +150,7 @@ function civicrm_api3_membership_status_calc($membershipParams) { $dao = CRM_Core_DAO::executeQuery($query, $params); if ($dao->fetch()) { $membershipTypeID = empty($membershipParams['membership_type_id']) ? $dao->membership_type_id : $membershipParams['membership_type_id']; - $result = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($dao->start_date, $dao->end_date, $dao->join_date, 'today', CRM_Utils_Array::value('ignore_admin_only', $membershipParams), $membershipTypeID, $membershipParams); + $result = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($dao->start_date, $dao->end_date, $dao->join_date, 'now', CRM_Utils_Array::value('ignore_admin_only', $membershipParams), $membershipTypeID, $membershipParams); //make is error zero only when valid status found. if (!empty($result['id'])) { $result['is_error'] = 0; diff --git a/tests/phpunit/CRM/Member/BAO/MembershipStatusTest.php b/tests/phpunit/CRM/Member/BAO/MembershipStatusTest.php index 09bf5353f555..e33f916c9aa6 100644 --- a/tests/phpunit/CRM/Member/BAO/MembershipStatusTest.php +++ b/tests/phpunit/CRM/Member/BAO/MembershipStatusTest.php @@ -178,7 +178,7 @@ public function testGetMembershipStatusByDate() { $membershipStatus = CRM_Member_BAO_MembershipStatus::add($params); $toDate = date('Ymd'); - $result = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($toDate, $toDate, $toDate, 'today', TRUE, NULL, $params); + $result = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($toDate, $toDate, $toDate, 'now', TRUE, NULL, $params); $this->assertEquals($result['name'], 'Current', 'Verify membership status record.'); $this->callAPISuccess('MembershipStatus', 'Delete', ['id' => $membershipStatus->id]); diff --git a/tests/phpunit/CRM/Member/Form/MembershipTest.php b/tests/phpunit/CRM/Member/Form/MembershipTest.php index 0f28c333720c..8c5d42578ea3 100644 --- a/tests/phpunit/CRM/Member/Form/MembershipTest.php +++ b/tests/phpunit/CRM/Member/Form/MembershipTest.php @@ -477,6 +477,7 @@ public function testFormRuleFixedJoin6MonthsAgo() { * * @dataProvider getThousandSeparators * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ public function testSubmit($thousandSeparator) { CRM_Core_Session::singleton()->getStatus(TRUE); From ba5e66f74649dcff0884aee170a7ce41c76a2acc Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 7 Sep 2020 08:25:44 +1200 Subject: [PATCH 216/834] Add v4 api LineItem entity --- Civi/Api4/LineItem.php | 28 +++++++++++++++++++++++++ tests/phpunit/api/v3/LineItemTest.php | 30 +++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 Civi/Api4/LineItem.php diff --git a/Civi/Api4/LineItem.php b/Civi/Api4/LineItem.php new file mode 100644 index 000000000000..c630346a4b25 --- /dev/null +++ b/Civi/Api4/LineItem.php @@ -0,0 +1,28 @@ +_apiversion = $version; $this->enableSalesTaxOnFinancialType('Donation'); $this->params['financial_type_id'] = 'Donation'; $result = $this->callAPISuccess('LineItem', 'create', $this->params); @@ -67,6 +72,8 @@ public function testCreateLineItemWithTax() { * * @todo move to a trait, share. * + * @dataProvider versionThreeAndFour + * * @param string $type */ public function enableSalesTaxOnFinancialType($type) { @@ -77,9 +84,14 @@ public function enableSalesTaxOnFinancialType($type) { /** * Test basic create line item. * + * @param int $version + * + * @dataProvider versionThreeAndFour + * * @throws \CRM_Core_Exception */ - public function testCreateLineItem() { + public function testCreateLineItem($version) { + $this->_apiversion = $version; $result = $this->callAPIAndDocument($this->_entity, 'create', $this->params, __FUNCTION__, __FILE__)['values']; $this->assertCount(1, $result); $this->getAndCheck($this->params, key($result), $this->_entity); @@ -87,8 +99,13 @@ public function testCreateLineItem() { /** * Test basic get line item. + * + * @param int $version + * + * @dataProvider versionThreeAndFour */ - public function testGetBasicLineItem() { + public function testGetBasicLineItem($version) { + $this->_apiversion = $version; $getParams = [ 'entity_table' => 'civicrm_contribution', ]; @@ -99,9 +116,14 @@ public function testGetBasicLineItem() { /** * Test delete line item. * + * @param int $version + * + * @dataProvider versionThreeAndFour + * * @throws \CRM_Core_Exception */ - public function testDeleteLineItem() { + public function testDeleteLineItem($version) { + $this->_apiversion = $version; $getParams = [ 'entity_table' => 'civicrm_contribution', ]; From 8eacd5221c7d4d26c5b959b0ef0b636f76097ba4 Mon Sep 17 00:00:00 2001 From: Matthew Wire Date: Mon, 7 Sep 2020 16:32:18 +0100 Subject: [PATCH 217/834] Clarify what getMembershipCount does --- CRM/Price/BAO/PriceSet.php | 10 +++++++--- tests/phpunit/CRM/Price/BAO/PriceSetTest.php | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CRM/Price/BAO/PriceSet.php b/CRM/Price/BAO/PriceSet.php index d2c1643071a2..7481dd09fae0 100644 --- a/CRM/Price/BAO/PriceSet.php +++ b/CRM/Price/BAO/PriceSet.php @@ -1234,16 +1234,20 @@ public static function getPricesetCount($sid, $onlyActive = TRUE) { } /** - * @param $ids + * Return a count of priceFieldValueIDs that are memberships by organisation and membership type + * + * @param string $priceFieldValueIDs + * Comma separated string of priceFieldValue IDs * * @return array + * Returns an array of counts by membership organisation */ - public static function getMembershipCount($ids) { + public static function getMembershipCount($priceFieldValueIDs) { $queryString = " SELECT count( pfv.id ) AS count, mt.member_of_contact_id AS id FROM civicrm_price_field_value pfv INNER JOIN civicrm_membership_type mt ON mt.id = pfv.membership_type_id -WHERE pfv.id IN ( $ids ) +WHERE pfv.id IN ( $priceFieldValueIDs ) GROUP BY mt.member_of_contact_id "; $crmDAO = CRM_Core_DAO::executeQuery($queryString); diff --git a/tests/phpunit/CRM/Price/BAO/PriceSetTest.php b/tests/phpunit/CRM/Price/BAO/PriceSetTest.php index 1c311ed327f7..36e1e19285cc 100644 --- a/tests/phpunit/CRM/Price/BAO/PriceSetTest.php +++ b/tests/phpunit/CRM/Price/BAO/PriceSetTest.php @@ -82,7 +82,7 @@ public function testCopyPriceSet() { /** * Test CRM_Price_BAO_PriceSet::getMembershipCount() that return correct number of - * membership type occurances against it's corresponding member orgaisation + * membership type occurrences against it's corresponding member organisation * * @throws \CRM_Core_Exception */ From db2be805220ef9197e1db2233047613bd30f4f26 Mon Sep 17 00:00:00 2001 From: eileen Date: Sat, 13 Jun 2020 10:50:44 +1200 Subject: [PATCH 218/834] Revert "PropertyBag - add cardNumber to getters" This reverts commit 194ae0f4206d0de00dea44553ac8a679cd771ea4. --- Civi/Payment/PropertyBag.php | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/Civi/Payment/PropertyBag.php b/Civi/Payment/PropertyBag.php index 7b11524e9892..49a4ea4e8c99 100644 --- a/Civi/Payment/PropertyBag.php +++ b/Civi/Payment/PropertyBag.php @@ -34,14 +34,12 @@ class PropertyBag implements \ArrayAccess { 'billingPostalCode' => TRUE, 'billingCounty' => TRUE, 'billingCountry' => TRUE, - 'cardNumber' => TRUE, 'contactID' => TRUE, 'contact_id' => 'contactID', 'contributionID' => TRUE, 'contribution_id' => 'contributionID', 'contributionRecurID' => TRUE, 'contribution_recur_id' => 'contributionRecurID', - 'credit_card_number' => 'cardNumber', 'currency' => TRUE, 'currencyID' => 'currency', 'description' => TRUE, @@ -594,29 +592,6 @@ public function setBillingCountry($input, $label = 'default') { } /** - * Get the (generally credit) card number. - * - * @param string $label - * - * @return string - */ - public function getCardNumber($label = 'default'): string { - return $this->get('cardNumber', $label); - } - - /** - * @param string $cardNumber - * @param string $label - * - * @return \Civi\Payment\PropertyBag - */ - public function setCardNumber($cardNumber, $label = 'default'): PropertyBag { - return $this->set('cardNumber', $label, (string) $cardNumber); - } - - /** - * @param string $label - * * @return int */ public function getContactID($label = 'default'): int { @@ -626,8 +601,6 @@ public function getContactID($label = 'default'): int { /** * @param int $contactID * @param string $label - * - * @return \Civi\Payment\PropertyBag */ public function setContactID($contactID, $label = 'default') { // We don't use this because it counts zero as positive: CRM_Utils_Type::validate($contactID, 'Positive'); From df1ebfea8e38ef4acd15e2ba799c07109782bafd Mon Sep 17 00:00:00 2001 From: Matthew Wire Date: Mon, 7 Sep 2020 16:31:28 +0100 Subject: [PATCH 219/834] Switch backend membership form to use non-deprecated/cached functions to get membership types --- CRM/Member/Form/Membership.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CRM/Member/Form/Membership.php b/CRM/Member/Form/Membership.php index 3537d0c83348..8c2af0302b07 100644 --- a/CRM/Member/Form/Membership.php +++ b/CRM/Member/Form/Membership.php @@ -225,7 +225,9 @@ public function preProcess() { $cMemTypes[] = $mem['membership_type_id']; } if (count($cMemTypes) > 0) { - $memberorgs = CRM_Member_BAO_MembershipType::getMemberOfContactByMemTypes($cMemTypes); + foreach ($cMemTypes as $memTypeID) { + $memberorgs[$memTypeID] = CRM_Member_BAO_MembershipType::getMembershipType($memTypeID)['member_of_contact_id']; + } $mems_by_org = []; foreach ($contactMemberships as $mem) { $mem['member_of_contact_id'] = $memberorgs[$mem['membership_type_id']] ?? NULL; @@ -759,8 +761,7 @@ public static function formRule($params, $files, $self) { $endDate = CRM_Utils_Date::processDate($params['end_date']); } - $membershipDetails = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($memType); - + $membershipDetails = CRM_Member_BAO_MembershipType::getMembershipType($memType); if ($startDate && CRM_Utils_Array::value('period_type', $membershipDetails) === 'rolling') { if ($startDate < $joinDate) { $errors['start_date'] = ts('Start date must be the same or later than Member since.'); From 7d9f60daadc66ca356862fbeeab6d72d61637422 Mon Sep 17 00:00:00 2001 From: Brian Shaughnessy Date: Thu, 10 Sep 2020 15:10:09 -0400 Subject: [PATCH 220/834] dev/core#2009 filter grant dashboard to exclude trashed contacts --- CRM/Grant/BAO/Grant.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CRM/Grant/BAO/Grant.php b/CRM/Grant/BAO/Grant.php index d38aeb5c41b4..e203d546f558 100644 --- a/CRM/Grant/BAO/Grant.php +++ b/CRM/Grant/BAO/Grant.php @@ -25,8 +25,13 @@ class CRM_Grant_BAO_Grant extends CRM_Grant_DAO_Grant { */ public static function getGrantSummary($admin = FALSE) { $query = " - SELECT status_id, count(id) as status_total - FROM civicrm_grant GROUP BY status_id"; + SELECT status_id, count(g.id) as status_total + FROM civicrm_grant g + JOIN civicrm_contact c + ON g.contact_id = c.id + WHERE c.is_deleted = 0 + GROUP BY status_id + "; $dao = CRM_Core_DAO::executeQuery($query); From 01a5d8d498a250e73c11089c392c6587a8907706 Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 7 Sep 2020 11:00:15 +1200 Subject: [PATCH 221/834] Simplify parameters passed to completeOrder Now that completeOrder has had most interaction with objects var cleaned up we know only 2 objects are actually used - contribution - which we can fetch in-function - paymentProcessor - which we were actually only guessing This alters it to only load & pass contribution. The value of passing in processor seems lost in time. This code can only be reached for pay_later contributions - see CRM_Contribute_BAO_Contribution::checkOnlinePendingContribution and specifically https://github.com/civicrm/civicrm-core/blob/318c63045e82d30ed7fe8cdacb91a6d31fc5331b/CRM/Contribute/BAO/Contribution.php#L2033 and only when submitted via an online event (same function) so by definition there IS NO PAYMENT PROCESSOR that applies. Given that we can just do the minimum to load the contribution and pass that in. --- CRM/Event/Form/Task/Batch.php | 42 ++++++----------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/CRM/Event/Form/Task/Batch.php b/CRM/Event/Form/Task/Batch.php index 11341b917da8..395feda8652f 100644 --- a/CRM/Event/Form/Task/Batch.php +++ b/CRM/Event/Form/Task/Batch.php @@ -312,43 +312,15 @@ public static function updatePendingOnlineContribution($participantId, $statusId * */ public static function updateContributionStatus($params) { - // get minimum required values. - $componentId = $params['component_id'] ?? NULL; - $contributionId = $params['contribution_id'] ?? NULL; - - $input = $ids = $objects = []; - - //get the required ids. - $ids['contribution'] = $contributionId; - $ids['participant'] = $params['component_id']; - - if (!$ids['contact'] = CRM_Utils_Array::value('contact_id', $params)) { - $ids['contact'] = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', - $contributionId, - 'contact_id' - ); - } - - if (!$ids['event'] = CRM_Utils_Array::value('event_id', $params)) { - $ids['event'] = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant', - $componentId, - 'event_id' - ); - } - - $input['component'] = 'event'; - - $baseIPN = new CRM_Core_Payment_BaseIPN(); + $input = ['component' => 'event']; // reset template values. $template = CRM_Core_Smarty::singleton(); $template->clearTemplateVars(); - if (!$baseIPN->validateData($input, $ids, $objects, FALSE)) { - throw new CRM_Core_Exception('validation error'); - } - - $contribution = &$objects['contribution']; + $contribution = new CRM_Contribute_BAO_Contribution(); + $contribution->id = $params['contribution_id']; + $contribution->fetch(); $contributionStatuses = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'contribution_status_id', [ 'labelColumn' => 'name', @@ -387,10 +359,10 @@ public static function updateContributionStatus($params) { // @todo use the api - ie civicrm_api3('Contribution', 'completetransaction', $input); // as this method is not preferred / supported. CRM_Contribute_BAO_Contribution::completeOrder($input, [ - 'related_contact' => $ids['related_contact'] ?? NULL, - 'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL, + 'related_contact' => NULL, + 'participant' => $params['component_id'], 'contributionRecur' => NULL, - ], $objects); + ], ['contribution' => $contribution]); // reset template values before processing next transactions $template->clearTemplateVars(); From ffdc26b86f8821db7640dc8f864353c61d3e7e60 Mon Sep 17 00:00:00 2001 From: eileen Date: Fri, 11 Sep 2020 20:59:09 +1200 Subject: [PATCH 222/834] dev/core#2017 Remove unused function OpenID::isAllowedToLogin --- CRM/Core/BAO/OpenID.php | 17 --------- tests/phpunit/CRM/Core/BAO/OpenIDTest.php | 45 ----------------------- 2 files changed, 62 deletions(-) diff --git a/CRM/Core/BAO/OpenID.php b/CRM/Core/BAO/OpenID.php index 34326a756257..365b60060391 100644 --- a/CRM/Core/BAO/OpenID.php +++ b/CRM/Core/BAO/OpenID.php @@ -43,23 +43,6 @@ public static function &getValues($entityBlock) { return CRM_Core_BAO_Block::getValues('openid', $entityBlock); } - /** - * Returns whether or not this OpenID is allowed to login. - * - * @param string $identity_url - * The OpenID to check. - * - * @return bool - */ - public static function isAllowedToLogin($identity_url) { - $openId = new CRM_Core_DAO_OpenID(); - $openId->openid = $identity_url; - if ($openId->find(TRUE)) { - return $openId->allowed_to_login == 1; - } - return FALSE; - } - /** * Get all the openids for a specified contact_id, with the primary openid being first * diff --git a/tests/phpunit/CRM/Core/BAO/OpenIDTest.php b/tests/phpunit/CRM/Core/BAO/OpenIDTest.php index 1d0886b5c461..0e4446635893 100644 --- a/tests/phpunit/CRM/Core/BAO/OpenIDTest.php +++ b/tests/phpunit/CRM/Core/BAO/OpenIDTest.php @@ -62,51 +62,6 @@ public function testAdd() { $this->assertDBRowNotExist('CRM_Contact_DAO_Contact', $contactId); } - /** - * IfAllowedToLogin() method (set and reset allowed_to_login) - */ - public function testIfAllowedToLogin() { - $contactId = $this->individualCreate(); - $this->assertDBRowExist('CRM_Contact_DAO_Contact', $contactId); - $openIdURL = "http://test-username.civicrm.org/"; - - $params = [ - 'contact_id' => $contactId, - 'location_type_id' => 1, - 'openid' => $openIdURL, - 'is_primary' => 1, - ]; - - $openObject = CRM_Core_BAO_OpenID::add($params); - - $openId = $openObject->id; - $this->assertDBNotNull('CRM_Core_DAO_OpenID', $openIdURL, 'id', 'openid', - 'Database check for created OpenID.' - ); - - $allowedToLogin = CRM_Core_BAO_OpenID::isAllowedToLogin($openIdURL); - $this->assertEquals($allowedToLogin, FALSE, 'Verify allowed_to_login value is 0.'); - - // Now call add() to modify an existing open-id record - - $params = [ - 'id' => $openId, - 'contact_id' => $contactId, - 'openid' => $openIdURL, - 'is_bulkmail' => 1, - 'allowed_to_login' => 1, - ]; - - CRM_Core_BAO_OpenID::add($params); - - $allowedToLogin = CRM_Core_BAO_OpenID::isAllowedToLogin($openIdURL); - - $this->assertEquals($allowedToLogin, TRUE, 'Verify allowed_to_login value is 1.'); - $this->contactDelete($contactId); - //domain contact doesn't really get deleted // - $this->assertDBRowNotExist('CRM_Contact_DAO_Contact', $contactId); - } - /** * AllOpenIDs() method - get all OpenIDs for the given contact */ From 7c326e9ee9c47cb0ab7a3701d2062f715a290a8a Mon Sep 17 00:00:00 2001 From: Rich Lott / Artful Robot Date: Fri, 11 Sep 2020 14:30:33 +0100 Subject: [PATCH 223/834] Add tests for PropertyBag::setAmount() --- Civi/Payment/PropertyBag.php | 15 +++++- .../phpunit/Civi/Payment/PropertyBagTest.php | 50 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/Civi/Payment/PropertyBag.php b/Civi/Payment/PropertyBag.php index f8817b8c4ecb..73179edb78ce 100644 --- a/Civi/Payment/PropertyBag.php +++ b/Civi/Payment/PropertyBag.php @@ -417,7 +417,20 @@ public function getAmount($label = 'default') { } /** - * Get the monetary amount. + * Set the monetary amount. + * + * - We expect to be called with a string amount with optional decimals using + * a '.' as the decimal point (not a ','). + * + * - We're ok with floats/ints being passed in, too, but we'll cast them to a + * string. + * + * - Negatives are fine. + * + * @see https://github.com/civicrm/civicrm-core/pull/18219 + * + * @param string|float|int $value + * @param string $label */ public function setAmount($value, $label = 'default') { if (!is_numeric($value)) { diff --git a/tests/phpunit/Civi/Payment/PropertyBagTest.php b/tests/phpunit/Civi/Payment/PropertyBagTest.php index 0a66c6dd7ce5..b127f10ee4f3 100644 --- a/tests/phpunit/Civi/Payment/PropertyBagTest.php +++ b/tests/phpunit/Civi/Payment/PropertyBagTest.php @@ -44,6 +44,56 @@ public function testSetContactID() { $this->assertEquals(456, $propertyBag->getContactID('new')); } + /** + * Test we can set an amount. + * + * @see https://github.com/civicrm/civicrm-core/pull/18219 + * + * @dataProvider setAmountDataProvider + * + * @param mixed $value input + * @param mixed $expect output expected. Typically a string like '1.23' or NULL + * @param string $expectedExceptionMessage if there is one expected. + */ + public function testSetAmount($value, $expect, $expectedExceptionMessage = '') { + $propertyBag = new PropertyBag(); + try { + $propertyBag->setAmount($value); + } + catch (\Exception $e) { + if ($expectedExceptionMessage) { + $this->assertEquals($expectedExceptionMessage, $e->getMessage(), 'Expected a different exception.'); + // OK. + return; + } + // not expecting an exception, re-throw it. + throw $e; + } + $got = $propertyBag->getAmount(); + $this->assertInternalType('string', $got); + $this->assertEquals($expect, $got); + } + + /** + * + */ + public function setAmountDataProvider() { + return [ + [1, '1'], + [1.23, '1.23'], + [1.234, '1.234'], + [-1.23, '-1.23'], + ['1', '1'], + ['1.23', '1.23'], + ['1.234', '1.234'], + ['-1.23', '-1.23'], + ['1,000.23', NULL, 'setAmount requires a numeric amount value'], + ['1,000', NULL, 'setAmount requires a numeric amount value'], + ['1,23', NULL, 'setAmount requires a numeric amount value'], + ['1.230,12', NULL, 'setAmount requires a numeric amount value'], + ]; + } + /** * Test we cannot set an invalid contact ID. * From 1684bccdd7b88b229a883a605d9c95f73650f0ed Mon Sep 17 00:00:00 2001 From: Pradeep Nayak Date: Tue, 8 Sep 2020 12:42:58 +0100 Subject: [PATCH 224/834] Replace & to and in button label --- CRM/Financial/Form/BatchTransaction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CRM/Financial/Form/BatchTransaction.php b/CRM/Financial/Form/BatchTransaction.php index 20848b58ecae..ef90e96c026a 100644 --- a/CRM/Financial/Form/BatchTransaction.php +++ b/CRM/Financial/Form/BatchTransaction.php @@ -87,7 +87,7 @@ public function buildQuickForm() { if (CRM_Batch_BAO_Batch::checkBatchPermission('close', $this->_values['created_id'])) { $this->add('xbutton', 'close_batch', ts('Close Batch'), ['type' => 'submit']); if (CRM_Batch_BAO_Batch::checkBatchPermission('export', $this->_values['created_id'])) { - $this->add('xbutton', 'export_batch', ts('Close & Export Batch'), ['type' => 'submit']); + $this->add('xbutton', 'export_batch', ts('Close and Export Batch'), ['type' => 'submit']); } } From 721c9da1a14e155e13d528106ca32690f171b515 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Thu, 3 Sep 2020 17:50:07 -0400 Subject: [PATCH 225/834] CustomField - Reformat data when modifying field serialize property. Adds or removes CRM_Core_DAO::VALUE_SEPARATOR from custom values when switching a field from single to muti-valued. --- CRM/Core/BAO/CustomField.php | 27 +++++ CRM/Core/DAO/AllCoreTables.php | 4 +- Civi/Api4/Generic/AbstractAction.php | 6 +- Civi/Api4/Generic/Traits/DAOActionTrait.php | 28 +++-- Civi/Api4/Query/Api4SelectQuery.php | 2 +- .../Schema/Joinable/CustomGroupJoinable.php | 11 +- .../Api4/Service/Schema/Joinable/Joinable.php | 18 +-- Civi/Api4/Utils/FormattingUtil.php | 18 ++- .../api/v4/Action/CustomFieldAlterTest.php | 114 ++++++++++++++++++ 9 files changed, 187 insertions(+), 41 deletions(-) create mode 100644 tests/phpunit/api/v4/Action/CustomFieldAlterTest.php diff --git a/CRM/Core/BAO/CustomField.php b/CRM/Core/BAO/CustomField.php index 107242a5155c..c67b60f7a9c5 100644 --- a/CRM/Core/BAO/CustomField.php +++ b/CRM/Core/BAO/CustomField.php @@ -1696,6 +1696,25 @@ public static function getAlterFieldSQL($field, $operation) { return CRM_Core_BAO_SchemaHandler::getFieldAlterSQL($params, $indexExist); } + /** + * Reformat existing values for a field when changing its serialize attribute + * + * @param CRM_Core_DAO_CustomField $field + * @throws CRM_Core_Exception + */ + private static function alterSerialize($field) { + $table = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $field->custom_group_id, 'table_name'); + $col = $field->column_name; + $sp = CRM_Core_DAO::VALUE_SEPARATOR; + if ($field->serialize) { + $sql = "UPDATE `$table` SET `$col` = CONCAT('$sp', `$col`, '$sp') WHERE `$col` IS NOT NULL AND `$col` NOT LIKE '$sp%' AND `$col` != ''"; + } + else { + $sql = "UPDATE `$table` SET `$col` = SUBSTRING_INDEX(SUBSTRING(`$col`, 2), '$sp', 1) WHERE `$col` LIKE '$sp%'"; + } + CRM_Core_DAO::executeQuery($sql); + } + /** * Determine whether it would be safe to move a field. * @@ -1989,6 +2008,9 @@ protected static function createCustomFieldRecord($params) { $transaction = new CRM_Core_Transaction(); $params = self::prepareCreate($params); + $alterSerialize = isset($params['serialize']) && !empty($params['id']) + && ($params['serialize'] != CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', $params['id'], 'serialize')); + $customField = new CRM_Core_DAO_CustomField(); $customField->copyValues($params); $customField->save(); @@ -2008,6 +2030,11 @@ protected static function createCustomFieldRecord($params) { // make sure all values are present in the object for further processing $customField->find(TRUE); + + if ($alterSerialize) { + self::alterSerialize($customField); + } + return $customField; } diff --git a/CRM/Core/DAO/AllCoreTables.php b/CRM/Core/DAO/AllCoreTables.php index 5b9ffe5ef671..9e4970c301d4 100644 --- a/CRM/Core/DAO/AllCoreTables.php +++ b/CRM/Core/DAO/AllCoreTables.php @@ -280,7 +280,7 @@ public static function getClasses() { * Get the classname for the table. * * @param string $tableName - * @return string + * @return string|CRM_Core_DAO|NULL */ public static function getClassForTable($tableName) { //CRM-19677: on multilingual setup, trim locale from $tableName to fetch class name @@ -296,7 +296,7 @@ public static function getClassForTable($tableName) { * * @param string $daoName * Ex: 'Contact'. - * @return CRM_Core_DAO|NULL + * @return string|CRM_Core_DAO|NULL * Ex: 'CRM_Contact_DAO_Contact'. */ public static function getFullName($daoName) { diff --git a/Civi/Api4/Generic/AbstractAction.php b/Civi/Api4/Generic/AbstractAction.php index 8d0111e6b35d..ea17309053a9 100644 --- a/Civi/Api4/Generic/AbstractAction.php +++ b/Civi/Api4/Generic/AbstractAction.php @@ -494,7 +494,7 @@ protected function formatWriteValues(&$record) { if ($field) { $optionFields[$fieldName] = [ 'val' => $record[$expr], - 'name' => empty($field['custom_field_id']) ? $field['name'] : 'custom_' . $field['custom_field_id'], + 'field' => $field, 'suffix' => substr($expr, $suffix + 1), 'depends' => $field['input_attrs']['controlField'] ?? NULL, ]; @@ -504,11 +504,11 @@ protected function formatWriteValues(&$record) { } // Sort option lookups by dependency, so e.g. country_id is processed first, then state_province_id, then county_id uasort($optionFields, function ($a, $b) { - return $a['name'] === $b['depends'] ? -1 : 1; + return $a['field']['name'] === $b['depends'] ? -1 : 1; }); // Replace pseudoconstants. Note this is a reverse lookup as we are evaluating input not output. foreach ($optionFields as $fieldName => $info) { - $options = FormattingUtil::getPseudoconstantList($this->_entityName, $info['name'], $info['suffix'], $record, 'create'); + $options = FormattingUtil::getPseudoconstantList($info['field'], $info['suffix'], $record, 'create'); $record[$fieldName] = FormattingUtil::replacePseudoconstant($options, $info['val'], TRUE); } } diff --git a/Civi/Api4/Generic/Traits/DAOActionTrait.php b/Civi/Api4/Generic/Traits/DAOActionTrait.php index 6497168a3d95..9404b11ecf6f 100644 --- a/Civi/Api4/Generic/Traits/DAOActionTrait.php +++ b/Civi/Api4/Generic/Traits/DAOActionTrait.php @@ -13,6 +13,7 @@ namespace Civi\Api4\Generic\Traits; use Civi\Api4\CustomField; +use Civi\Api4\Service\Schema\Joinable\CustomGroupJoinable; use Civi\Api4\Utils\FormattingUtil; /** @@ -178,7 +179,7 @@ protected function formatCustomParams(&$params, $entityId) { if (NULL !== $value) { if ($field['suffix']) { - $options = FormattingUtil::getPseudoconstantList($this->getEntityName(), 'custom_' . $field['id'], $field['suffix'], $params, $this->getActionName()); + $options = FormattingUtil::getPseudoconstantList($field, $field['suffix'], $params, $this->getActionName()); $value = FormattingUtil::replacePseudoconstant($options, $value, TRUE); } @@ -210,24 +211,33 @@ protected function formatCustomParams(&$params, $entityId) { /** * Gets field info needed to save custom data * - * @param string $name + * @param string $fieldExpr * Field identifier with possible suffix, e.g. MyCustomGroup.MyField1:label * @return array|NULL */ - protected function getCustomFieldInfo($name) { - if (strpos($name, '.') === FALSE) { + protected function getCustomFieldInfo(string $fieldExpr) { + if (strpos($fieldExpr, '.') === FALSE) { return NULL; } - list($groupName, $fieldName) = explode('.', $name); + list($groupName, $fieldName) = explode('.', $fieldExpr); list($fieldName, $suffix) = array_pad(explode(':', $fieldName), 2, NULL); - if (empty(\Civi::$statics['APIv4_Custom_Fields'][$groupName])) { - \Civi::$statics['APIv4_Custom_Fields'][$groupName] = (array) CustomField::get(FALSE) + $cacheKey = "APIv4_Custom_Fields-$groupName"; + $info = \Civi::cache('metadata')->get($cacheKey); + if (!isset($info[$fieldName])) { + $info = []; + $fields = CustomField::get(FALSE) ->addSelect('id', 'name', 'html_type', 'custom_group.extends') ->addWhere('custom_group.name', '=', $groupName) ->execute()->indexBy('name'); + foreach ($fields as $name => $field) { + $field['custom_field_id'] = $field['id']; + $field['name'] = $groupName . '.' . $name; + $field['entity'] = CustomGroupJoinable::getEntityFromExtends($field['custom_group.extends']); + $info[$name] = $field; + } + \Civi::cache('metadata')->set($cacheKey, $info); } - $info = \Civi::$statics['APIv4_Custom_Fields'][$groupName][$fieldName] ?? NULL; - return $info ? ['suffix' => $suffix] + $info : NULL; + return isset($info[$fieldName]) ? ['suffix' => $suffix] + $info[$fieldName] : NULL; } /** diff --git a/Civi/Api4/Query/Api4SelectQuery.php b/Civi/Api4/Query/Api4SelectQuery.php index e4f8ceb187fc..009095ebc91d 100644 --- a/Civi/Api4/Query/Api4SelectQuery.php +++ b/Civi/Api4/Query/Api4SelectQuery.php @@ -275,7 +275,7 @@ protected function buildOrderBy() { $suffix = strstr($item, ':'); if ($suffix && $expr->getType() === 'SqlField') { $field = $this->getField($item); - $options = FormattingUtil::getPseudoconstantList($field['entity'], $field['name'], substr($suffix, 1)); + $options = FormattingUtil::getPseudoconstantList($field, substr($suffix, 1)); if ($options) { asort($options); $column = "FIELD($column,'" . implode("','", array_keys($options)) . "')"; diff --git a/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php b/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php index a8ed4042b062..510ff78826c6 100644 --- a/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php +++ b/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php @@ -56,16 +56,19 @@ public function __construct($targetTable, $alias, $isMultiRecord, $columns) { * @inheritDoc */ public function getEntityFields() { - if (!$this->entityFields) { + $cacheKey = 'APIv4_CustomGroupJoinable-' . $this->getTargetTable(); + $entityFields = (array) \Civi::cache('metadata')->get($cacheKey); + if (!$entityFields) { $fields = CustomField::get(FALSE) ->setSelect(['custom_group.name', 'custom_group.extends', 'custom_group.table_name', '*']) ->addWhere('custom_group.table_name', '=', $this->getTargetTable()) ->execute(); foreach ($fields as $field) { - $this->entityFields[] = \Civi\Api4\Service\Spec\SpecFormatter::arrayToField($field, $this->getEntityFromExtends($field['custom_group.extends'])); + $entityFields[] = \Civi\Api4\Service\Spec\SpecFormatter::arrayToField($field, self::getEntityFromExtends($field['custom_group.extends'])); } + \Civi::cache('metadata')->set($cacheKey, $entityFields); } - return $this->entityFields; + return $entityFields; } /** @@ -102,7 +105,7 @@ public function getSqlColumn($fieldName) { * @throws \API_Exception * @throws \Civi\API\Exception\UnauthorizedException */ - private function getEntityFromExtends($extends) { + public static function getEntityFromExtends($extends) { if (strpos($extends, 'Participant') === 0) { return 'Participant'; } diff --git a/Civi/Api4/Service/Schema/Joinable/Joinable.php b/Civi/Api4/Service/Schema/Joinable/Joinable.php index 0375fa0c2a05..8e6d4e711680 100644 --- a/Civi/Api4/Service/Schema/Joinable/Joinable.php +++ b/Civi/Api4/Service/Schema/Joinable/Joinable.php @@ -78,11 +78,6 @@ class Joinable { */ protected $entity; - /** - * @var array - */ - protected $entityFields; - /** * @param $targetTable * @param $targetColumn @@ -268,15 +263,14 @@ public function toArray() { * @return \Civi\Api4\Service\Spec\FieldSpec[] */ public function getEntityFields() { - if (!$this->entityFields) { - $bao = AllCoreTables::getClassForTable($this->getTargetTable()); - if ($bao) { - foreach ($bao::fields() as $field) { - $this->entityFields[] = \Civi\Api4\Service\Spec\SpecFormatter::arrayToField($field, $this->getEntity()); - } + $entityFields = []; + $bao = AllCoreTables::getClassForTable($this->getTargetTable()); + if ($bao) { + foreach ($bao::getSupportedFields() as $field) { + $entityFields[] = \Civi\Api4\Service\Spec\SpecFormatter::arrayToField($field, $this->getEntity()); } } - return $this->entityFields; + return $entityFields; } /** diff --git a/Civi/Api4/Utils/FormattingUtil.php b/Civi/Api4/Utils/FormattingUtil.php index 900df21ccfe8..f170969a7fda 100644 --- a/Civi/Api4/Utils/FormattingUtil.php +++ b/Civi/Api4/Utils/FormattingUtil.php @@ -88,7 +88,7 @@ public static function formatInputValue(&$value, $fieldName, $fieldSpec, $action // Evaluate pseudoconstant suffix $suffix = strpos($fieldName, ':'); if ($suffix) { - $options = self::getPseudoconstantList($fieldSpec['entity'], $fieldSpec['name'], substr($fieldName, $suffix + 1), $action); + $options = self::getPseudoconstantList($fieldSpec, substr($fieldName, $suffix + 1), $action); $value = self::replacePseudoconstant($options, $value, TRUE); return; } @@ -148,8 +148,7 @@ public static function formatOutputValues(&$results, $fields, $entity, $action = // Evaluate pseudoconstant suffixes $suffix = strrpos($fieldExpr, ':'); if ($suffix) { - $fieldName = empty($field['custom_field_id']) ? $field['name'] : 'custom_' . $field['custom_field_id']; - $fieldOptions[$fieldExpr] = $fieldOptions[$fieldExpr] ?? self::getPseudoconstantList($field['entity'], $fieldName, substr($fieldExpr, $suffix + 1), $result, $action); + $fieldOptions[$fieldExpr] = $fieldOptions[$fieldExpr] ?? self::getPseudoconstantList($field, substr($fieldExpr, $suffix + 1), $result, $action); $dataType = NULL; } if (!empty($field['serialize'])) { @@ -178,9 +177,7 @@ public static function formatOutputValues(&$results, $fields, $entity, $action = /** * Retrieves pseudoconstant option list for a field. * - * @param string $entity - * Name of api entity - * @param string $fieldName + * @param array $field * @param string $valueType * name|label|abbr from self::$pseudoConstantContexts * @param array $params @@ -189,27 +186,28 @@ public static function formatOutputValues(&$results, $fields, $entity, $action = * @return array * @throws \API_Exception */ - public static function getPseudoconstantList($entity, $fieldName, $valueType, $params = [], $action = 'get') { + public static function getPseudoconstantList($field, $valueType, $params = [], $action = 'get') { $context = self::$pseudoConstantContexts[$valueType] ?? NULL; // For create actions, only unique identifiers can be used. // For get actions any valid suffix is ok. if (($action === 'create' && !$context) || !in_array($valueType, self::$pseudoConstantSuffixes, TRUE)) { throw new \API_Exception('Illegal expression'); } - $baoName = $context ? CoreUtil::getBAOFromApiName($entity) : NULL; + $baoName = $context ? CoreUtil::getBAOFromApiName($field['entity']) : NULL; // Use BAO::buildOptions if possible if ($baoName) { + $fieldName = empty($field['custom_field_id']) ? $field['name'] : 'custom_' . $field['custom_field_id']; $options = $baoName::buildOptions($fieldName, $context, $params); } // Fallback for option lists that exist in the api but not the BAO if (!isset($options) || $options === FALSE) { - $options = civicrm_api4($entity, 'getFields', ['action' => $action, 'loadOptions' => ['id', $valueType], 'where' => [['name', '=', $fieldName]]])[0]['options'] ?? NULL; + $options = civicrm_api4($field['entity'], 'getFields', ['action' => $action, 'loadOptions' => ['id', $valueType], 'where' => [['name', '=', $field['name']]]])[0]['options'] ?? NULL; $options = $options ? array_column($options, $valueType, 'id') : $options; } if (is_array($options)) { return $options; } - throw new \API_Exception("No option list found for '$fieldName'"); + throw new \API_Exception("No option list found for '{$field['name']}'"); } /** diff --git a/tests/phpunit/api/v4/Action/CustomFieldAlterTest.php b/tests/phpunit/api/v4/Action/CustomFieldAlterTest.php new file mode 100644 index 000000000000..3e44ca82e2d5 --- /dev/null +++ b/tests/phpunit/api/v4/Action/CustomFieldAlterTest.php @@ -0,0 +1,114 @@ +createEntity(['type' => 'Individual']); + + $customGroup = CustomGroup::create(FALSE) + ->addValue('title', 'MyFieldsToAlter') + ->addValue('extends', 'Activity') + ->addChain('field1', CustomField::create() + ->addValue('custom_group_id', '$id') + ->addValue('label', 'TestOptions') + ->addValue('html_type', 'Select') + ->addValue('option_values', [ + 1 => 'One', + 2 => 'Two', + 3 => 'Three', + 4 => 'Four', + ]), 0 + ) + ->addChain('field2', CustomField::create() + ->addValue('custom_group_id', '$id') + ->addValue('label', 'TestText') + ->addValue('html_type', 'Text'), 0 + ) + ->execute() + ->first(); + + Activity::save(FALSE) + ->setDefaults(['activity_type_id' => 1, 'source_contact_id' => $contact['id']]) + ->addRecord(['subject' => 'A1', 'MyFieldsToAlter.TestText' => 'A1', 'MyFieldsToAlter.TestOptions' => '1']) + ->addRecord(['subject' => 'A2', 'MyFieldsToAlter.TestText' => 'A2', 'MyFieldsToAlter.TestOptions' => '2']) + ->addRecord(['subject' => 'A3', 'MyFieldsToAlter.TestText' => 'A3', 'MyFieldsToAlter.TestOptions' => '']) + ->addRecord(['subject' => 'A4', 'MyFieldsToAlter.TestText' => 'A4']) + ->execute(); + + $result = Activity::get(FALSE) + ->addWhere('MyFieldsToAlter.TestText', 'IS NOT NULL') + ->addSelect('custom.*', 'subject', 'MyFieldsToAlter.TestOptions:label') + ->execute()->indexBy('subject'); + + $this->assertCount(4, $result); + $this->assertEquals(1, $result['A1']['MyFieldsToAlter.TestOptions']); + $this->assertEquals(2, $result['A2']['MyFieldsToAlter.TestOptions']); + $this->assertEquals('One', $result['A1']['MyFieldsToAlter.TestOptions:label']); + $this->assertEquals('Two', $result['A2']['MyFieldsToAlter.TestOptions:label']); + $this->assertTrue(empty($result['A3']['MyFieldsToAlter.TestOptions'])); + $this->assertTrue(empty($result['A4']['MyFieldsToAlter.TestOptions'])); + + // Change field to multiselect + CustomField::update(FALSE) + ->addWhere('id', '=', $customGroup['field1']['id']) + ->addValue('serialize', TRUE) + ->execute(); + + $result = Activity::get(FALSE) + ->addWhere('MyFieldsToAlter.TestText', 'IS NOT NULL') + ->addSelect('custom.*', 'subject', 'MyFieldsToAlter.TestOptions:label') + ->execute()->indexBy('subject'); + + $this->assertEquals([1], $result['A1']['MyFieldsToAlter.TestOptions']); + $this->assertEquals([2], $result['A2']['MyFieldsToAlter.TestOptions']); + $this->assertEquals(['One'], $result['A1']['MyFieldsToAlter.TestOptions:label']); + $this->assertTrue(empty($result['A3']['MyFieldsToAlter.TestOptions'])); + $this->assertTrue(empty($result['A4']['MyFieldsToAlter.TestOptions'])); + + // Change back to single-select + CustomField::update(FALSE) + ->addWhere('id', '=', $customGroup['field1']['id']) + ->addValue('serialize', FALSE) + ->execute(); + + $result = Activity::get(FALSE) + ->addWhere('MyFieldsToAlter.TestText', 'IS NOT NULL') + ->addSelect('custom.*', 'subject', 'MyFieldsToAlter.TestOptions:label') + ->execute()->indexBy('subject'); + + $this->assertCount(4, $result); + $this->assertEquals(1, $result['A1']['MyFieldsToAlter.TestOptions']); + $this->assertEquals(2, $result['A2']['MyFieldsToAlter.TestOptions']); + $this->assertEquals('One', $result['A1']['MyFieldsToAlter.TestOptions:label']); + $this->assertEquals('Two', $result['A2']['MyFieldsToAlter.TestOptions:label']); + $this->assertTrue(empty($result['A3']['MyFieldsToAlter.TestOptions'])); + $this->assertTrue(empty($result['A4']['MyFieldsToAlter.TestOptions'])); + } + +} From 9aea3ced3b1d81536d8ffe5183086c7d75655ccb Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Tue, 8 Sep 2020 11:27:32 -0400 Subject: [PATCH 226/834] Custom Field Form - Cleanup country/state leftover cruft --- CRM/Custom/Form/ChangeFieldType.php | 1 - CRM/Custom/Form/Field.php | 25 ++++++++----------------- templates/CRM/Custom/Form/Field.tpl | 6 +++--- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/CRM/Custom/Form/ChangeFieldType.php b/CRM/Custom/Form/ChangeFieldType.php index 8ba794c5cc56..54f9197ea853 100644 --- a/CRM/Custom/Form/ChangeFieldType.php +++ b/CRM/Custom/Form/ChangeFieldType.php @@ -225,7 +225,6 @@ public static function fieldTypeTransitions($dataType, $htmlType) { case 'Int': case 'Float': - case 'Int': case 'Money': if (in_array($htmlType, array_keys($singleValueOps))) { unset($singleValueOps[$htmlType]); diff --git a/CRM/Custom/Form/Field.php b/CRM/Custom/Form/Field.php index af6ca363cbe7..2bbc8e0c073d 100644 --- a/CRM/Custom/Form/Field.php +++ b/CRM/Custom/Form/Field.php @@ -158,8 +158,8 @@ public function preProcess() { ['TextArea' => ts('TextArea'), 'RichTextEditor' => ts('Rich Text Editor')], ['Date' => ts('Select Date')], ['Radio' => ts('Radio')], - ['StateProvince' => ts('Select')], - ['Country' => ts('Select')], + ['Select' => ts('Select')], + ['Select' => ts('Select')], ['File' => ts('Select File')], ['Link' => ts('Link')], ['ContactReference' => ts('Autocomplete-Select')], @@ -180,17 +180,14 @@ public function setDefaultValues() { if ($this->_id) { $this->assign('id', $this->_id); $this->_gid = $defaults['custom_group_id']; + $defaultValue = $defaults['default_value'] ?? NULL; //get the value for state or country - if ($defaults['data_type'] == 'StateProvince' && - $stateId = CRM_Utils_Array::value('default_value', $defaults) - ) { - $defaults['default_value'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_StateProvince', $stateId); + if ($defaults['data_type'] == 'StateProvince' && $defaultValue) { + $defaults['default_value'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_StateProvince', $defaultValue); } - elseif ($defaults['data_type'] == 'Country' && - $countryId = CRM_Utils_Array::value('default_value', $defaults) - ) { - $defaults['default_value'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Country', $countryId); + elseif ($defaults['data_type'] == 'Country' && $defaultValue) { + $defaults['default_value'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Country', $defaultValue); } if ($defaults['data_type'] == 'ContactReference' && !empty($defaults['filter'])) { @@ -259,13 +256,6 @@ public function setDefaultValues() { $defaults['is_view'] = 0; } - if (!empty($defaults['html_type'])) { - $dontShowLink = substr($defaults['html_type'], -14) == 'State/Province' || substr($defaults['html_type'], -7) == 'Country' ? 1 : 0; - } - - if (isset($dontShowLink)) { - $this->assign('dontShowLink', $dontShowLink); - } if ($this->_action & CRM_Core_Action::ADD && CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_gid, 'is_multiple', 'id') ) { @@ -330,6 +320,7 @@ public function buildQuickForm() { if ($this->_action == CRM_Core_Action::UPDATE) { $this->freeze('data_type'); if (!empty($this->_values['option_group_id'])) { + $this->assign('hasOptionGroup', TRUE); // Before dev/core#155 we didn't set the is_reserved flag properly, which should be handled by the upgrade script... // but it is still possible that existing installs may have optiongroups linked to custom fields that are marked reserved. $optionGroupParams['id'] = $this->_values['option_group_id']; diff --git a/templates/CRM/Custom/Form/Field.tpl b/templates/CRM/Custom/Form/Field.tpl index fe0d6914825e..5dd62613b9ca 100644 --- a/templates/CRM/Custom/Form/Field.tpl +++ b/templates/CRM/Custom/Form/Field.tpl @@ -272,7 +272,7 @@ $("#noteColumns, #noteRows, #noteLength", $form).toggle(dataType === 'Memo'); - $(".crm-custom-field-form-block-serialize", $form).toggle(htmlType === 'Select' || htmlType === 'Country' || htmlType === 'StateProvince'); + $(".crm-custom-field-form-block-serialize", $form).toggle(htmlType === 'Select'); } $('[name^="data_type"]', $form).change(customOptionHtmlType); @@ -282,8 +282,8 @@ }); {/literal} -{* Give link to view/edit choice options if in edit mode and html_type is one of the multiple choice types *} -{if $action eq 2 AND ($form.data_type.value.1.0 eq 'CheckBox' OR ($form.data_type.value.1.0 eq 'Radio' AND $form.data_type.value.0.0 neq 6) OR $form.data_type.value.1.0 eq 'Select' OR ($form.data_type.value.1.0 eq 'Multi-Select' AND $dontShowLink neq 1 ) ) } +{* Give link to view/edit option group *} +{if $action eq 2 && !empty($hasOptionGroup) } From 9b85cd33d84cbd8ca53fe346c8b1fc1cf8c90fe0 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Wed, 9 Sep 2020 09:47:23 -0400 Subject: [PATCH 227/834] Custom field admin form reform This overhauls the custom field administration form: - Gets rid of the difficult-to-use hierarchcal select - Removes changeFieldType as a separate form - Allows changing field type on the main form, with improved validation - Fixes up some metadata - Improves choosing default values --- CRM/Core/BAO/CustomField.php | 4 +- CRM/Core/DAO/CustomField.php | 7 +- CRM/Core/SelectValues.php | 1 - CRM/Core/xml/Menu/Admin.xml | 5 - CRM/Custom/Form/ChangeFieldType.php | 309 ---------------- CRM/Custom/Form/Field.php | 344 ++++++------------ js/Common.js | 11 + templates/CRM/Custom/Form/ChangeFieldType.tpl | 46 --- templates/CRM/Custom/Form/Field.tpl | 157 +++++--- tests/phpunit/api/v3/CustomFieldTest.php | 15 +- tests/phpunit/api/v3/CustomValueTest.php | 12 +- xml/schema/Core/CustomField.xml | 5 + 12 files changed, 247 insertions(+), 669 deletions(-) delete mode 100644 CRM/Custom/Form/ChangeFieldType.php delete mode 100644 templates/CRM/Custom/Form/ChangeFieldType.tpl diff --git a/CRM/Core/BAO/CustomField.php b/CRM/Core/BAO/CustomField.php index c67b60f7a9c5..55e1b786bbc7 100644 --- a/CRM/Core/BAO/CustomField.php +++ b/CRM/Core/BAO/CustomField.php @@ -157,13 +157,13 @@ public static function bulkSave($bulkParams, $defaults = []) { * Fetch object based on array of properties. * * @param array $params - * (reference ) an assoc array of name/value pairs. + * An assoc array of name/value pairs. * @param array $defaults * (reference ) an assoc array to hold the flattened values. * * @return CRM_Core_DAO_CustomField */ - public static function retrieve(&$params, &$defaults) { + public static function retrieve($params, &$defaults) { return CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_CustomField', $params, $defaults); } diff --git a/CRM/Core/DAO/CustomField.php b/CRM/Core/DAO/CustomField.php index 698d34a4956d..5edafdf5c6e1 100644 --- a/CRM/Core/DAO/CustomField.php +++ b/CRM/Core/DAO/CustomField.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/CustomField.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4ded3c0d1a8e34502a5957ee74c4480a) + * (GenCodeChecksum:b74179ea5553c544931562d6aac5641e) */ /** @@ -365,6 +365,7 @@ public static function &fields() { 'localizable' => 0, 'html' => [ 'type' => 'Select', + 'label' => ts("Data Type"), ], 'pseudoconstant' => [ 'callback' => 'CRM_Core_BAO_CustomField::dataType', @@ -384,6 +385,10 @@ public static function &fields() { 'entity' => 'CustomField', 'bao' => 'CRM_Core_BAO_CustomField', 'localizable' => 0, + 'html' => [ + 'type' => 'Select', + 'label' => ts("Field Input Type"), + ], 'pseudoconstant' => [ 'callback' => 'CRM_Core_SelectValues::customHtmlType', ], diff --git a/CRM/Core/SelectValues.php b/CRM/Core/SelectValues.php index 7adcd178c528..b81422698ff8 100644 --- a/CRM/Core/SelectValues.php +++ b/CRM/Core/SelectValues.php @@ -177,7 +177,6 @@ public static function customHtmlType() { 'RichTextEditor' => ts('Rich Text Editor'), 'Autocomplete-Select' => ts('Autocomplete-Select'), 'Link' => ts('Link'), - 'ContactReference' => ts('Autocomplete-Select'), ]; } diff --git a/CRM/Core/xml/Menu/Admin.xml b/CRM/Core/xml/Menu/Admin.xml index df4d108a1d47..245b7ae549bd 100644 --- a/CRM/Core/xml/Menu/Admin.xml +++ b/CRM/Core/xml/Menu/Admin.xml @@ -36,11 +36,6 @@ Custom Field - Move CRM_Custom_Form_MoveField - - civicrm/admin/custom/group/field/changetype - Custom Field - Change Type - CRM_Custom_Form_ChangeFieldType - civicrm/admin/uf/group Profiles diff --git a/CRM/Custom/Form/ChangeFieldType.php b/CRM/Custom/Form/ChangeFieldType.php deleted file mode 100644 index 54f9197ea853..000000000000 --- a/CRM/Custom/Form/ChangeFieldType.php +++ /dev/null @@ -1,309 +0,0 @@ -_id = CRM_Utils_Request::retrieve('id', 'Positive', - $this, TRUE - ); - - $this->_values = []; - $params = ['id' => $this->_id]; - CRM_Core_BAO_CustomField::retrieve($params, $this->_values); - - if ($this->_values['html_type'] == 'Select' && $this->_values['serialize']) { - $this->_values['html_type'] = 'Multi-Select'; - } - $this->_htmlTypeTransitions = self::fieldTypeTransitions(CRM_Utils_Array::value('data_type', $this->_values), - CRM_Utils_Array::value('html_type', $this->_values) - ); - - if (empty($this->_values) || empty($this->_htmlTypeTransitions)) { - CRM_Core_Error::statusBounce(ts("Invalid custom field or can't change input type of this custom field.")); - } - - $url = CRM_Utils_System::url('civicrm/admin/custom/group/field/update', - "action=update&reset=1&gid={$this->_values['custom_group_id']}&id={$this->_id}" - ); - $session = CRM_Core_Session::singleton(); - $session->pushUserContext($url); - - CRM_Utils_System::setTitle(ts('Change Field Type: %1', - [1 => $this->_values['label']] - )); - } - - /** - * Build the form object. - * - * @return void - */ - public function buildQuickForm() { - - $srcHtmlType = $this->add('select', - 'src_html_type', - ts('Current HTML Type'), - [$this->_values['html_type'] => $this->_values['html_type']], - TRUE - ); - - $srcHtmlType->setValue($this->_values['html_type']); - $srcHtmlType->freeze(); - - $this->assign('srcHtmlType', $this->_values['html_type']); - - $dstHtmlType = $this->add('select', - 'dst_html_type', - ts('New HTML Type'), - [ - '' => ts('- select -'), - ] + $this->_htmlTypeTransitions, - TRUE - ); - - $this->addButtons([ - [ - 'type' => 'next', - 'name' => ts('Change Field Type'), - 'isDefault' => TRUE, - 'js' => ['onclick' => 'return checkCustomDataField();'], - ], - [ - 'type' => 'cancel', - 'name' => ts('Cancel'), - ], - ]); - } - - /** - * Process the form when submitted. - * - * @return void - */ - public function postProcess() { - $params = $this->controller->exportValues($this->_name); - - $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', - $this->_values['custom_group_id'], - 'table_name' - ); - - $singleValueOps = [ - 'Text', - 'Select', - 'Radio', - 'Autocomplete-Select', - ]; - - $mutliValueOps = [ - 'CheckBox', - 'Multi-Select', - ]; - - $srcHtmlType = $this->_values['html_type']; - $dstHtmlType = $params['dst_html_type']; - - $customField = new CRM_Core_DAO_CustomField(); - $customField->id = $this->_id; - $customField->find(TRUE); - $customField->serialize = in_array($dstHtmlType, $mutliValueOps, TRUE); - - if ($dstHtmlType == 'Text' && in_array($srcHtmlType, [ - 'Select', - 'Radio', - 'Autocomplete-Select', - ])) { - $customField->option_group_id = 'NULL'; - CRM_Core_BAO_CustomField::checkOptionGroup($this->_values['option_group_id']); - } - - if (in_array($srcHtmlType, $mutliValueOps) && - in_array($dstHtmlType, $singleValueOps)) { - $this->flattenToFirstValue($tableName, $this->_values['column_name']); - } - elseif (in_array($srcHtmlType, $singleValueOps) && - in_array($dstHtmlType, $mutliValueOps)) { - $this->firstValueToFlatten($tableName, $this->_values['column_name']); - } - - $customField->html_type = ($dstHtmlType === 'Multi-Select') ? 'Select' : $dstHtmlType; - $customField->save(); - - // Reset cache for custom fields - Civi::cache('fields')->flush(); - // reset ACL and system caches. - CRM_Core_BAO_Cache::resetCaches(); - - CRM_Core_Session::setStatus(ts('Input type of custom field \'%1\' has been successfully changed to \'%2\'.', - [1 => $this->_values['label'], 2 => $dstHtmlType] - ), ts('Field Type Changed'), 'success'); - } - - /** - * @param $dataType - * @param $htmlType - * - * @return array|null - */ - public static function fieldTypeTransitions($dataType, $htmlType) { - // Text field is single value field, - // can not be change to other single value option which contains option group - if ($htmlType == 'Text') { - return NULL; - } - - $singleValueOps = [ - 'Text' => 'Text', - 'Select' => 'Select', - 'Radio' => 'Radio', - 'Autocomplete-Select' => 'Autocomplete-Select', - ]; - - $mutliValueOps = [ - 'CheckBox' => 'CheckBox', - 'Multi-Select' => 'Multi-Select', - ]; - - switch ($dataType) { - case 'String': - if (in_array($htmlType, array_keys($singleValueOps))) { - unset($singleValueOps[$htmlType]); - return array_merge($singleValueOps, $mutliValueOps); - } - elseif (in_array($htmlType, array_keys($mutliValueOps))) { - unset($singleValueOps['Text']); - foreach ($singleValueOps as $type => $label) { - $singleValueOps[$type] = "{$label} ( " . ts('Not Safe') . " )"; - } - unset($mutliValueOps[$htmlType]); - return array_merge($mutliValueOps, $singleValueOps); - } - break; - - case 'Int': - case 'Float': - case 'Money': - if (in_array($htmlType, array_keys($singleValueOps))) { - unset($singleValueOps[$htmlType]); - return $singleValueOps; - } - break; - - case 'Memo': - $ops = [ - 'TextArea' => 'TextArea', - 'RichTextEditor' => 'RichTextEditor', - ]; - if (in_array($htmlType, array_keys($ops))) { - unset($ops[$htmlType]); - return $ops; - } - break; - } - - return NULL; - } - - /** - * Take a single-value column (eg: a Radio or Select etc ) and convert - * value to the multi listed value (eg:"^Foo^") - * - * @param string $table - * @param string $column - */ - public function firstValueToFlatten($table, $column) { - $selectSql = "SELECT id, $column FROM $table WHERE $column IS NOT NULL"; - $updateSql = "UPDATE $table SET $column = %1 WHERE id = %2"; - $dao = CRM_Core_DAO::executeQuery($selectSql); - while ($dao->fetch()) { - if (!$dao->{$column}) { - continue; - } - $value = CRM_Core_DAO::VALUE_SEPARATOR . $dao->{$column} . CRM_Core_DAO::VALUE_SEPARATOR; - $params = [ - 1 => [(string) $value, 'String'], - 2 => [$dao->id, 'Integer'], - ]; - CRM_Core_DAO::executeQuery($updateSql, $params); - } - } - - /** - * Take a multi-value column (e.g. a Multi-Select or CheckBox column), and convert - * all values (of the form "^^" or "^Foo^" or "^Foo^Bar^") to the first listed value ("Foo") - * - * @param string $table - * @param string $column - */ - public function flattenToFirstValue($table, $column) { - $selectSql = "SELECT id, $column FROM $table WHERE $column IS NOT NULL"; - $updateSql = "UPDATE $table SET $column = %1 WHERE id = %2"; - $dao = CRM_Core_DAO::executeQuery($selectSql); - while ($dao->fetch()) { - $values = self::explode($dao->{$column}); - $params = [ - 1 => [(string) array_shift($values), 'String'], - 2 => [$dao->id, 'Integer'], - ]; - CRM_Core_DAO::executeQuery($updateSql, $params); - } - } - - /** - * @param $str - * - * @return array - */ - public static function explode($str) { - if (empty($str) || $str == CRM_Core_DAO::VALUE_SEPARATOR . CRM_Core_DAO::VALUE_SEPARATOR) { - return []; - } - else { - return explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($str, CRM_Core_DAO::VALUE_SEPARATOR)); - } - } - -} diff --git a/CRM/Custom/Form/Field.php b/CRM/Custom/Form/Field.php index 2bbc8e0c073d..ba68cc706a72 100644 --- a/CRM/Custom/Form/Field.php +++ b/CRM/Custom/Form/Field.php @@ -39,13 +39,6 @@ class CRM_Custom_Form_Field extends CRM_Core_Form { */ protected $_id; - /** - * The default custom data/input types, when editing the field - * - * @var array - */ - protected $_defaultDataType; - /** * Array of custom field values if update mode. * @var array @@ -57,36 +50,26 @@ class CRM_Custom_Form_Field extends CRM_Core_Form { * * @var array */ - private static $_dataTypeValues = NULL; - private static $_dataTypeKeys = NULL; - - private static $_dataToLabels = NULL; + public static $htmlTypesWithOptions = ['Select', 'Radio', 'CheckBox', 'Autocomplete-Select']; /** - * Used for mapping data types to html type options. + * Maps each data_type to allowed html_type options * - * Each item in this array corresponds to the same index in the dataType array - * @var array + * @var array[] */ public static $_dataToHTML = [ - [ - 'Text' => 'Text', - 'Select' => 'Select', - 'Radio' => 'Radio', - 'CheckBox' => 'CheckBox', - 'Autocomplete-Select' => 'Autocomplete-Select', - ], - ['Text' => 'Text', 'Select' => 'Select', 'Radio' => 'Radio'], - ['Text' => 'Text', 'Select' => 'Select', 'Radio' => 'Radio'], - ['Text' => 'Text', 'Select' => 'Select', 'Radio' => 'Radio'], - ['TextArea' => 'TextArea', 'RichTextEditor' => 'RichTextEditor'], - ['Date' => 'Select Date'], - ['Radio' => 'Radio'], - ['StateProvince' => 'Select'], - ['Country' => 'Select'], - ['File' => 'File'], - ['Link' => 'Link'], - ['ContactReference' => 'Autocomplete-Select'], + 'String' => ['Text', 'Select', 'Radio', 'CheckBox', 'Autocomplete-Select'], + 'Int' => ['Text', 'Select', 'Radio'], + 'Float' => ['Text', 'Select', 'Radio'], + 'Money' => ['Text', 'Select', 'Radio'], + 'Memo' => ['TextArea', 'RichTextEditor'], + 'Date' => ['Select Date'], + 'Boolean' => ['Radio'], + 'StateProvince' => ['Select'], + 'Country' => ['Select'], + 'File' => ['File'], + 'Link' => ['Link'], + 'ContactReference' => ['Autocomplete-Select'], ]; /** @@ -95,19 +78,15 @@ class CRM_Custom_Form_Field extends CRM_Core_Form { * @return void */ public function preProcess() { - if (!(self::$_dataTypeKeys)) { - self::$_dataTypeKeys = array_keys(CRM_Core_BAO_CustomField::dataType()); - self::$_dataTypeValues = array_values(CRM_Core_BAO_CustomField::dataType()); - } - //custom field id $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this); + $this->assign('dataToHTML', self::$_dataToHTML); + $this->_values = []; //get the values form db if update. if ($this->_id) { - $params = ['id' => $this->_id]; - CRM_Core_BAO_CustomField::retrieve($params, $this->_values); + CRM_Core_BAO_CustomField::retrieve(['id' => $this->_id], $this->_values); // note_length is an alias for the text_length field $this->_values['note_length'] = $this->_values['text_length'] ?? NULL; // custom group id @@ -130,41 +109,6 @@ public function preProcess() { $session = CRM_Core_Session::singleton(); $session->pushUserContext($url); } - - if (self::$_dataToLabels == NULL) { - self::$_dataToLabels = [ - [ - 'Text' => ts('Text'), - 'Select' => ts('Select'), - 'Radio' => ts('Radio'), - 'CheckBox' => ts('CheckBox'), - 'Autocomplete-Select' => ts('Autocomplete-Select'), - ], - [ - 'Text' => ts('Text'), - 'Select' => ts('Select'), - 'Radio' => ts('Radio'), - ], - [ - 'Text' => ts('Text'), - 'Select' => ts('Select'), - 'Radio' => ts('Radio'), - ], - [ - 'Text' => ts('Text'), - 'Select' => ts('Select'), - 'Radio' => ts('Radio'), - ], - ['TextArea' => ts('TextArea'), 'RichTextEditor' => ts('Rich Text Editor')], - ['Date' => ts('Select Date')], - ['Radio' => ts('Radio')], - ['Select' => ts('Select')], - ['Select' => ts('Select')], - ['File' => ts('Select File')], - ['Link' => ts('Link')], - ['ContactReference' => ts('Autocomplete-Select')], - ]; - } } /** @@ -177,19 +121,12 @@ public function preProcess() { public function setDefaultValues() { $defaults = $this->_values; + // Defaults for update mode if ($this->_id) { $this->assign('id', $this->_id); $this->_gid = $defaults['custom_group_id']; $defaultValue = $defaults['default_value'] ?? NULL; - //get the value for state or country - if ($defaults['data_type'] == 'StateProvince' && $defaultValue) { - $defaults['default_value'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_StateProvince', $defaultValue); - } - elseif ($defaults['data_type'] == 'Country' && $defaultValue) { - $defaults['default_value'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Country', $defaultValue); - } - if ($defaults['data_type'] == 'ContactReference' && !empty($defaults['filter'])) { $contactRefFilter = 'Advance'; if (strpos($defaults['filter'], 'action=lookup') !== FALSE && @@ -215,51 +152,32 @@ public function setDefaultValues() { $defaults['filter_selected'] = $contactRefFilter; } - if (!empty($defaults['data_type'])) { - $defaultDataType = array_search($defaults['data_type'], - self::$_dataTypeKeys - ); - $defaultHTMLType = array_search($defaults['html_type'], - self::$_dataToHTML[$defaultDataType] - ); - $defaults['data_type'] = [ - '0' => $defaultDataType, - '1' => $defaultHTMLType, - ]; - $this->_defaultDataType = $defaults['data_type']; - } - $defaults['option_type'] = 2; - - $this->assign('changeFieldType', CRM_Custom_Form_ChangeFieldType::fieldTypeTransitions($this->_values['data_type'], $this->_values['html_type'])); } - else { + + // Defaults for create mode + if ($this->_action & CRM_Core_Action::ADD) { + $defaults['data_type'] = 'String'; + $defaults['html_type'] = 'Text'; $defaults['is_active'] = 1; $defaults['option_type'] = 1; $defaults['is_search_range'] = 1; - } - - // set defaults for weight. - for ($i = 1; $i <= self::NUM_OPTION; $i++) { - $defaults['option_status[' . $i . ']'] = 1; - $defaults['option_weight[' . $i . ']'] = $i; - $defaults['option_value[' . $i . ']'] = $i; - } - - if ($this->_action & CRM_Core_Action::ADD) { - $fieldValues = ['custom_group_id' => $this->_gid]; - $defaults['weight'] = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_CustomField', $fieldValues); - + $defaults['weight'] = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_CustomField', ['custom_group_id' => $this->_gid]); $defaults['text_length'] = 255; $defaults['note_columns'] = 60; $defaults['note_rows'] = 4; $defaults['is_view'] = 0; + + if (CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_gid, 'is_multiple')) { + $defaults['in_selector'] = 1; + } } - if ($this->_action & CRM_Core_Action::ADD && - CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_gid, 'is_multiple', 'id') - ) { - $defaults['in_selector'] = 1; + // Set defaults for option values. + for ($i = 1; $i <= self::NUM_OPTION; $i++) { + $defaults['option_status[' . $i . ']'] = 1; + $defaults['option_weight[' . $i . ']'] = $i; + $defaults['option_value[' . $i . ']'] = $i; } return $defaults; @@ -277,8 +195,6 @@ public function buildQuickForm() { $this->assign('gid', $this->_gid); } - $this->assign('dataTypeKeys', self::$_dataTypeKeys); - // lets trim all the whitespace $this->applyFilter('__ALL__', 'trim'); @@ -292,17 +208,11 @@ public function buildQuickForm() { TRUE ); - $dt = &self::$_dataTypeValues; - $it = []; - foreach ($dt as $key => $value) { - $it[$key] = self::$_dataToLabels[$key]; - } - $sel = &$this->addElement('hierselect', - 'data_type', - ts('Data and Input Field Type'), - '   ' - ); - $sel->setOptions([$dt, $it]); + // FIXME: Switch addField to use APIv4 so we don't get those legacy options from v3 + $htmlOptions = CRM_Core_BAO_CustomField::buildOptions('html_type', 'create'); + + $this->addField('data_type', ['class' => 'twenty'], TRUE); + $this->addField('html_type', ['class' => 'twenty', 'options' => $htmlOptions], TRUE); if (CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_gid, 'is_multiple')) { $this->add('checkbox', 'in_selector', ts('Display in Table?')); @@ -320,12 +230,17 @@ public function buildQuickForm() { if ($this->_action == CRM_Core_Action::UPDATE) { $this->freeze('data_type'); if (!empty($this->_values['option_group_id'])) { - $this->assign('hasOptionGroup', TRUE); + $this->assign('hasOptionGroup', in_array($this->_values['html_type'], self::$htmlTypesWithOptions)); // Before dev/core#155 we didn't set the is_reserved flag properly, which should be handled by the upgrade script... // but it is still possible that existing installs may have optiongroups linked to custom fields that are marked reserved. $optionGroupParams['id'] = $this->_values['option_group_id']; $optionGroupParams['options']['or'] = [["is_reserved", "id"]]; } + $this->assign('originalHtmlType', $this->_values['html_type']); + $this->assign('originalSerialize', $this->_values['serialize']); + if (!empty($this->_values['serialize'])) { + $this->assign('existingMultiValueCount', $this->getMultiValueCount()); + } } // Retrieve optiongroups for selection list @@ -531,8 +446,7 @@ public function buildQuickForm() { // is searchable by range? $this->addRadio('is_search_range', ts('Search by Range?'), [ts('No'), ts('Yes')]); - // add buttons - $this->addButtons([ + $buttons = [ [ 'type' => 'done', 'name' => ts('Save'), @@ -547,7 +461,14 @@ public function buildQuickForm() { 'type' => 'cancel', 'name' => ts('Cancel'), ], - ]); + ]; + // Save & new only applies to adding a field + if ($this->_id) { + unset($buttons[1]); + } + + // add buttons + $this->addButtons($buttons); // add a form rule to check default value $this->addFormRule(['CRM_Custom_Form_Field', 'formRule'], $this); @@ -617,11 +538,7 @@ public static function formRule($fields, $files, $self) { $errors['label'] = ts("You cannot use 'id' as a field label."); } - if (!isset($fields['data_type'][0]) || !isset($fields['data_type'][1])) { - $errors['_qf_default'] = ts('Please enter valid - Data and Input Field Type.'); - } - - $dataType = self::$_dataTypeKeys[$fields['data_type'][0]]; + $dataType = $fields['data_type']; if ($default || $dataType == 'ContactReference') { switch ($dataType) { @@ -663,8 +580,8 @@ public static function formRule($fields, $files, $self) { case 'Country': if (!empty($default)) { - $query = "SELECT count(*) FROM civicrm_country WHERE name = %1 OR iso_code = %1"; - $params = [1 => [$fields['default_value'], 'String']]; + $query = "SELECT count(*) FROM civicrm_country WHERE id = %1"; + $params = [1 => [$fields['default_value'], 'Int']]; if (CRM_Core_DAO::singleValueQuery($query, $params) <= 0) { $errors['default_value'] = ts('Invalid default value for country.'); } @@ -676,9 +593,8 @@ public static function formRule($fields, $files, $self) { $query = " SELECT count(*) FROM civicrm_state_province - WHERE name = %1 - OR abbreviation = %1"; - $params = [1 => [$fields['default_value'], 'String']]; + WHERE id = %1"; + $params = [1 => [$fields['default_value'], 'Int']]; if (CRM_Core_DAO::singleValueQuery($query, $params) <= 0) { $errors['default_value'] = ts('The invalid default value for State/Province data type'); } @@ -699,7 +615,7 @@ public static function formRule($fields, $files, $self) { } } - if (self::$_dataTypeKeys[$fields['data_type'][0]] == 'Date') { + if ($dataType == 'Date') { if (!$fields['date_format']) { $errors['date_format'] = ts('Please select a date format.'); } @@ -711,11 +627,7 @@ public static function formRule($fields, $files, $self) { */ $_flagOption = $_rowError = 0; $_showHide = new CRM_Core_ShowHideBlocks('', ''); - $dataType = self::$_dataTypeKeys[$fields['data_type'][0]]; - if (isset($fields['data_type'][1])) { - $dataField = $fields['data_type'][1]; - } - $optionFields = ['Select', 'CheckBox', 'Radio']; + $htmlType = $fields['html_type']; if (isset($fields['option_type']) && $fields['option_type'] == 1) { //capture duplicate Custom option values @@ -827,8 +739,7 @@ public static function formRule($fields, $files, $self) { $_flagOption = $_emptyRow = 0; } } - elseif (isset($dataField) && - in_array($dataField, $optionFields) && + elseif (in_array($htmlType, self::$htmlTypesWithOptions) && !in_array($dataType, ['Boolean', 'Country', 'StateProvince']) ) { if (!$fields['option_group_id']) { @@ -841,10 +752,7 @@ public static function formRule($fields, $files, $self) { WHERE data_type != %1 AND option_group_id = %2"; $params = [ - 1 => [ - self::$_dataTypeKeys[$fields['data_type'][0]], - 'String', - ], + 1 => [$dataType, 'String'], 2 => [$fields['option_group_id'], 'Integer'], ]; $count = CRM_Core_DAO::singleValueQuery($query, $params); @@ -860,18 +768,10 @@ public static function formRule($fields, $files, $self) { $assignError->assign('optionRowError', $_rowError); } else { - if (isset($fields['data_type'][1])) { - switch (self::$_dataToHTML[$fields['data_type'][0]][$fields['data_type'][1]]) { + if (isset($htmlType)) { + switch ($htmlType) { case 'Radio': - $_fieldError = 1; - $assignError->assign('fieldError', $_fieldError); - break; - - case 'Checkbox': - $_fieldError = 1; - $assignError->assign('fieldError', $_fieldError); - break; - + case 'CheckBox': case 'Select': $_fieldError = 1; $assignError->assign('fieldError', $_fieldError); @@ -900,6 +800,27 @@ public static function formRule($fields, $files, $self) { $errors['is_view'] = ts('Can not set this field Required and View Only at the same time.'); } + // If switching to a new option list, validate existing data + if (empty($errors) && $self->_id && in_array($htmlType, self::$htmlTypesWithOptions)) { + $oldHtmlType = $self->_values['html_type']; + $oldOptionGroup = $self->_values['option_group_id']; + if ($oldHtmlType === 'Text' || $oldOptionGroup != $fields['option_group_id'] || $fields['option_type'] == 1) { + if ($fields['option_type'] == 2) { + $optionQuery = "SELECT value FROM civicrm_option_value WHERE option_group_id = " . (int) $fields['option_group_id']; + } + else { + $options = array_map(['CRM_Core_DAO', 'escapeString'], array_filter($fields['option_value'], 'strlen')); + $optionQuery = '"' . implode('","', $options) . '"'; + } + $table = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $self->_gid, 'table_name'); + $column = $self->_values['column_name']; + $invalid = CRM_Core_DAO::singleValueQuery("SELECT COUNT(*) FROM `$table` WHERE `$column` NOT IN ($optionQuery)"); + if ($invalid) { + $errors['html_type'] = ts('Cannot impose option list because there is existing data which does not match the options.'); + } + } + } + return empty($errors) ? TRUE : $errors; } @@ -912,24 +833,9 @@ public function postProcess() { // store the submitted values in an array $params = $this->controller->exportValues($this->_name); self::clearEmptyOptions($params); - if ($this->_action == CRM_Core_Action::UPDATE) { - $dataTypeKey = $this->_defaultDataType[0]; - $params['data_type'] = self::$_dataTypeKeys[$this->_defaultDataType[0]]; - $params['html_type'] = self::$_dataToHTML[$this->_defaultDataType[0]][$this->_defaultDataType[1]]; - } - else { - $dataTypeKey = $params['data_type'][0]; - $params['html_type'] = self::$_dataToHTML[$params['data_type'][0]][$params['data_type'][1]]; - $params['data_type'] = self::$_dataTypeKeys[$params['data_type'][0]]; - } //fix for 'is_search_range' field. - if (in_array($dataTypeKey, [ - 1, - 2, - 3, - 5, - ])) { + if (in_array($params['data_type'], ['Int', 'Float', 'Money', 'Date'])) { if (empty($params['is_searchable'])) { $params['is_search_range'] = 0; } @@ -938,11 +844,7 @@ public function postProcess() { $params['is_search_range'] = 0; } - // Serialization cannot be changed on update - if ($this->_id) { - unset($params['serialize']); - } - elseif (strpos($params['html_type'], 'Select') === 0) { + if ($params['html_type'] === 'Select') { $params['serialize'] = $params['serialize'] ? CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND : 'null'; } else { @@ -950,12 +852,11 @@ public function postProcess() { } $filter = 'null'; - if ($dataTypeKey == 11 && !empty($params['filter_selected'])) { + if ($params['data_type'] == 'ContactReference' && !empty($params['filter_selected'])) { if ($params['filter_selected'] == 'Advance' && trim(CRM_Utils_Array::value('filter', $params))) { $filter = trim($params['filter']); } elseif ($params['filter_selected'] == 'Group' && !empty($params['group_id'])) { - $filter = 'action=lookup&group=' . implode(',', $params['group_id']); } } @@ -971,43 +872,6 @@ public function postProcess() { $params['weight'] = CRM_Utils_Weight::updateOtherWeights('CRM_Core_DAO_CustomField', $oldWeight, $params['weight'], $fieldValues); } - $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; - - //store the primary key for State/Province or Country as default value. - if (strlen(trim($params['default_value']))) { - switch ($params['data_type']) { - case 'StateProvince': - $fieldStateProvince = $strtolower($params['default_value']); - - // LOWER in query below roughly translates to 'hurt my database without deriving any benefit' See CRM-19811. - $query = " -SELECT id - FROM civicrm_state_province - WHERE LOWER(name) = '$fieldStateProvince' - OR abbreviation = '$fieldStateProvince'"; - $dao = CRM_Core_DAO::executeQuery($query); - if ($dao->fetch()) { - $params['default_value'] = $dao->id; - } - break; - - case 'Country': - $fieldCountry = $strtolower($params['default_value']); - - // LOWER in query below roughly translates to 'hurt my database without deriving any benefit' See CRM-19811. - $query = " -SELECT id - FROM civicrm_country - WHERE LOWER(name) = '$fieldCountry' - OR iso_code = '$fieldCountry'"; - $dao = CRM_Core_DAO::executeQuery($query); - if ($dao->fetch()) { - $params['default_value'] = $dao->id; - } - break; - } - } - // The text_length attribute for Memo fields is in a different input as there // are different label, help text and default value than for other type fields if ($params['data_type'] == "Memo") { @@ -1062,4 +926,32 @@ public static function clearEmptyOptions(&$fields) { } } + /** + * Get number of existing records for this field that contain more than one serialized value. + * + * @return int + * @throws CRM_Core_Exception + */ + public function getMultiValueCount() { + $table = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_gid, 'table_name'); + $column = $this->_values['column_name']; + $sp = CRM_Core_DAO::VALUE_SEPARATOR; + $sql = "SELECT COUNT(*) FROM `$table` WHERE `$column` LIKE '{$sp}%{$sp}%{$sp}'"; + return (int) CRM_Core_DAO::singleValueQuery($sql); + } + + /** + * @return string + */ + public function getDefaultContext() { + return 'create'; + } + + /** + * @return string + */ + public function getDefaultEntity() { + return 'CustomField'; + } + } diff --git a/js/Common.js b/js/Common.js index f42314f6edb8..881dda124d0a 100644 --- a/js/Common.js +++ b/js/Common.js @@ -289,6 +289,17 @@ if (!CRM.vars) CRM.vars = {}; return rendered; }; + CRM.utils.getOptions = function(select) { + var options = []; + $('option', select).each(function() { + var option = {key: $(this).attr('value'), value: $(this).text()}; + if (option.key !== '') { + options.push(option); + } + }); + return options; + }; + function chainSelect() { var $form = $(this).closest('form'), $target = $('select[data-name="' + $(this).data('target') + '"]', $form), diff --git a/templates/CRM/Custom/Form/ChangeFieldType.tpl b/templates/CRM/Custom/Form/ChangeFieldType.tpl deleted file mode 100644 index e87067ea8447..000000000000 --- a/templates/CRM/Custom/Form/ChangeFieldType.tpl +++ /dev/null @@ -1,46 +0,0 @@ -{* - +--------------------------------------------------------------------+ - | 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 | - +--------------------------------------------------------------------+ -*} -
-
{include file="CRM/common/formButtons.tpl" location="top"}
-
{icon icon="fa-info-circle"}{/icon} -  {ts}Warning: This functionality is currently in beta stage. Consider backing up your database before using it. Click "Cancel" to return to the "edit custom field" form without making changes.{/ts} -
- - - - - - - - - -
{$form.src_html_type.label}{$form.src_html_type.html}
{$form.dst_html_type.label}{$form.dst_html_type.html}
-
{include file="CRM/common/formButtons.tpl" location="bottom"}
-
-{literal} - -{/literal} - diff --git a/templates/CRM/Custom/Form/Field.tpl b/templates/CRM/Custom/Form/Field.tpl index 5dd62613b9ca..aeb56d695bc6 100644 --- a/templates/CRM/Custom/Form/Field.tpl +++ b/templates/CRM/Custom/Form/Field.tpl @@ -20,29 +20,16 @@ {$form.data_type.label} - {$form.data_type.html} - {if $action neq 1 && $form.data_type.value[1][0] eq "Select" && $form.serialize.value} - ({ts}Multi-Select{/ts}) - {/if} - {if $action neq 4 and $action neq 2} -
{ts}Select the type of data you want to collect and store for this contact. Then select from the available HTML input field types (choices are based on the type of data being collected).{/ts} - {/if} - {if $action eq 2 and $changeFieldType} -
- - - {ts}Change Input Field Type{/ts} - -
- {/if} - + {$form.data_type.html} + + + {$form.html_type.label} + {$form.html_type.html} + + + {$form.serialize.label} + {$form.serialize.html} - {if $action eq 1} - - {$form.serialize.label} - {$form.serialize.html} - - {/if} {if $form.in_selector} {$form.in_selector.label} @@ -176,19 +163,45 @@ {literal} {/literal} diff --git a/tests/phpunit/api/v3/CustomFieldTest.php b/tests/phpunit/api/v3/CustomFieldTest.php index e5c900dd245f..cfa7894fe48d 100644 --- a/tests/phpunit/api/v3/CustomFieldTest.php +++ b/tests/phpunit/api/v3/CustomFieldTest.php @@ -108,22 +108,13 @@ public function testCustomFieldCreateAllAvailableFormInputs() { $htype = CRM_Custom_Form_Field::$_dataToHTML; // Legacy html types returned by v3 - foreach ($htype as &$item) { - if (isset($item['StateProvince'])) { - $item['StateProvince'] = 'Select State/Province'; - } - if (isset($item['Country'])) { - $item['Country'] = 'Select Country'; - } - } + $htype['StateProvince'] = ['Select State/Province']; + $htype['Country'] = ['Select Country']; - $n = 0; foreach ($dtype as $dkey => $dvalue) { - foreach ($htype[$n] as $hkey => $hvalue) { - //echo $dkey."][".$hvalue."\n"; + foreach ($htype[$dkey] as $hvalue) { $this->_loopingCustomFieldCreateTest($this->_buildParams($gid['id'], $hvalue, $dkey)); } - $n++; } } diff --git a/tests/phpunit/api/v3/CustomValueTest.php b/tests/phpunit/api/v3/CustomValueTest.php index 02c16401c41c..96ce2e42c0c6 100644 --- a/tests/phpunit/api/v3/CustomValueTest.php +++ b/tests/phpunit/api/v3/CustomValueTest.php @@ -95,11 +95,11 @@ public function testCreateCustomValue() { $customFieldDataType = CRM_Core_BAO_CustomField::dataType(); $dataToHtmlTypes = CRM_Custom_Form_Field::$_dataToHTML; - $count = 0; - $optionSupportingHTMLTypes = ['Select', 'Radio', 'CheckBox', 'Autocomplete-Select', 'Multi-Select']; + $optionSupportingHTMLTypes = CRM_Custom_Form_Field::$htmlTypesWithOptions; foreach ($customFieldDataType as $dataType => $label) { switch ($dataType) { + // skipping File data-type & state province due to caching issues // case 'Country': // case 'StateProvince': case 'String': @@ -139,7 +139,7 @@ public function testCreateCustomValue() { } //Create custom field of $dataType and html-type $html - foreach ($dataToHtmlTypes[$count] as $html) { + foreach ($dataToHtmlTypes[$dataType] as $html) { // per CRM-18568 the like operator does not currently work for fields with options. // the LIKE operator could potentially bypass ACLs (as could IS NOT NULL) and some thought needs to be given // to it. @@ -160,13 +160,7 @@ public function testCreateCustomValue() { //Now test with $validSQLOperator SQL operators against its custom value(s) $this->_testCustomValue($customField['values'][$customField['id']], $validSQLOperators, $type); } - $count++; - break; - default: - // skipping File data-type & state province due to caching issues - $count++; - break; } } } diff --git a/xml/schema/Core/CustomField.xml b/xml/schema/Core/CustomField.xml index 44c632256010..54cd5c5d7454 100644 --- a/xml/schema/Core/CustomField.xml +++ b/xml/schema/Core/CustomField.xml @@ -73,6 +73,7 @@ 1.1 Select + @@ -85,6 +86,10 @@ CRM_Core_SelectValues::customHtmlType + + Select + + 1.1 From 5f0a0a71ae47507556fe735f13c9a37b270a20db Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Thu, 10 Sep 2020 11:39:25 -0400 Subject: [PATCH 228/834] [REF] Simplify array construction In trying to interpret https://github.com/civicrm/civicrm-core/pull/17992 I realised that the code copies values from the dao to an array, copies the array to another array, unsets most of the variables and then uses that second array. This simplifies. The easiest way to work through this code is in a debugger running testProcessMembershipUpdateStatus --- CRM/Member/BAO/Membership.php | 42 +++++++---------------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/CRM/Member/BAO/Membership.php b/CRM/Member/BAO/Membership.php index 5260b31c2722..3a98f1d58776 100644 --- a/CRM/Member/BAO/Membership.php +++ b/CRM/Member/BAO/Membership.php @@ -2284,21 +2284,6 @@ public static function updateAllMembershipStatus($params = []) { while ($dao2->fetch()) { $processCount++; - // Put common parameters into array for easy access - $memberParams = [ - 'id' => $dao2->membership_id, - 'status_id' => $dao2->status_id, - 'contact_id' => $dao2->contact_id, - 'membership_type_id' => $dao2->membership_type_id, - 'membership_type' => $allMembershipTypes[$dao2->membership_type_id]['name'], - 'join_date' => $dao2->join_date, - 'start_date' => $dao2->start_date, - 'end_date' => $dao2->end_date, - 'source' => $dao2->source, - 'skipStatusCal' => TRUE, - 'skipRecentView' => TRUE, - ]; - // CRM-7248: added excludeIsAdmin param to the following fn call to prevent moving to admin statuses //get the membership status as per id. $newStatus = civicrm_api3('membership_status', 'calc', @@ -2313,27 +2298,16 @@ public static function updateAllMembershipStatus($params = []) { if ($statusId && $statusId != $dao2->status_id ) { - //take all params that need to save. - $memParams = $memberParams; - $memParams['status_id'] = $statusId; - $memParams['createActivity'] = TRUE; - - // Unset columns which should remain unchanged from their current saved - // values. This avoids race condition in which these values may have - // been changed by other processes. - unset( - $memParams['contact_id'], - $memParams['membership_type_id'], - $memParams['membership_type'], - $memParams['join_date'], - $memParams['start_date'], - $memParams['end_date'], - $memParams['source'] - ); - //since there is change in status. + $memberParams = [ + 'id' => $dao2->membership_id, + 'skipStatusCal' => TRUE, + 'skipRecentView' => TRUE, + 'status_id' => $statusId, + 'createActivity' => TRUE, + ]; //process member record. - civicrm_api3('membership', 'create', $memParams); + civicrm_api3('membership', 'create', $memberParams); $updateCount++; } } From 6fa2a892457cab50a0f68b7876907dace806b975 Mon Sep 17 00:00:00 2001 From: eileen Date: Sat, 12 Sep 2020 08:43:55 +1200 Subject: [PATCH 229/834] Remove unused property, null constructor --- CRM/Member/BAO/MembershipStatus.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/CRM/Member/BAO/MembershipStatus.php b/CRM/Member/BAO/MembershipStatus.php index 6dd327e5b076..535f51ed9560 100644 --- a/CRM/Member/BAO/MembershipStatus.php +++ b/CRM/Member/BAO/MembershipStatus.php @@ -16,19 +16,6 @@ */ class CRM_Member_BAO_MembershipStatus extends CRM_Member_DAO_MembershipStatus { - /** - * Static holder for the default LT. - * @var int - */ - public static $_defaultMembershipStatus = NULL; - - /** - * Class constructor. - */ - public function __construct() { - parent::__construct(); - } - /** * Fetch object based on array of properties. * From e021e985fe46d5141c418c5ca9225aa37398e31a Mon Sep 17 00:00:00 2001 From: eileen Date: Sat, 12 Sep 2020 10:02:12 +1200 Subject: [PATCH 230/834] [REF] minor tidy up on membershipStatus::create & add We have been deprecating & removing the array from BAO create & add actions - this continues that (long slow) process' --- CRM/Member/BAO/MembershipStatus.php | 23 +++---- .../CRM/Member/BAO/MembershipStatusTest.php | 69 ++++++++----------- 2 files changed, 40 insertions(+), 52 deletions(-) diff --git a/CRM/Member/BAO/MembershipStatus.php b/CRM/Member/BAO/MembershipStatus.php index 6dd327e5b076..de34eec19801 100644 --- a/CRM/Member/BAO/MembershipStatus.php +++ b/CRM/Member/BAO/MembershipStatus.php @@ -68,26 +68,21 @@ public static function setIsActive($id, $is_active) { * Takes an associative array and creates a membership Status object. * * @param array $params - * (reference ) an assoc array of name/value pairs. + * Array of name/value pairs. * - * @throws Exception - * @return CRM_Member_BAO_MembershipStatus + * @throws CRM_Core_Exception + * @return CRM_Member_DAO_MembershipStatus */ public static function create($params) { - $ids = []; - if (!empty($params['id'])) { - $ids['membershipStatus'] = $params['id']; - } - else { + if (empty($params['id'])) { //don't allow duplicate names - if id not set $status = new CRM_Member_DAO_MembershipStatus(); $status->name = $params['name']; if ($status->find(TRUE)) { - throw new Exception('A membership status with this name already exists.'); + throw new CRM_Core_Exception('A membership status with this name already exists.'); } } - $membershipStatusBAO = CRM_Member_BAO_MembershipStatus::add($params, $ids); - return $membershipStatusBAO; + return self::add($params); } /** @@ -98,10 +93,12 @@ public static function create($params) { * @param array $ids * Array contains the id - this param is deprecated. * - * - * @return object + * @return CRM_Member_DAO_MembershipStatus */ public static function add(&$params, $ids = []) { + if (!empty($ids)) { + CRM_Core_Error::deprecatedFunctionWarning('ids is a deprecated parameter'); + } $id = $params['id'] ?? $ids['membershipStatus'] ?? NULL; if (!$id) { CRM_Core_DAO::setCreateDefaults($params, self::getDefaults()); diff --git a/tests/phpunit/CRM/Member/BAO/MembershipStatusTest.php b/tests/phpunit/CRM/Member/BAO/MembershipStatusTest.php index e33f916c9aa6..f413ffd22b7c 100644 --- a/tests/phpunit/CRM/Member/BAO/MembershipStatusTest.php +++ b/tests/phpunit/CRM/Member/BAO/MembershipStatusTest.php @@ -15,8 +15,13 @@ */ class CRM_Member_BAO_MembershipStatusTest extends CiviUnitTestCase { - public function setUp() { - parent::setUp(); + protected function tearDown() { + foreach ($this->ids as $entity => $ids) { + foreach ($ids as $id) { + $this->callAPISuccess($entity, 'Delete', ['id' => $id]); + } + } + parent::tearDown(); } /** @@ -24,33 +29,27 @@ public function setUp() { */ public function testAdd() { $params = [ - 'name' => 'pending', + 'name' => 'added', 'is_active' => 1, ]; - $membershipStatus = CRM_Member_BAO_MembershipStatus::add($params); + $this->ids['MembershipStatus'][0] = $this->callAPISuccess('MembershipStatus', 'create', $params)['id']; - $result = $this->assertDBNotNull('CRM_Member_BAO_MembershipStatus', $membershipStatus->id, + $result = $this->assertDBNotNull('CRM_Member_BAO_MembershipStatus', $this->ids['MembershipStatus'][0], 'name', 'id', 'Database check on updated membership status record.' ); - $this->assertEquals($result, 'pending', 'Verify membership status is_active.'); - - $this->callAPISuccess('MembershipStatus', 'Delete', ['id' => $membershipStatus->id]); + $this->assertEquals($result, 'added', 'Verify membership status is_active.'); } public function testRetrieve() { - $params = [ - 'name' => 'testStatus', - 'is_active' => 1, - ]; + $params = ['name' => 'testStatus', 'is_active' => 1]; - $membershipStatus = CRM_Member_BAO_MembershipStatus::add($params); + $this->ids['MembershipStatus'][0] = $this->callAPISuccess('MembershipStatus', 'create', $params)['id']; $defaults = []; $result = CRM_Member_BAO_MembershipStatus::retrieve($params, $defaults); $this->assertEquals($result->name, 'testStatus', 'Verify membership status name.'); - CRM_Member_BAO_MembershipStatus::del($membershipStatus->id); } public function testPseudoConstantflush() { @@ -58,66 +57,58 @@ public function testPseudoConstantflush() { 'name' => 'testStatus', 'is_active' => 1, ]; - $membershipStatus = CRM_Member_BAO_MembershipStatus::add($params); + $this->ids['MembershipStatus'][0] = $this->callAPISuccess('MembershipStatus', 'create', $params)['id']; $defaults = []; $result = CRM_Member_BAO_MembershipStatus::retrieve($params, $defaults); $this->assertEquals($result->name, 'testStatus', 'Verify membership status name.'); $updateParams = [ - 'id' => $membershipStatus->id, + 'id' => $this->ids['MembershipStatus'][0], 'name' => 'testStatus', 'label' => 'Changed Status', 'is_active' => 1, ]; - $membershipStatus2 = CRM_Member_BAO_MembershipStatus::add($updateParams); - $result = CRM_Member_PseudoConstant::membershipStatus($membershipStatus->id, NULL, 'label', FALSE, FALSE); + $this->callAPISuccess('MembershipStatus', 'create', $updateParams)['id']; + $result = CRM_Member_PseudoConstant::membershipStatus($this->ids['MembershipStatus'][0], NULL, 'label', FALSE, FALSE); $this->assertEquals($result, 'Changed Status', 'Verify updated membership status label From PseudoConstant.'); - CRM_Member_BAO_MembershipStatus::del($membershipStatus->id); } public function testSetIsActive() { $params = [ - 'name' => 'pending', + 'name' => 'added', 'is_active' => 1, ]; - $membershipStatus = CRM_Member_BAO_MembershipStatus::add($params); - $result = CRM_Member_BAO_MembershipStatus::setIsActive($membershipStatus->id, 0); - $this->assertEquals($result, TRUE, 'Verify membership status record updation.'); + $this->ids['MembershipStatus'][0] = $this->callAPISuccess('MembershipStatus', 'create', $params)['id']; + $result = CRM_Member_BAO_MembershipStatus::setIsActive($this->ids['MembershipStatus'][0], 0); + $this->assertEquals($result, TRUE, 'Verify membership status record updated.'); - $isActive = $this->assertDBNotNull('CRM_Member_BAO_MembershipStatus', $membershipStatus->id, + $isActive = $this->assertDBNotNull('CRM_Member_BAO_MembershipStatus', $this->ids['MembershipStatus'][0], 'is_active', 'id', 'Database check on updated membership status record.' ); $this->assertEquals($isActive, 0, 'Verify membership status is_active.'); - - $this->callAPISuccess('MembershipStatus', 'Delete', ['id' => $membershipStatus->id]); } public function testGetMembershipStatus() { $params = [ - 'name' => 'pending', + 'name' => 'added', 'is_active' => 1, ]; - $membershipStatus = CRM_Member_BAO_MembershipStatus::add($params); - $result = CRM_Member_BAO_MembershipStatus::getMembershipStatus($membershipStatus->id); - $this->assertEquals($result['name'], 'pending', 'Verify membership status name.'); - - $this->callAPISuccess('MembershipStatus', 'Delete', ['id' => $membershipStatus->id]); + $this->ids['MembershipStatus'][0] = $this->callAPISuccess('MembershipStatus', 'create', $params)['id']; + $result = CRM_Member_BAO_MembershipStatus::getMembershipStatus($this->ids['MembershipStatus'][0]); + $this->assertEquals($result['name'], 'added', 'Verify membership status name.'); } public function testDel() { - $params = [ - 'name' => 'testStatus', - 'is_active' => 1, - ]; + $params = ['name' => 'testStatus', 'is_active' => 1]; - $membershipStatus = CRM_Member_BAO_MembershipStatus::add($params); - CRM_Member_BAO_MembershipStatus::del($membershipStatus->id); + $membershipID = $this->callAPISuccess('MembershipStatus', 'create', $params)['id']; + CRM_Member_BAO_MembershipStatus::del($membershipID); $defaults = []; $result = CRM_Member_BAO_MembershipStatus::retrieve($params, $defaults); - $this->assertEquals(empty($result), TRUE, 'Verify membership status record deletion.'); + $this->assertEquals($result === NULL, TRUE, 'Verify membership status record deletion.'); } /** From 737bd87c577641305476feb17f2e318e78862920 Mon Sep 17 00:00:00 2001 From: eileen Date: Sat, 12 Sep 2020 18:37:51 +1200 Subject: [PATCH 231/834] [REF] Folllow up cleanup - remove now unused param In a recent regression fix we stopped returning from getLine. This fully removes the, now unused, parameter. The functions that call getLine calculate the total now --- CRM/Financial/BAO/Order.php | 2 +- CRM/Price/BAO/PriceSet.php | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/CRM/Financial/BAO/Order.php b/CRM/Financial/BAO/Order.php index 80cf3b66ec58..ffe8af8ff973 100644 --- a/CRM/Financial/BAO/Order.php +++ b/CRM/Financial/BAO/Order.php @@ -238,7 +238,7 @@ protected function calculateLineItems(): array { } $throwAwayArray = []; // @todo - still using getLine for now but better to bring it to this class & do a better job. - $newLines = CRM_Price_BAO_PriceSet::getLine($params, $throwAwayArray, $this->getPriceSetID(), $this->getPriceFieldSpec($fieldID), $fieldID, 0)[1]; + $newLines = CRM_Price_BAO_PriceSet::getLine($params, $throwAwayArray, $this->getPriceSetID(), $this->getPriceFieldSpec($fieldID), $fieldID)[1]; foreach ($newLines as $newLine) { $lineItems[$newLine['price_field_value_id']] = $newLine; } diff --git a/CRM/Price/BAO/PriceSet.php b/CRM/Price/BAO/PriceSet.php index 7481dd09fae0..4bbf042d6c4a 100644 --- a/CRM/Price/BAO/PriceSet.php +++ b/CRM/Price/BAO/PriceSet.php @@ -673,7 +673,7 @@ public static function processAmount($fields, &$params, &$lineItem, $priceSetID continue; } - list($params, $lineItem) = self::getLine($params, $lineItem, $priceSetID, $field, $id, $totalPrice); + list($params, $lineItem) = self::getLine($params, $lineItem, $priceSetID, $field, $id); } $amount_level = []; @@ -1694,11 +1694,10 @@ protected static function reformatUsedByFormsWithEntityData($forms) { * @param int $priceSetID * @param array $field * @param int $id - * @param float $totalPrice * * @return array */ - public static function getLine(&$params, &$lineItem, $priceSetID, $field, $id, $totalPrice): array { + public static function getLine(&$params, &$lineItem, $priceSetID, $field, $id): array { $totalTax = 0; switch ($field['html_type']) { case 'Text': @@ -1718,7 +1717,6 @@ public static function getLine(&$params, &$lineItem, $priceSetID, $field, $id, $ if (!empty($field['options'][$optionValueId]['tax_rate'])) { $lineItem = self::setLineItem($field, $lineItem, $optionValueId, $totalTax); } - $totalPrice += $lineItem[$firstOption['id']]['line_total'] + CRM_Utils_Array::value('tax_amount', $lineItem[key($field['options'])]); break; case 'Radio': @@ -1748,7 +1746,6 @@ public static function getLine(&$params, &$lineItem, $priceSetID, $field, $id, $ $lineItem[$optionValueId]['line_total'] = $lineItem[$optionValueId]['unit_price'] = CRM_Utils_Rule::cleanMoney($lineItem[$optionValueId]['line_total'] - $lineItem[$optionValueId]['tax_amount']); } } - $totalPrice += $lineItem[$optionValueId]['line_total'] + CRM_Utils_Array::value('tax_amount', $lineItem[$optionValueId]); break; case 'Select': @@ -1759,7 +1756,6 @@ public static function getLine(&$params, &$lineItem, $priceSetID, $field, $id, $ if (!empty($field['options'][$optionValueId]['tax_rate'])) { $lineItem = self::setLineItem($field, $lineItem, $optionValueId, $totalTax); } - $totalPrice += $lineItem[$optionValueId]['line_total'] + CRM_Utils_Array::value('tax_amount', $lineItem[$optionValueId]); break; case 'CheckBox': @@ -1769,7 +1765,6 @@ public static function getLine(&$params, &$lineItem, $priceSetID, $field, $id, $ if (!empty($field['options'][$optionId]['tax_rate'])) { $lineItem = self::setLineItem($field, $lineItem, $optionId, $totalTax); } - $totalPrice += $lineItem[$optionId]['line_total'] + CRM_Utils_Array::value('tax_amount', $lineItem[$optionId]); } break; } From d1961207361fb5b4f39e0ad2ee7c290eade50876 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Fri, 11 Sep 2020 21:03:36 -0400 Subject: [PATCH 232/834] APIv4 - Map specific action names to more generic versions --- Civi/Api4/Generic/AbstractAction.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Civi/Api4/Generic/AbstractAction.php b/Civi/Api4/Generic/AbstractAction.php index 8d0111e6b35d..fa995ce0d8d4 100644 --- a/Civi/Api4/Generic/AbstractAction.php +++ b/Civi/Api4/Generic/AbstractAction.php @@ -407,13 +407,15 @@ public function getPermissions() { 'default' => ['administer CiviCRM'], ]; $action = $this->getActionName(); - if (isset($permissions[$action])) { - return $permissions[$action]; - } - elseif (in_array($action, ['getActions', 'getFields'])) { - return $permissions['meta']; - } - return $permissions['default']; + // Map specific action names to more generic versions + $map = [ + 'getActions' => 'meta', + 'getFields' => 'meta', + 'replace' => 'delete', + 'save' => 'create', + ]; + $generic = $map[$action] ?? 'default'; + return $permissions[$action] ?? $permissions[$generic] ?? $permissions['default']; } /** From e5c6b3cd9fe4245e125f2582d435d02712f5d65d Mon Sep 17 00:00:00 2001 From: demeritcowboy Date: Sat, 12 Sep 2020 18:42:49 -0400 Subject: [PATCH 233/834] make service public like in symfony 2.x --- Civi/Core/Container.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index d898c61fde3b..acedf8a78024 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -239,7 +239,7 @@ public function createContainer() { )) ->setFactory([$class, 'singleton'])->setPublic(TRUE); } - $container->setAlias('cache.short', 'cache.default'); + $container->setAlias('cache.short', 'cache.default')->setPublic(TRUE); $container->setDefinition('resources', new Definition( 'CRM_Core_Resources', From e4cba171cbe08291fef5b4a7e02241ca050c57c0 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sun, 13 Sep 2020 09:08:10 -0400 Subject: [PATCH 234/834] Remove unnecessary debug from tests which messes up array output GetValue and debug can't really be used together. If the output is scalar, then there's no place for the debug info to go. If the output is an array of values, the debug info will be injected into the values, messing up the test. --- Civi/Test/Api3TestTrait.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Civi/Test/Api3TestTrait.php b/Civi/Test/Api3TestTrait.php index 2ce8d75d0e58..176564a68663 100644 --- a/Civi/Test/Api3TestTrait.php +++ b/Civi/Test/Api3TestTrait.php @@ -260,7 +260,6 @@ public function callAPISuccessGetSingle($entity, $params, $checkAgainst = NULL) public function callAPISuccessGetValue($entity, $params, $type = NULL) { $params += [ 'version' => $this->_apiversion, - 'debug' => 1, ]; $result = $this->civicrm_api($entity, 'getvalue', $params); if (is_array($result) && (!empty($result['is_error']) || isset($result['values']))) { From 8c9643b3c40a305232c598e87ef3fc0092cb37b8 Mon Sep 17 00:00:00 2001 From: Mathieu Lutfy Date: Sun, 13 Sep 2020 10:38:46 -0400 Subject: [PATCH 235/834] dev/translation#51 Fix inheritLocale regression --- CRM/Core/BAO/ConfigSetting.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CRM/Core/BAO/ConfigSetting.php b/CRM/Core/BAO/ConfigSetting.php index 45e037897490..54ad2e1a74d1 100644 --- a/CRM/Core/BAO/ConfigSetting.php +++ b/CRM/Core/BAO/ConfigSetting.php @@ -178,7 +178,7 @@ public static function applyLocale($settings, $activatedLocales) { * If the language is specified via "lcMessages" we skip this, since the * intention of the URL query var is to override all other sources. */ - if ($settings->get('inheritLocale') && empty($chosenLocale)) { + if ($settings->get('inheritLocale')) { /* * FIXME: On multi-language installs, CRM_Utils_System::getUFLocale() in From 7b66c3b57753e74e46815ce9d9367126a19acee8 Mon Sep 17 00:00:00 2001 From: Andrew Hunt Date: Fri, 11 Sep 2020 16:46:17 -0400 Subject: [PATCH 236/834] DAOs with singular/plural options for entity titles --- CRM/ACL/DAO/ACL.php | 9 ++++++--- CRM/ACL/DAO/ACLCache.php | 9 ++++++--- CRM/ACL/DAO/EntityRole.php | 9 ++++++--- CRM/Activity/DAO/Activity.php | 9 ++++++--- CRM/Activity/DAO/ActivityContact.php | 9 ++++++--- CRM/Batch/DAO/Batch.php | 9 ++++++--- CRM/Batch/DAO/EntityBatch.php | 9 ++++++--- CRM/Campaign/DAO/Campaign.php | 9 ++++++--- CRM/Campaign/DAO/CampaignGroup.php | 9 ++++++--- CRM/Campaign/DAO/Survey.php | 9 ++++++--- CRM/Case/DAO/Case.php | 9 ++++++--- CRM/Case/DAO/CaseActivity.php | 9 ++++++--- CRM/Case/DAO/CaseContact.php | 9 ++++++--- CRM/Case/DAO/CaseType.php | 9 ++++++--- CRM/Contact/DAO/ACLContactCache.php | 9 ++++++--- CRM/Contact/DAO/Contact.php | 9 ++++++--- CRM/Contact/DAO/ContactType.php | 9 ++++++--- CRM/Contact/DAO/DashboardContact.php | 9 ++++++--- CRM/Contact/DAO/Group.php | 9 ++++++--- CRM/Contact/DAO/GroupContact.php | 9 ++++++--- CRM/Contact/DAO/GroupContactCache.php | 9 ++++++--- CRM/Contact/DAO/GroupNesting.php | 9 ++++++--- CRM/Contact/DAO/GroupOrganization.php | 9 ++++++--- CRM/Contact/DAO/Relationship.php | 9 ++++++--- CRM/Contact/DAO/RelationshipCache.php | 9 ++++++--- CRM/Contact/DAO/RelationshipType.php | 9 ++++++--- CRM/Contact/DAO/SavedSearch.php | 9 ++++++--- CRM/Contact/DAO/SubscriptionHistory.php | 9 ++++++--- CRM/Contribute/DAO/Contribution.php | 9 ++++++--- CRM/Contribute/DAO/ContributionPage.php | 9 ++++++--- CRM/Contribute/DAO/ContributionProduct.php | 9 ++++++--- CRM/Contribute/DAO/ContributionRecur.php | 9 ++++++--- CRM/Contribute/DAO/ContributionSoft.php | 9 ++++++--- CRM/Contribute/DAO/Premium.php | 9 ++++++--- CRM/Contribute/DAO/PremiumsProduct.php | 9 ++++++--- CRM/Contribute/DAO/Product.php | 9 ++++++--- CRM/Contribute/DAO/Widget.php | 9 ++++++--- CRM/Core/CodeGen/Specification.php | 15 +++------------ CRM/Core/DAO.php | 3 ++- CRM/Core/DAO/ActionLog.php | 9 ++++++--- CRM/Core/DAO/ActionMapping.php | 9 ++++++--- CRM/Core/DAO/ActionSchedule.php | 9 ++++++--- CRM/Core/DAO/Address.php | 9 ++++++--- CRM/Core/DAO/AddressFormat.php | 9 ++++++--- CRM/Core/DAO/Cache.php | 9 ++++++--- CRM/Core/DAO/Component.php | 9 ++++++--- CRM/Core/DAO/Country.php | 9 ++++++--- CRM/Core/DAO/County.php | 9 ++++++--- CRM/Core/DAO/CustomField.php | 9 ++++++--- CRM/Core/DAO/CustomGroup.php | 9 ++++++--- CRM/Core/DAO/Dashboard.php | 9 ++++++--- CRM/Core/DAO/Discount.php | 9 ++++++--- CRM/Core/DAO/Domain.php | 9 ++++++--- CRM/Core/DAO/Email.php | 9 ++++++--- CRM/Core/DAO/EntityFile.php | 9 ++++++--- CRM/Core/DAO/EntityTag.php | 9 ++++++--- CRM/Core/DAO/Extension.php | 9 ++++++--- CRM/Core/DAO/File.php | 9 ++++++--- CRM/Core/DAO/IM.php | 9 ++++++--- CRM/Core/DAO/Job.php | 9 ++++++--- CRM/Core/DAO/JobLog.php | 9 ++++++--- CRM/Core/DAO/LocBlock.php | 9 ++++++--- CRM/Core/DAO/LocationType.php | 9 ++++++--- CRM/Core/DAO/Log.php | 9 ++++++--- CRM/Core/DAO/MailSettings.php | 9 ++++++--- CRM/Core/DAO/Managed.php | 9 ++++++--- CRM/Core/DAO/Mapping.php | 9 ++++++--- CRM/Core/DAO/MappingField.php | 9 ++++++--- CRM/Core/DAO/Menu.php | 9 ++++++--- CRM/Core/DAO/MessageTemplate.php | 9 ++++++--- CRM/Core/DAO/Navigation.php | 9 ++++++--- CRM/Core/DAO/Note.php | 9 ++++++--- CRM/Core/DAO/OpenID.php | 9 ++++++--- CRM/Core/DAO/OptionGroup.php | 9 ++++++--- CRM/Core/DAO/OptionValue.php | 9 ++++++--- CRM/Core/DAO/Phone.php | 9 ++++++--- CRM/Core/DAO/PreferencesDate.php | 9 ++++++--- CRM/Core/DAO/PrevNextCache.php | 9 ++++++--- CRM/Core/DAO/PrintLabel.php | 9 ++++++--- CRM/Core/DAO/RecurringEntity.php | 9 ++++++--- CRM/Core/DAO/Setting.php | 9 ++++++--- CRM/Core/DAO/StateProvince.php | 9 ++++++--- CRM/Core/DAO/StatusPreference.php | 9 ++++++--- CRM/Core/DAO/SystemLog.php | 9 ++++++--- CRM/Core/DAO/Tag.php | 9 ++++++--- CRM/Core/DAO/Timezone.php | 9 ++++++--- CRM/Core/DAO/UFField.php | 9 ++++++--- CRM/Core/DAO/UFGroup.php | 9 ++++++--- CRM/Core/DAO/UFJoin.php | 9 ++++++--- CRM/Core/DAO/UFMatch.php | 9 ++++++--- CRM/Core/DAO/Website.php | 9 ++++++--- CRM/Core/DAO/WordReplacement.php | 9 ++++++--- CRM/Core/DAO/Worldregion.php | 9 ++++++--- CRM/Cxn/DAO/Cxn.php | 9 ++++++--- CRM/Dedupe/DAO/Exception.php | 9 ++++++--- CRM/Dedupe/DAO/Rule.php | 9 ++++++--- CRM/Dedupe/DAO/RuleGroup.php | 9 ++++++--- CRM/Event/Cart/DAO/Cart.php | 9 ++++++--- CRM/Event/Cart/DAO/EventInCart.php | 9 ++++++--- CRM/Event/DAO/Event.php | 9 ++++++--- CRM/Event/DAO/Participant.php | 9 ++++++--- CRM/Event/DAO/ParticipantPayment.php | 9 ++++++--- CRM/Event/DAO/ParticipantStatusType.php | 9 ++++++--- CRM/Export/Controller/Standalone.php | 2 +- CRM/Financial/DAO/Currency.php | 9 ++++++--- CRM/Financial/DAO/EntityFinancialAccount.php | 9 ++++++--- CRM/Financial/DAO/EntityFinancialTrxn.php | 9 ++++++--- CRM/Financial/DAO/FinancialAccount.php | 9 ++++++--- CRM/Financial/DAO/FinancialItem.php | 9 ++++++--- CRM/Financial/DAO/FinancialTrxn.php | 9 ++++++--- CRM/Financial/DAO/FinancialType.php | 9 ++++++--- CRM/Financial/DAO/PaymentProcessor.php | 9 ++++++--- CRM/Financial/DAO/PaymentProcessorType.php | 9 ++++++--- CRM/Financial/DAO/PaymentToken.php | 9 ++++++--- CRM/Friend/DAO/Friend.php | 9 ++++++--- CRM/Grant/DAO/Grant.php | 9 ++++++--- CRM/Mailing/DAO/BouncePattern.php | 9 ++++++--- CRM/Mailing/DAO/BounceType.php | 9 ++++++--- CRM/Mailing/DAO/Mailing.php | 9 ++++++--- CRM/Mailing/DAO/MailingAB.php | 9 ++++++--- CRM/Mailing/DAO/MailingComponent.php | 9 ++++++--- CRM/Mailing/DAO/MailingGroup.php | 9 ++++++--- CRM/Mailing/DAO/MailingJob.php | 9 ++++++--- CRM/Mailing/DAO/Recipients.php | 9 ++++++--- CRM/Mailing/DAO/Spool.php | 9 ++++++--- CRM/Mailing/DAO/TrackableURL.php | 9 ++++++--- CRM/Mailing/Event/DAO/Bounce.php | 9 ++++++--- CRM/Mailing/Event/DAO/Confirm.php | 9 ++++++--- CRM/Mailing/Event/DAO/Delivered.php | 9 ++++++--- CRM/Mailing/Event/DAO/Forward.php | 9 ++++++--- CRM/Mailing/Event/DAO/Opened.php | 9 ++++++--- CRM/Mailing/Event/DAO/Queue.php | 9 ++++++--- CRM/Mailing/Event/DAO/Reply.php | 9 ++++++--- CRM/Mailing/Event/DAO/Subscribe.php | 9 ++++++--- CRM/Mailing/Event/DAO/TrackableURLOpen.php | 9 ++++++--- CRM/Mailing/Event/DAO/Unsubscribe.php | 9 ++++++--- CRM/Member/DAO/Membership.php | 9 ++++++--- CRM/Member/DAO/MembershipBlock.php | 9 ++++++--- CRM/Member/DAO/MembershipLog.php | 9 ++++++--- CRM/Member/DAO/MembershipPayment.php | 9 ++++++--- CRM/Member/DAO/MembershipStatus.php | 9 ++++++--- CRM/Member/DAO/MembershipType.php | 9 ++++++--- CRM/PCP/DAO/PCP.php | 9 ++++++--- CRM/PCP/DAO/PCPBlock.php | 9 ++++++--- CRM/Pledge/DAO/Pledge.php | 9 ++++++--- CRM/Pledge/DAO/PledgeBlock.php | 9 ++++++--- CRM/Pledge/DAO/PledgePayment.php | 9 ++++++--- CRM/Price/DAO/LineItem.php | 9 ++++++--- CRM/Price/DAO/PriceField.php | 9 ++++++--- CRM/Price/DAO/PriceFieldValue.php | 9 ++++++--- CRM/Price/DAO/PriceSet.php | 9 ++++++--- CRM/Price/DAO/PriceSetEntity.php | 9 ++++++--- CRM/Queue/DAO/QueueItem.php | 9 ++++++--- CRM/Report/DAO/ReportInstance.php | 9 ++++++--- CRM/SMS/DAO/Provider.php | 9 ++++++--- Civi/Api4/Generic/AbstractEntity.php | 5 ++++- Civi/Api4/Generic/DAOEntity.php | 6 ++++-- xml/schema/Contribute/ContributionRecur.xml | 2 +- xml/schema/Core/IM.xml | 1 + xml/schema/Report/ReportInstance.xml | 2 +- xml/templates/dao.tpl | 7 +++++-- 161 files changed, 934 insertions(+), 477 deletions(-) diff --git a/CRM/ACL/DAO/ACL.php b/CRM/ACL/DAO/ACL.php index fa06cf96c5e2..57934d13036c 100644 --- a/CRM/ACL/DAO/ACL.php +++ b/CRM/ACL/DAO/ACL.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/ACL/ACL.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:f75eaa0ee87675c14a224ec22b2c30a7) + * (GenCodeChecksum:54e8c75c28c9dd74192f60bbcf1605f6) */ /** @@ -117,9 +117,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('ACLs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('ACLs') : ts('ACL'); } /** diff --git a/CRM/ACL/DAO/ACLCache.php b/CRM/ACL/DAO/ACLCache.php index 7e3ac8429f16..671354acb388 100644 --- a/CRM/ACL/DAO/ACLCache.php +++ b/CRM/ACL/DAO/ACLCache.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/ACL/ACLCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:cbf36d56ce734a5f7ceeb2071b68ebf8) + * (GenCodeChecksum:cec3d7c7aced95902840b72829550156) */ /** @@ -68,9 +68,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('ACLCaches'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('ACLCaches') : ts('ACLCache'); } /** diff --git a/CRM/ACL/DAO/EntityRole.php b/CRM/ACL/DAO/EntityRole.php index 9130d307ae4c..4e31bd8466f9 100644 --- a/CRM/ACL/DAO/EntityRole.php +++ b/CRM/ACL/DAO/EntityRole.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/ACL/EntityRole.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d985c951ef9a8872008576b41c1f2b9c) + * (GenCodeChecksum:c1087517beb5b266d4a1a0a1a342ced0) */ /** @@ -75,9 +75,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Entity Roles'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Entity Roles') : ts('Entity Role'); } /** diff --git a/CRM/Activity/DAO/Activity.php b/CRM/Activity/DAO/Activity.php index 03ad751cd6b7..26f50e0b5f65 100644 --- a/CRM/Activity/DAO/Activity.php +++ b/CRM/Activity/DAO/Activity.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Activity/Activity.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:cbcbcbb6720f015deae4097b01196c9a) + * (GenCodeChecksum:c1b4cc908c0220abf69f57d281eeda95) */ /** @@ -226,9 +226,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Activities'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Activities') : ts('Activity'); } /** diff --git a/CRM/Activity/DAO/ActivityContact.php b/CRM/Activity/DAO/ActivityContact.php index 61676f395b5f..fd6633a19111 100644 --- a/CRM/Activity/DAO/ActivityContact.php +++ b/CRM/Activity/DAO/ActivityContact.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Activity/ActivityContact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:777598f3625dfeaf37a81de282808c60) + * (GenCodeChecksum:60851972b9f03efb52350929e557c768) */ /** @@ -68,9 +68,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Activity Contacts'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Activity Contacts') : ts('Activity Contact'); } /** diff --git a/CRM/Batch/DAO/Batch.php b/CRM/Batch/DAO/Batch.php index f2571b674f83..7e153f47861e 100644 --- a/CRM/Batch/DAO/Batch.php +++ b/CRM/Batch/DAO/Batch.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Batch/Batch.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8169fc2f338afc4a163214c0018030be) + * (GenCodeChecksum:29e187971d03eba3ce5e3848f2ea1a5b) */ /** @@ -157,9 +157,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Batches'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Batches') : ts('Batch'); } /** diff --git a/CRM/Batch/DAO/EntityBatch.php b/CRM/Batch/DAO/EntityBatch.php index 6c697fb37073..f7e2fffb74bd 100644 --- a/CRM/Batch/DAO/EntityBatch.php +++ b/CRM/Batch/DAO/EntityBatch.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Batch/EntityBatch.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6b6bd1337d9011c2a262de0e62c1e8e1) + * (GenCodeChecksum:aaeb317f89d831d683f53e7c45e1b175) */ /** @@ -68,9 +68,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Entity Batches'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Entity Batches') : ts('Entity Batch'); } /** diff --git a/CRM/Campaign/DAO/Campaign.php b/CRM/Campaign/DAO/Campaign.php index 742996a6bde2..8dc0245910ea 100644 --- a/CRM/Campaign/DAO/Campaign.php +++ b/CRM/Campaign/DAO/Campaign.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Campaign/Campaign.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:cfa77579eb9b91b31b6c5618b52c6e87) + * (GenCodeChecksum:a5a49e13e66a5d32b690835a49baf535) */ /** @@ -166,9 +166,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Campaigns'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Campaigns') : ts('Campaign'); } /** diff --git a/CRM/Campaign/DAO/CampaignGroup.php b/CRM/Campaign/DAO/CampaignGroup.php index 97d4abd5af18..581ab4e5052b 100644 --- a/CRM/Campaign/DAO/CampaignGroup.php +++ b/CRM/Campaign/DAO/CampaignGroup.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Campaign/CampaignGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:74c02a4708ef706860d023c1635b98c4) + * (GenCodeChecksum:55b3974bbc1aa8405d11a5c396401fa9) */ /** @@ -75,9 +75,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Campaign Groups'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Campaign Groups') : ts('Campaign Group'); } /** diff --git a/CRM/Campaign/DAO/Survey.php b/CRM/Campaign/DAO/Survey.php index 9c9626b6be8c..b6bc19126061 100644 --- a/CRM/Campaign/DAO/Survey.php +++ b/CRM/Campaign/DAO/Survey.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Campaign/Survey.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e955546c8081852591bc08b1fdee4213) + * (GenCodeChecksum:afb7cfcccd2a6177b2b10e07afa92e8e) */ /** @@ -187,9 +187,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Surveys'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Surveys') : ts('Survey'); } /** diff --git a/CRM/Case/DAO/Case.php b/CRM/Case/DAO/Case.php index 946c992f4b6a..ebf2311f678e 100644 --- a/CRM/Case/DAO/Case.php +++ b/CRM/Case/DAO/Case.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Case/Case.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8b18140da75bbf971a143c205f2af1cd) + * (GenCodeChecksum:bae905b3b253acc0df005cc66dd9b717) */ /** @@ -115,9 +115,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Cases'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Cases') : ts('Case'); } /** diff --git a/CRM/Case/DAO/CaseActivity.php b/CRM/Case/DAO/CaseActivity.php index 9efe7b4af3e5..e0d65cb58b8d 100644 --- a/CRM/Case/DAO/CaseActivity.php +++ b/CRM/Case/DAO/CaseActivity.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Case/CaseActivity.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:974d18e84d3416c98293bedd66c3384c) + * (GenCodeChecksum:565c78ce07c94d858e5e6e400c4d1ad6) */ /** @@ -61,9 +61,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Case Activities'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Case Activities') : ts('Case Activity'); } /** diff --git a/CRM/Case/DAO/CaseContact.php b/CRM/Case/DAO/CaseContact.php index 788b1e58f948..d5b524ae02b4 100644 --- a/CRM/Case/DAO/CaseContact.php +++ b/CRM/Case/DAO/CaseContact.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Case/CaseContact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b315f42d7c886c123c9e87c9713c4911) + * (GenCodeChecksum:57c93b00e00d0a48d5071d5a991289ab) */ /** @@ -61,9 +61,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Case Contacts'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Case Contacts') : ts('Case Contact'); } /** diff --git a/CRM/Case/DAO/CaseType.php b/CRM/Case/DAO/CaseType.php index dbafcb30a8db..246edc51fd66 100644 --- a/CRM/Case/DAO/CaseType.php +++ b/CRM/Case/DAO/CaseType.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Case/CaseType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:cde81a56b0e8201eac521b92ded6fb45) + * (GenCodeChecksum:52f839e38c020cd422ef99dff4ab5f1b) */ /** @@ -96,9 +96,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Case Types'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Case Types') : ts('Case Type'); } /** diff --git a/CRM/Contact/DAO/ACLContactCache.php b/CRM/Contact/DAO/ACLContactCache.php index 6b319f9636ce..d5383fbc1d58 100644 --- a/CRM/Contact/DAO/ACLContactCache.php +++ b/CRM/Contact/DAO/ACLContactCache.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/ACLContactCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:97d9be5e13ece64b6c9ad1722d9bca68) + * (GenCodeChecksum:ff86d1eed99d09ea6768d93b3cc39973) */ /** @@ -68,9 +68,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('ACLContact Caches'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('ACLContact Caches') : ts('ACLContact Cache'); } /** diff --git a/CRM/Contact/DAO/Contact.php b/CRM/Contact/DAO/Contact.php index f91dc3264b1e..c585b127039b 100644 --- a/CRM/Contact/DAO/Contact.php +++ b/CRM/Contact/DAO/Contact.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/Contact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:f58884560d4f49764182cd97f1bbbcdf) + * (GenCodeChecksum:f118596cceae71668861504b7316afa7) */ /** @@ -397,9 +397,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Contacts'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Contacts') : ts('Contact'); } /** diff --git a/CRM/Contact/DAO/ContactType.php b/CRM/Contact/DAO/ContactType.php index 41d8b8780369..4f7f63f5a3ed 100644 --- a/CRM/Contact/DAO/ContactType.php +++ b/CRM/Contact/DAO/ContactType.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/ContactType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:0f7546e10f09f7637d50f7a34c632cb5) + * (GenCodeChecksum:9719ec84435a76decf1937f7a389ac7f) */ /** @@ -96,9 +96,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Contact Types'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Contact Types') : ts('Contact Type'); } /** diff --git a/CRM/Contact/DAO/DashboardContact.php b/CRM/Contact/DAO/DashboardContact.php index 16c0b3b59379..f8f4e87daa3c 100644 --- a/CRM/Contact/DAO/DashboardContact.php +++ b/CRM/Contact/DAO/DashboardContact.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/DashboardContact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:67153c09e74eda2febf15986f9c04439) + * (GenCodeChecksum:ef3a393d20b6654dcef952f21df8072d) */ /** @@ -80,9 +80,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Dashboard Contacts'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Dashboard Contacts') : ts('Dashboard Contact'); } /** diff --git a/CRM/Contact/DAO/Group.php b/CRM/Contact/DAO/Group.php index cdae40198635..81f335ad6022 100644 --- a/CRM/Contact/DAO/Group.php +++ b/CRM/Contact/DAO/Group.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/Group.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6a2a222c5fa5b461727bb95379723b08) + * (GenCodeChecksum:9d3fc8fbc20e3b8068d5989828341c66) */ /** @@ -185,9 +185,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Groups'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Groups') : ts('Group'); } /** diff --git a/CRM/Contact/DAO/GroupContact.php b/CRM/Contact/DAO/GroupContact.php index 52c66fd638c9..3cee7b7e0790 100644 --- a/CRM/Contact/DAO/GroupContact.php +++ b/CRM/Contact/DAO/GroupContact.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/GroupContact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:143ba4d95cae73fc81c8e932970cbc1f) + * (GenCodeChecksum:69e994c5047ecf8d68700c694047720f) */ /** @@ -82,9 +82,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Group Contacts'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Group Contacts') : ts('Group Contact'); } /** diff --git a/CRM/Contact/DAO/GroupContactCache.php b/CRM/Contact/DAO/GroupContactCache.php index 3ee02aa7e209..e4310747a9db 100644 --- a/CRM/Contact/DAO/GroupContactCache.php +++ b/CRM/Contact/DAO/GroupContactCache.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/GroupContactCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a169b776ec9bfc8864a05750d4ae6b95) + * (GenCodeChecksum:424f49d5c972e05144c327cf7ba0992c) */ /** @@ -61,9 +61,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Group Contact Caches'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Group Contact Caches') : ts('Group Contact Cache'); } /** diff --git a/CRM/Contact/DAO/GroupNesting.php b/CRM/Contact/DAO/GroupNesting.php index 90624a6e98fd..6262c1f5a44f 100644 --- a/CRM/Contact/DAO/GroupNesting.php +++ b/CRM/Contact/DAO/GroupNesting.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/GroupNesting.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:0ca7da77b0229b439c9c3a4c4c2e4326) + * (GenCodeChecksum:396dffdb22106c85cc4832b61307c264) */ /** @@ -61,9 +61,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Group Nestings'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Group Nestings') : ts('Group Nesting'); } /** diff --git a/CRM/Contact/DAO/GroupOrganization.php b/CRM/Contact/DAO/GroupOrganization.php index 407ea31d83f5..0553dff9ec64 100644 --- a/CRM/Contact/DAO/GroupOrganization.php +++ b/CRM/Contact/DAO/GroupOrganization.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/GroupOrganization.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:dfe8edf8f786790af95f09f456d1cbe7) + * (GenCodeChecksum:0ae83aef7dbfb877f46a92f27001dd6b) */ /** @@ -61,9 +61,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Group Organizations'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Group Organizations') : ts('Group Organization'); } /** diff --git a/CRM/Contact/DAO/Relationship.php b/CRM/Contact/DAO/Relationship.php index 14b10a52802e..8ad5e40702fd 100644 --- a/CRM/Contact/DAO/Relationship.php +++ b/CRM/Contact/DAO/Relationship.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/Relationship.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:25faea8225f483ae95cf29af08a8542d) + * (GenCodeChecksum:7fed0ad7c2ed2b072582b55afdb6469f) */ /** @@ -124,9 +124,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Relationships'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Relationships') : ts('Relationship'); } /** diff --git a/CRM/Contact/DAO/RelationshipCache.php b/CRM/Contact/DAO/RelationshipCache.php index cfa5a75bc2e0..1e874cb2bd9a 100644 --- a/CRM/Contact/DAO/RelationshipCache.php +++ b/CRM/Contact/DAO/RelationshipCache.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/RelationshipCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b40781c15c3351a766a6083522f0e5e4) + * (GenCodeChecksum:6de8ba08019ff8821fd4e09f14db6da9) */ /** @@ -124,9 +124,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Relationship Caches'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Relationship Caches') : ts('Relationship Cache'); } /** diff --git a/CRM/Contact/DAO/RelationshipType.php b/CRM/Contact/DAO/RelationshipType.php index e703e2fe849b..3ac981c81f84 100644 --- a/CRM/Contact/DAO/RelationshipType.php +++ b/CRM/Contact/DAO/RelationshipType.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/RelationshipType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6e9767fcd0fc6eba8fcd408588fe0755) + * (GenCodeChecksum:258f862b2238ae69432d8955ae8df803) */ /** @@ -124,9 +124,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Relationship Types'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Relationship Types') : ts('Relationship Type'); } /** diff --git a/CRM/Contact/DAO/SavedSearch.php b/CRM/Contact/DAO/SavedSearch.php index a5fb2d460375..c532997a2a3d 100644 --- a/CRM/Contact/DAO/SavedSearch.php +++ b/CRM/Contact/DAO/SavedSearch.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/SavedSearch.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4b2f292a8196a5dc4a73afc078cd11cb) + * (GenCodeChecksum:d863f8b0b8659633bc84578e1d6cbf10) */ /** @@ -82,9 +82,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Saved Searches'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Saved Searches') : ts('Saved Search'); } /** diff --git a/CRM/Contact/DAO/SubscriptionHistory.php b/CRM/Contact/DAO/SubscriptionHistory.php index 421ea3381555..cbdab5b5352a 100644 --- a/CRM/Contact/DAO/SubscriptionHistory.php +++ b/CRM/Contact/DAO/SubscriptionHistory.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/SubscriptionHistory.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7c033b0631f14da30172883b14686574) + * (GenCodeChecksum:c38d68dcab2d037fc65d4e59bd30d1d4) */ /** @@ -89,9 +89,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Subscription Histories'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Subscription Histories') : ts('Subscription History'); } /** diff --git a/CRM/Contribute/DAO/Contribution.php b/CRM/Contribute/DAO/Contribution.php index 45f51b756cad..174e9bf69591 100644 --- a/CRM/Contribute/DAO/Contribution.php +++ b/CRM/Contribute/DAO/Contribution.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/Contribution.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:cc3bcdbce84066823084f71e30f6990b) + * (GenCodeChecksum:d937ea0497be1a1aeb1bac09986dd802) */ /** @@ -252,9 +252,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Contributions'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Contributions') : ts('Contribution'); } /** diff --git a/CRM/Contribute/DAO/ContributionPage.php b/CRM/Contribute/DAO/ContributionPage.php index 80ec08d3acf6..be85752fb787 100644 --- a/CRM/Contribute/DAO/ContributionPage.php +++ b/CRM/Contribute/DAO/ContributionPage.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/ContributionPage.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:902bfa164280b9ba21a7cb5a38aceba8) + * (GenCodeChecksum:4910b973830834fcb2ce5bb3637070d6) */ /** @@ -362,9 +362,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Contribution Pages'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Contribution Pages') : ts('Contribution Page'); } /** diff --git a/CRM/Contribute/DAO/ContributionProduct.php b/CRM/Contribute/DAO/ContributionProduct.php index e5a0f053702a..9837f2b8c084 100644 --- a/CRM/Contribute/DAO/ContributionProduct.php +++ b/CRM/Contribute/DAO/ContributionProduct.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/ContributionProduct.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4e76d9dc75f5bc1b1141645c8ee5e2e4) + * (GenCodeChecksum:a2a4170ca2004a1630e27ba83e5edff3) */ /** @@ -100,9 +100,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Contribution Products'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Contribution Products') : ts('Contribution Product'); } /** diff --git a/CRM/Contribute/DAO/ContributionRecur.php b/CRM/Contribute/DAO/ContributionRecur.php index 2ccd7486d6c3..f61c44705145 100644 --- a/CRM/Contribute/DAO/ContributionRecur.php +++ b/CRM/Contribute/DAO/ContributionRecur.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/ContributionRecur.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:decf43c002d0e4ded0fe5f2a2e2f7bd0) + * (GenCodeChecksum:ba5f7682a5f99b682f70cd45097feb56) */ /** @@ -239,9 +239,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Recurring Contributions'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Recurring Contributions') : ts('Recurring Contribution'); } /** diff --git a/CRM/Contribute/DAO/ContributionSoft.php b/CRM/Contribute/DAO/ContributionSoft.php index 3a1a9b7af760..fa78630bad12 100644 --- a/CRM/Contribute/DAO/ContributionSoft.php +++ b/CRM/Contribute/DAO/ContributionSoft.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/ContributionSoft.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:caa58722ef865c7342fdff08f24d86ee) + * (GenCodeChecksum:e37496d0b9938151e5bcf9e6dad23c0a) */ /** @@ -104,9 +104,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Contribution Softs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Contribution Softs') : ts('Contribution Soft'); } /** diff --git a/CRM/Contribute/DAO/Premium.php b/CRM/Contribute/DAO/Premium.php index 7e0020c13492..ebbafd660e65 100644 --- a/CRM/Contribute/DAO/Premium.php +++ b/CRM/Contribute/DAO/Premium.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/Premium.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:cd1826e777cea80450636ef175aaab7f) + * (GenCodeChecksum:b35b6fb4895df990a55d9015bb82a852) */ /** @@ -111,9 +111,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Premiums'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Premiums') : ts('Premium'); } /** diff --git a/CRM/Contribute/DAO/PremiumsProduct.php b/CRM/Contribute/DAO/PremiumsProduct.php index b153adb27496..f99e9ddf30b8 100644 --- a/CRM/Contribute/DAO/PremiumsProduct.php +++ b/CRM/Contribute/DAO/PremiumsProduct.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/PremiumsProduct.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4831cb4c7e0611db0f4312f6522d2c20) + * (GenCodeChecksum:84fea8d6a2a852495da5ed86232d42d1) */ /** @@ -73,9 +73,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Premiums Products'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Premiums Products') : ts('Premiums Product'); } /** diff --git a/CRM/Contribute/DAO/Product.php b/CRM/Contribute/DAO/Product.php index 8433e1820ad0..7b481641af4d 100644 --- a/CRM/Contribute/DAO/Product.php +++ b/CRM/Contribute/DAO/Product.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/Product.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:dea1c7db61776456a70f752fe9f93f06) + * (GenCodeChecksum:d6c90aacbe802ff244a6a4bbaecad4d3) */ /** @@ -170,9 +170,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Products'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Products') : ts('Product'); } /** diff --git a/CRM/Contribute/DAO/Widget.php b/CRM/Contribute/DAO/Widget.php index 5da47438da53..a49f0a9471d9 100644 --- a/CRM/Contribute/DAO/Widget.php +++ b/CRM/Contribute/DAO/Widget.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contribute/Widget.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e24eaf675b793969d408fbc0f847a9ed) + * (GenCodeChecksum:bb99920b9b2c2a8b7419ecd94dcbf577) */ /** @@ -141,9 +141,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Widgets'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Widgets') : ts('Widget'); } /** diff --git a/CRM/Core/CodeGen/Specification.php b/CRM/Core/CodeGen/Specification.php index 4d2be52ad41a..6a96e0d7c2ab 100644 --- a/CRM/Core/CodeGen/Specification.php +++ b/CRM/Core/CodeGen/Specification.php @@ -204,13 +204,15 @@ public function getTable($tableXML, &$database, &$tables) { } } + $titleFromClass = preg_replace('/([a-z])([A-Z])/', '$1 $2', $klass); $table = [ 'name' => $name, 'base' => $daoPath, 'sourceFile' => $sourceFile, 'fileName' => $klass . '.php', 'objectName' => $klass, - 'title' => $tableXML->title ?? self::nameToTitle($klass), + 'title' => $tableXML->title ?? $titleFromClass, + 'titlePlural' => $tableXML->titlePlural ?? CRM_Utils_String::pluralize($tableXML->title ?? $titleFromClass), 'icon' => $tableXML->icon ?? NULL, 'add' => $tableXML->add ?? NULL, 'labelName' => substr($name, 8), @@ -746,15 +748,4 @@ protected function getSize($fieldXML) { return 'CRM_Utils_Type::HUGE'; } - /** - * Converts an entity name to a user friendly string. - * - * @param string $name - * return string - */ - public static function nameToTitle(string $name) { - $name = preg_replace('/([a-z])([A-Z])/', '$1 $2', $name); - return CRM_Utils_String::pluralize($name); - } - } diff --git a/CRM/Core/DAO.php b/CRM/Core/DAO.php index d62fba1401e7..ee3cfaeebb6d 100644 --- a/CRM/Core/DAO.php +++ b/CRM/Core/DAO.php @@ -121,11 +121,12 @@ public function __construct() { /** * Returns localized title of this entity. + * * @return string */ public static function getEntityTitle() { $className = static::class; - Civi::log()->warning("$className needs to be regeneraged. Missing getEntityTitle method.", ['civi.tag' => 'deprecated']); + Civi::log()->warning("$className needs to be regenerated. Missing getEntityTitle method.", ['civi.tag' => 'deprecated']); return CRM_Core_DAO_AllCoreTables::getBriefName($className); } diff --git a/CRM/Core/DAO/ActionLog.php b/CRM/Core/DAO/ActionLog.php index f725a2558a18..8eb9523e50a5 100644 --- a/CRM/Core/DAO/ActionLog.php +++ b/CRM/Core/DAO/ActionLog.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/ActionLog.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:2ccef6f7cc6a43d833301e93a2a0d61f) + * (GenCodeChecksum:a449765feaf80c56214be9fce2f118b4) */ /** @@ -108,9 +108,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Action Logs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Action Logs') : ts('Action Log'); } /** diff --git a/CRM/Core/DAO/ActionMapping.php b/CRM/Core/DAO/ActionMapping.php index 8a61ce5bbc49..3224bc1269fa 100644 --- a/CRM/Core/DAO/ActionMapping.php +++ b/CRM/Core/DAO/ActionMapping.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/ActionMapping.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:037a3f26719a4774957814f28c499e60) + * (GenCodeChecksum:7db8e13984f33629f584a8a54c37fd4a) */ /** @@ -101,9 +101,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Action Mappings'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Action Mappings') : ts('Action Mapping'); } /** diff --git a/CRM/Core/DAO/ActionSchedule.php b/CRM/Core/DAO/ActionSchedule.php index 666f773ba859..181a1bdee506 100644 --- a/CRM/Core/DAO/ActionSchedule.php +++ b/CRM/Core/DAO/ActionSchedule.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/ActionSchedule.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d05639de89f460efbb3474dcaf5acd27) + * (GenCodeChecksum:77bfa18590c85ad7b6430c018acd508c) */ /** @@ -300,9 +300,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Action Schedules'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Action Schedules') : ts('Action Schedule'); } /** diff --git a/CRM/Core/DAO/Address.php b/CRM/Core/DAO/Address.php index 7151847db2f9..257de9924f39 100644 --- a/CRM/Core/DAO/Address.php +++ b/CRM/Core/DAO/Address.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Address.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a6b8f21dd3839de1ce1273e0910f0c8c) + * (GenCodeChecksum:01b95dc4df972f40d718b61c77cd3b58) */ /** @@ -250,9 +250,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Addresses'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Addresses') : ts('Address'); } /** diff --git a/CRM/Core/DAO/AddressFormat.php b/CRM/Core/DAO/AddressFormat.php index 762b90b6a177..d1c1d088427b 100644 --- a/CRM/Core/DAO/AddressFormat.php +++ b/CRM/Core/DAO/AddressFormat.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/AddressFormat.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1ce11647576d05acfc364969eddfcce4) + * (GenCodeChecksum:7cc2864a78be48031ca829de49afeef6) */ /** @@ -54,9 +54,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Address Formats'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Address Formats') : ts('Address Format'); } /** diff --git a/CRM/Core/DAO/Cache.php b/CRM/Core/DAO/Cache.php index 01bc33ea91c5..f8366cb2d037 100644 --- a/CRM/Core/DAO/Cache.php +++ b/CRM/Core/DAO/Cache.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Cache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:af1401f844c699c6ad35366a32a8db03) + * (GenCodeChecksum:bafe6ce2f7fd94b2581bfc4dad813130) */ /** @@ -89,9 +89,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Caches'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Caches') : ts('Cache'); } /** diff --git a/CRM/Core/DAO/Component.php b/CRM/Core/DAO/Component.php index f9b7b077f9ad..f1d560b656b6 100644 --- a/CRM/Core/DAO/Component.php +++ b/CRM/Core/DAO/Component.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Component.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6c3fd2c8e875746c0ceffa499624f77c) + * (GenCodeChecksum:a5136517000cfab182cdacc3130bc29c) */ /** @@ -61,9 +61,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Components'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Components') : ts('Component'); } /** diff --git a/CRM/Core/DAO/Country.php b/CRM/Core/DAO/Country.php index 24d458b99cef..b91e0062fb23 100644 --- a/CRM/Core/DAO/Country.php +++ b/CRM/Core/DAO/Country.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Country.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:cdd80b394924586274cf4b91183d3637) + * (GenCodeChecksum:2215bb79c9fe62c60700f232598a9462) */ /** @@ -103,9 +103,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Countries'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Countries') : ts('Country'); } /** diff --git a/CRM/Core/DAO/County.php b/CRM/Core/DAO/County.php index 97d2fae58ea1..9b05e4bee6cc 100644 --- a/CRM/Core/DAO/County.php +++ b/CRM/Core/DAO/County.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/County.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:96f94dbbafff9a4e1f0ff276799fcbbd) + * (GenCodeChecksum:5620d2136c764c786eef1c15853eaf9b) */ /** @@ -68,9 +68,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Counties'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Counties') : ts('County'); } /** diff --git a/CRM/Core/DAO/CustomField.php b/CRM/Core/DAO/CustomField.php index 5edafdf5c6e1..a453096360b9 100644 --- a/CRM/Core/DAO/CustomField.php +++ b/CRM/Core/DAO/CustomField.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/CustomField.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b74179ea5553c544931562d6aac5641e) + * (GenCodeChecksum:3a8f6978ec00d7e2cff93f2915ac1f48) */ /** @@ -257,9 +257,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Custom Fields'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Custom Fields') : ts('Custom Field'); } /** diff --git a/CRM/Core/DAO/CustomGroup.php b/CRM/Core/DAO/CustomGroup.php index 12dd47769ebc..8be9d28b2426 100644 --- a/CRM/Core/DAO/CustomGroup.php +++ b/CRM/Core/DAO/CustomGroup.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/CustomGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:494d883be861157d8067e6a6c50c23f6) + * (GenCodeChecksum:3436e2a4cf99bd9b7c859170db37bce3) */ /** @@ -194,9 +194,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Custom Groups'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Custom Groups') : ts('Custom Group'); } /** diff --git a/CRM/Core/DAO/Dashboard.php b/CRM/Core/DAO/Dashboard.php index a9d40d32437c..57773da9dfce 100644 --- a/CRM/Core/DAO/Dashboard.php +++ b/CRM/Core/DAO/Dashboard.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Dashboard.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:2d134bfa6938d2e8a8d8e25e99769823) + * (GenCodeChecksum:262213759ac6f4c9943f4ebd454256ae) */ /** @@ -115,9 +115,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Dashboards'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Dashboards') : ts('Dashboard'); } /** diff --git a/CRM/Core/DAO/Discount.php b/CRM/Core/DAO/Discount.php index ed2f53e1e1d5..a18cacfb6003 100644 --- a/CRM/Core/DAO/Discount.php +++ b/CRM/Core/DAO/Discount.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Discount.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a23716379d3cccf678a9d8e423690e7c) + * (GenCodeChecksum:5fbe08bc556f5b913860a55c6d0cedc4) */ /** @@ -82,9 +82,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Discounts'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Discounts') : ts('Discount'); } /** diff --git a/CRM/Core/DAO/Domain.php b/CRM/Core/DAO/Domain.php index 82ccf0f5a8f9..ce6b480508f9 100644 --- a/CRM/Core/DAO/Domain.php +++ b/CRM/Core/DAO/Domain.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Domain.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:57a526de0b2bc02fed832a22dc50ad80) + * (GenCodeChecksum:99a50c29878792b8864e20d184ce9bbb) */ /** @@ -89,9 +89,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Domains'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Domains') : ts('Domain'); } /** diff --git a/CRM/Core/DAO/Email.php b/CRM/Core/DAO/Email.php index 7c0a55b812bd..4f890d5cf0b0 100644 --- a/CRM/Core/DAO/Email.php +++ b/CRM/Core/DAO/Email.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Email.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:866b627595adac9091080a4e4ab146bc) + * (GenCodeChecksum:2736b767bcd747315f0382f4e298ad35) */ /** @@ -131,9 +131,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Emails'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Emails') : ts('Email'); } /** diff --git a/CRM/Core/DAO/EntityFile.php b/CRM/Core/DAO/EntityFile.php index 0d32257d1e18..e405a0d0d6a6 100644 --- a/CRM/Core/DAO/EntityFile.php +++ b/CRM/Core/DAO/EntityFile.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/EntityFile.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:f2d4dfec2466ad664b4949983b1c7e58) + * (GenCodeChecksum:70221552c8c9532b2aaf9cd89e73a68d) */ /** @@ -68,9 +68,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Entity Files'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Entity Files') : ts('Entity File'); } /** diff --git a/CRM/Core/DAO/EntityTag.php b/CRM/Core/DAO/EntityTag.php index eb2be19b6666..6db35b547c23 100644 --- a/CRM/Core/DAO/EntityTag.php +++ b/CRM/Core/DAO/EntityTag.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/EntityTag.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:58f15f695b38fa4cacfdf82d2734e0f0) + * (GenCodeChecksum:82e27d87178a408cd0a1c201f3501dd2) */ /** @@ -68,9 +68,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Entity Tags'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Entity Tags') : ts('Entity Tag'); } /** diff --git a/CRM/Core/DAO/Extension.php b/CRM/Core/DAO/Extension.php index c666e3ab0222..b1fc0de3f60f 100644 --- a/CRM/Core/DAO/Extension.php +++ b/CRM/Core/DAO/Extension.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Extension.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:08d2151b75e68f334bd88475b58fab7b) + * (GenCodeChecksum:46f6ff725b1ad9909d2340d728438d36) */ /** @@ -94,9 +94,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Extensions'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Extensions') : ts('Extension'); } /** diff --git a/CRM/Core/DAO/File.php b/CRM/Core/DAO/File.php index dc6508d4a6e4..620734811c1e 100644 --- a/CRM/Core/DAO/File.php +++ b/CRM/Core/DAO/File.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/File.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:aa0883a815a43dd250612348f3ec470e) + * (GenCodeChecksum:dd8a70727f67481339dd514fdca6aae5) */ /** @@ -96,9 +96,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Files'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Files') : ts('File'); } /** diff --git a/CRM/Core/DAO/IM.php b/CRM/Core/DAO/IM.php index 958b4dda190c..fdc40df188c4 100644 --- a/CRM/Core/DAO/IM.php +++ b/CRM/Core/DAO/IM.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/IM.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:da6b080a31b208a71635d272fabab7ec) + * (GenCodeChecksum:f64b9a15f4a240d7a137cb5655feb696) */ /** @@ -96,9 +96,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Instant Messaging'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Instant Messaging') : ts('Instant Messaging'); } /** diff --git a/CRM/Core/DAO/Job.php b/CRM/Core/DAO/Job.php index ebdb62818e4e..cbc870082e44 100644 --- a/CRM/Core/DAO/Job.php +++ b/CRM/Core/DAO/Job.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Job.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6e3a5de515fda550b1b5aeb493c50f0b) + * (GenCodeChecksum:bbc36abe96310ec5cf23d46d2d1728cb) */ /** @@ -117,9 +117,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Jobs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Jobs') : ts('Job'); } /** diff --git a/CRM/Core/DAO/JobLog.php b/CRM/Core/DAO/JobLog.php index b59a98b1f5b2..51db9714637f 100644 --- a/CRM/Core/DAO/JobLog.php +++ b/CRM/Core/DAO/JobLog.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/JobLog.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:eca8e7af1026dbfaf7beecb95ce02361) + * (GenCodeChecksum:74e191eba977eb496bae109ca9720ab7) */ /** @@ -96,9 +96,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Job Logs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Job Logs') : ts('Job Log'); } /** diff --git a/CRM/Core/DAO/LocBlock.php b/CRM/Core/DAO/LocBlock.php index 154a2e776c61..2eca88b4b625 100644 --- a/CRM/Core/DAO/LocBlock.php +++ b/CRM/Core/DAO/LocBlock.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/LocBlock.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8459c5a6d25e5c70e44de49b109a82fa) + * (GenCodeChecksum:801a2e7e05f688cf38882b466f22a292) */ /** @@ -87,9 +87,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Loc Blocks'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Loc Blocks') : ts('Loc Block'); } /** diff --git a/CRM/Core/DAO/LocationType.php b/CRM/Core/DAO/LocationType.php index 70648f67db31..3427ba89b38b 100644 --- a/CRM/Core/DAO/LocationType.php +++ b/CRM/Core/DAO/LocationType.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/LocationType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:325ccb933339bc909efc7d6b60c7186b) + * (GenCodeChecksum:450719aeeb146b60007152d03e0b6faf) */ /** @@ -96,9 +96,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Location Types'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Location Types') : ts('Location Type'); } /** diff --git a/CRM/Core/DAO/Log.php b/CRM/Core/DAO/Log.php index 182d42f5d40e..b550b04515c6 100644 --- a/CRM/Core/DAO/Log.php +++ b/CRM/Core/DAO/Log.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Log.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:5dfdb2863ba1bc7b84288a522cdaef51) + * (GenCodeChecksum:3b97d17eeaa407d9f7f7aa6e1d819090) */ /** @@ -82,9 +82,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Logs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Logs') : ts('Log'); } /** diff --git a/CRM/Core/DAO/MailSettings.php b/CRM/Core/DAO/MailSettings.php index acc8278c1904..6c3c02f70d60 100644 --- a/CRM/Core/DAO/MailSettings.php +++ b/CRM/Core/DAO/MailSettings.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/MailSettings.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b43716d8c8e362738d8d3420e8fbe03d) + * (GenCodeChecksum:310f3c60fe656e8ef27a67234d6fa80c) */ /** @@ -145,9 +145,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Mail Settingses'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Mail Settingses') : ts('Mail Settings'); } /** diff --git a/CRM/Core/DAO/Managed.php b/CRM/Core/DAO/Managed.php index 212817ec7860..a67271247cbe 100644 --- a/CRM/Core/DAO/Managed.php +++ b/CRM/Core/DAO/Managed.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Managed.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e6146e35f8c8321e600a4198cbd6949e) + * (GenCodeChecksum:119de7e386aa83b1ca5038a1e409aafe) */ /** @@ -82,9 +82,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Manageds'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Manageds') : ts('Managed'); } /** diff --git a/CRM/Core/DAO/Mapping.php b/CRM/Core/DAO/Mapping.php index 59505d1d5fb0..3c7639a857d5 100644 --- a/CRM/Core/DAO/Mapping.php +++ b/CRM/Core/DAO/Mapping.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Mapping.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c97b13ea2aaccdf8ba13b6552ccb59f2) + * (GenCodeChecksum:49336d40a24f0312944123741932dd25) */ /** @@ -68,9 +68,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Mappings'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Mappings') : ts('Mapping'); } /** diff --git a/CRM/Core/DAO/MappingField.php b/CRM/Core/DAO/MappingField.php index cd5f06e741b4..bfe139eda992 100644 --- a/CRM/Core/DAO/MappingField.php +++ b/CRM/Core/DAO/MappingField.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/MappingField.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:3702a3c3cb9cd696eb829d15f4676439) + * (GenCodeChecksum:7197da108e5452be6ab8b419a1506aec) */ /** @@ -137,9 +137,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Mapping Fields'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Mapping Fields') : ts('Mapping Field'); } /** diff --git a/CRM/Core/DAO/Menu.php b/CRM/Core/DAO/Menu.php index 98f3f4ad1491..3fb9a797c3e0 100644 --- a/CRM/Core/DAO/Menu.php +++ b/CRM/Core/DAO/Menu.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Menu.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8be7941b4dccb08266109e3e1599159f) + * (GenCodeChecksum:b94ecc10dafe21deb7e5067ef46f32af) */ /** @@ -190,9 +190,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Menus'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Menus') : ts('Menu'); } /** diff --git a/CRM/Core/DAO/MessageTemplate.php b/CRM/Core/DAO/MessageTemplate.php index 5c7965c83f83..f3da47e578b7 100644 --- a/CRM/Core/DAO/MessageTemplate.php +++ b/CRM/Core/DAO/MessageTemplate.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/MessageTemplate.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:68dd4ac3c9f098e3577dbed8d5a2a105) + * (GenCodeChecksum:6881b34cbbefc06722c58fe7f20b1c58) */ /** @@ -120,9 +120,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Message Templates'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Message Templates') : ts('Message Template'); } /** diff --git a/CRM/Core/DAO/Navigation.php b/CRM/Core/DAO/Navigation.php index b567777810a4..ed52b19610f3 100644 --- a/CRM/Core/DAO/Navigation.php +++ b/CRM/Core/DAO/Navigation.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Navigation.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:34c3d3b834400b49f1b8c6c99a08c99e) + * (GenCodeChecksum:8cc5473f0cd98bf289dc455eefb0af76) */ /** @@ -122,9 +122,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Navigations'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Navigations') : ts('Navigation'); } /** diff --git a/CRM/Core/DAO/Note.php b/CRM/Core/DAO/Note.php index 748d1076f18e..3c730b47ee70 100644 --- a/CRM/Core/DAO/Note.php +++ b/CRM/Core/DAO/Note.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Note.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:86e72396a497a58c1568d0d081435e75) + * (GenCodeChecksum:75161cdedcd719f035387c9c1d0d83dd) */ /** @@ -103,9 +103,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Notes'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Notes') : ts('Note'); } /** diff --git a/CRM/Core/DAO/OpenID.php b/CRM/Core/DAO/OpenID.php index 1a61aba15603..dfe13c489d5c 100644 --- a/CRM/Core/DAO/OpenID.php +++ b/CRM/Core/DAO/OpenID.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/OpenID.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4d60933113e2b5330dd8194e7ebe6ae4) + * (GenCodeChecksum:7af55174e40a30da959ad7734573eb9a) */ /** @@ -82,9 +82,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Open IDs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Open IDs') : ts('Open ID'); } /** diff --git a/CRM/Core/DAO/OptionGroup.php b/CRM/Core/DAO/OptionGroup.php index 9feb9fd8f107..76ef9a0bcff1 100644 --- a/CRM/Core/DAO/OptionGroup.php +++ b/CRM/Core/DAO/OptionGroup.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/OptionGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d0011ad2bb6c090eeb86d25916c5624b) + * (GenCodeChecksum:e9bb68e874d377a0c30b433103d438d4) */ /** @@ -96,9 +96,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Option Groups'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Option Groups') : ts('Option Group'); } /** diff --git a/CRM/Core/DAO/OptionValue.php b/CRM/Core/DAO/OptionValue.php index 7f0c3644f208..5fb8ddb4521f 100644 --- a/CRM/Core/DAO/OptionValue.php +++ b/CRM/Core/DAO/OptionValue.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/OptionValue.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e51b16ecfe5f8302c8610b7f5dfd55e5) + * (GenCodeChecksum:f47024ac081427ddadce4c569934f8a6) */ /** @@ -164,9 +164,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Option Values'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Option Values') : ts('Option Value'); } /** diff --git a/CRM/Core/DAO/Phone.php b/CRM/Core/DAO/Phone.php index 9a6b668620a6..5c0b7816a5c2 100644 --- a/CRM/Core/DAO/Phone.php +++ b/CRM/Core/DAO/Phone.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Phone.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:efdb60e03b54f246e73588b6eb99611d) + * (GenCodeChecksum:f77d3ef5985c8945730c2fe22bb3fa45) */ /** @@ -117,9 +117,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Phones'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Phones') : ts('Phone'); } /** diff --git a/CRM/Core/DAO/PreferencesDate.php b/CRM/Core/DAO/PreferencesDate.php index b1d08e7cd045..78fa11538f44 100644 --- a/CRM/Core/DAO/PreferencesDate.php +++ b/CRM/Core/DAO/PreferencesDate.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/PreferencesDate.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:21383b05b8c8e98ed1721aab06031907) + * (GenCodeChecksum:d0de29e655d17a479ec8cfc762582c39) */ /** @@ -87,9 +87,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Preferences Dates'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Preferences Dates') : ts('Preferences Date'); } /** diff --git a/CRM/Core/DAO/PrevNextCache.php b/CRM/Core/DAO/PrevNextCache.php index a39549db9e6c..258e3ef5c403 100644 --- a/CRM/Core/DAO/PrevNextCache.php +++ b/CRM/Core/DAO/PrevNextCache.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/PrevNextCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8086ffe55554b0fba698136fd6dee894) + * (GenCodeChecksum:325d605774498631dd0a2742963d1032) */ /** @@ -85,9 +85,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Prev Next Caches'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Prev Next Caches') : ts('Prev Next Cache'); } /** diff --git a/CRM/Core/DAO/PrintLabel.php b/CRM/Core/DAO/PrintLabel.php index 129e80804da9..97575a87dd91 100644 --- a/CRM/Core/DAO/PrintLabel.php +++ b/CRM/Core/DAO/PrintLabel.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/PrintLabel.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:484a16ebc1b881e7718bfcf139024ee7) + * (GenCodeChecksum:9612aababed43ba4eaacc71a727c5ed9) */ /** @@ -115,9 +115,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Print Labels'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Print Labels') : ts('Print Label'); } /** diff --git a/CRM/Core/DAO/RecurringEntity.php b/CRM/Core/DAO/RecurringEntity.php index d22aa96ac773..1cffe8c273d4 100644 --- a/CRM/Core/DAO/RecurringEntity.php +++ b/CRM/Core/DAO/RecurringEntity.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/RecurringEntity.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b78474c715335f7689a9a5fcdcb5718e) + * (GenCodeChecksum:2667b60a5f917352d52964de09cf85fa) */ /** @@ -73,9 +73,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Recurring Entities'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Recurring Entities') : ts('Recurring Entity'); } /** diff --git a/CRM/Core/DAO/Setting.php b/CRM/Core/DAO/Setting.php index 160f781cda0f..4fb42fd82a45 100644 --- a/CRM/Core/DAO/Setting.php +++ b/CRM/Core/DAO/Setting.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Setting.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c1fda2807e8265021ffaa490325a7e4f) + * (GenCodeChecksum:24ec63102452f5cdff8424b5caf8b679) */ /** @@ -101,9 +101,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Settings'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Settings') : ts('Setting'); } /** diff --git a/CRM/Core/DAO/StateProvince.php b/CRM/Core/DAO/StateProvince.php index 80a91c2e1f15..7342cb6e69a6 100644 --- a/CRM/Core/DAO/StateProvince.php +++ b/CRM/Core/DAO/StateProvince.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/StateProvince.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:2dced9a7a3e6be3d05ea7b7babe4b113) + * (GenCodeChecksum:ca396996ffd11fc0b89e34657679a9ad) */ /** @@ -68,9 +68,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('State Provinces'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('State Provinces') : ts('State Province'); } /** diff --git a/CRM/Core/DAO/StatusPreference.php b/CRM/Core/DAO/StatusPreference.php index b06b206f2483..e5654e60147c 100644 --- a/CRM/Core/DAO/StatusPreference.php +++ b/CRM/Core/DAO/StatusPreference.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/StatusPreference.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1fa80acc24bcb14df3947cba2daa930f) + * (GenCodeChecksum:7c7deea1bd07dccee5a5bae0b1d6c4c7) */ /** @@ -96,9 +96,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Status Preferences'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Status Preferences') : ts('Status Preference'); } /** diff --git a/CRM/Core/DAO/SystemLog.php b/CRM/Core/DAO/SystemLog.php index c18ee55ebca7..d3cc2ec35880 100644 --- a/CRM/Core/DAO/SystemLog.php +++ b/CRM/Core/DAO/SystemLog.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/SystemLog.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:46d7f317ffb5d01d9cb22898ce38abb3) + * (GenCodeChecksum:3765c5a89ca1e9ff224fbd49e31a4037) */ /** @@ -89,9 +89,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('System Logs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('System Logs') : ts('System Log'); } /** diff --git a/CRM/Core/DAO/Tag.php b/CRM/Core/DAO/Tag.php index 65ae7a378b80..15078e57da7e 100644 --- a/CRM/Core/DAO/Tag.php +++ b/CRM/Core/DAO/Tag.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Tag.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7a2eb010fd96445604104b6ada9c0b99) + * (GenCodeChecksum:8a5eeefa40273898a4e3661b1ef43b3d) */ /** @@ -118,9 +118,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Tags'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Tags') : ts('Tag'); } /** diff --git a/CRM/Core/DAO/Timezone.php b/CRM/Core/DAO/Timezone.php index 5f9abdfc6c34..bae04f5e2d28 100644 --- a/CRM/Core/DAO/Timezone.php +++ b/CRM/Core/DAO/Timezone.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Timezone.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:fb1089cb65c1587b1242b9d250c664f7) + * (GenCodeChecksum:b49b5541f20c732fc7ff4cdc3687899b) */ /** @@ -80,9 +80,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Timezones'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Timezones') : ts('Timezone'); } /** diff --git a/CRM/Core/DAO/UFField.php b/CRM/Core/DAO/UFField.php index a112e1c6f32f..87cde6f32d88 100644 --- a/CRM/Core/DAO/UFField.php +++ b/CRM/Core/DAO/UFField.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/UFField.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e717ec384cfe13629b4fc440af2a99d5) + * (GenCodeChecksum:1cf845aa31eed1a29bffcd4404862f6a) */ /** @@ -173,9 +173,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('UFFields'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('UFFields') : ts('UFField'); } /** diff --git a/CRM/Core/DAO/UFGroup.php b/CRM/Core/DAO/UFGroup.php index ae79d22da8e8..89d25b997ee0 100644 --- a/CRM/Core/DAO/UFGroup.php +++ b/CRM/Core/DAO/UFGroup.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/UFGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1cac6e6b80a630f69ce25f709a20e4a3) + * (GenCodeChecksum:e33df676b4e3ae50e18f1c44e437e3ea) */ /** @@ -227,9 +227,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('UFGroups'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('UFGroups') : ts('UFGroup'); } /** diff --git a/CRM/Core/DAO/UFJoin.php b/CRM/Core/DAO/UFJoin.php index 98639423b142..28f3f56340ba 100644 --- a/CRM/Core/DAO/UFJoin.php +++ b/CRM/Core/DAO/UFJoin.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/UFJoin.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:191143bced279d48cf34cdf6cf85a5fb) + * (GenCodeChecksum:4bcf2b3e5905f98d83d449b1226903da) */ /** @@ -96,9 +96,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('UFJoins'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('UFJoins') : ts('UFJoin'); } /** diff --git a/CRM/Core/DAO/UFMatch.php b/CRM/Core/DAO/UFMatch.php index fa08f60b222f..6a5d32b772a0 100644 --- a/CRM/Core/DAO/UFMatch.php +++ b/CRM/Core/DAO/UFMatch.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/UFMatch.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4fca2151c2ecbd762ac9e2f067f0030f) + * (GenCodeChecksum:613e38722266d0117e69f521e4f0d140) */ /** @@ -82,9 +82,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('UFMatches'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('UFMatches') : ts('UFMatch'); } /** diff --git a/CRM/Core/DAO/Website.php b/CRM/Core/DAO/Website.php index e9415359c114..aed21d3676e1 100644 --- a/CRM/Core/DAO/Website.php +++ b/CRM/Core/DAO/Website.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Website.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:9e449b1f3a997b63c79066bd5cd782ae) + * (GenCodeChecksum:1d9c6cf4e3d809a3b3f5963ccb189e83) */ /** @@ -75,9 +75,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Websites'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Websites') : ts('Website'); } /** diff --git a/CRM/Core/DAO/WordReplacement.php b/CRM/Core/DAO/WordReplacement.php index 65ac087b5543..691291abcfc1 100644 --- a/CRM/Core/DAO/WordReplacement.php +++ b/CRM/Core/DAO/WordReplacement.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/WordReplacement.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:f4afc331da543068dba6d243d98b8e39) + * (GenCodeChecksum:104090c38770547ffa491fccdf299765) */ /** @@ -80,9 +80,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Word Replacements'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Word Replacements') : ts('Word Replacement'); } /** diff --git a/CRM/Core/DAO/Worldregion.php b/CRM/Core/DAO/Worldregion.php index 1dd866013d60..0974aecff55c 100644 --- a/CRM/Core/DAO/Worldregion.php +++ b/CRM/Core/DAO/Worldregion.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Core/Worldregion.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:0312ba4169a285839ec54d655ff5ceb3) + * (GenCodeChecksum:af9fba05839764ec479cc5fad738390a) */ /** @@ -54,9 +54,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Worldregions'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Worldregions') : ts('Worldregion'); } /** diff --git a/CRM/Cxn/DAO/Cxn.php b/CRM/Cxn/DAO/Cxn.php index b0b70337bd8a..03e9fc8ef045 100644 --- a/CRM/Cxn/DAO/Cxn.php +++ b/CRM/Cxn/DAO/Cxn.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Cxn/Cxn.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:059dd4994211085d728a9fc8b7d80803) + * (GenCodeChecksum:10e6547299a52750abd62b96dfacc9de) */ /** @@ -117,9 +117,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Cxns'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Cxns') : ts('Cxn'); } /** diff --git a/CRM/Dedupe/DAO/Exception.php b/CRM/Dedupe/DAO/Exception.php index cdbb87cc90c7..ca488f4208b1 100644 --- a/CRM/Dedupe/DAO/Exception.php +++ b/CRM/Dedupe/DAO/Exception.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Dedupe/Exception.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:f4bc21b42b1b5c9cfb0ffa7d3eb46e65) + * (GenCodeChecksum:95d83f44443d6ddfed8758214c46ff1b) */ /** @@ -61,9 +61,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Exceptions'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Exceptions') : ts('Exception'); } /** diff --git a/CRM/Dedupe/DAO/Rule.php b/CRM/Dedupe/DAO/Rule.php index dea3d38759d4..a09f6a7f4be5 100644 --- a/CRM/Dedupe/DAO/Rule.php +++ b/CRM/Dedupe/DAO/Rule.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Dedupe/Rule.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:56abeb7ada5e3dfde910bc5033ca047d) + * (GenCodeChecksum:1c0c64573702774a043ea32c73f05bd8) */ /** @@ -82,9 +82,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Rules'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Rules') : ts('Rule'); } /** diff --git a/CRM/Dedupe/DAO/RuleGroup.php b/CRM/Dedupe/DAO/RuleGroup.php index 366ff64246d5..62c6a4b2d017 100644 --- a/CRM/Dedupe/DAO/RuleGroup.php +++ b/CRM/Dedupe/DAO/RuleGroup.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Dedupe/RuleGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:87a385df0b5bca8150117411f2c31a4a) + * (GenCodeChecksum:5fb31b058249567562ab0a30a739fda2) */ /** @@ -89,9 +89,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Rule Groups'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Rule Groups') : ts('Rule Group'); } /** diff --git a/CRM/Event/Cart/DAO/Cart.php b/CRM/Event/Cart/DAO/Cart.php index 38da6b32272d..bf8ece70d43a 100644 --- a/CRM/Event/Cart/DAO/Cart.php +++ b/CRM/Event/Cart/DAO/Cart.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Event/Cart/Cart.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b4aacbeb6deddb31e520ce700e774db5) + * (GenCodeChecksum:b7a4ad0bbb09a64b8afefd7a1a2d6a9b) */ /** @@ -59,9 +59,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Carts'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Carts') : ts('Cart'); } /** diff --git a/CRM/Event/Cart/DAO/EventInCart.php b/CRM/Event/Cart/DAO/EventInCart.php index d1550cd54ad6..61f42ceb2254 100644 --- a/CRM/Event/Cart/DAO/EventInCart.php +++ b/CRM/Event/Cart/DAO/EventInCart.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Event/Cart/EventInCart.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b1cb9524ae26740c93dda80d0cb4ff91) + * (GenCodeChecksum:ef07999cbb7872dc9c1cfa6a0055bee1) */ /** @@ -61,9 +61,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Event In Carts'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Event In Carts') : ts('Event In Cart'); } /** diff --git a/CRM/Event/DAO/Event.php b/CRM/Event/DAO/Event.php index a7febe5bfe94..553ddd7e1f39 100644 --- a/CRM/Event/DAO/Event.php +++ b/CRM/Event/DAO/Event.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Event/Event.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:82ba48cbb804cf6f4b26fa50f07d44db) + * (GenCodeChecksum:3514f838a27ddbf9bdf6e63ea20aabec) */ /** @@ -528,9 +528,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Events'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Events') : ts('Event'); } /** diff --git a/CRM/Event/DAO/Participant.php b/CRM/Event/DAO/Participant.php index 94db537c3abe..3d7a2fa49dbd 100644 --- a/CRM/Event/DAO/Participant.php +++ b/CRM/Event/DAO/Participant.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Event/Participant.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:bf8ed42264e81ccaef0ae236242990d0) + * (GenCodeChecksum:97d6a90c8b1f973347dc8decd97f84ee) */ /** @@ -177,9 +177,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Participants'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Participants') : ts('Participant'); } /** diff --git a/CRM/Event/DAO/ParticipantPayment.php b/CRM/Event/DAO/ParticipantPayment.php index ee0070dfc213..44cff71fabed 100644 --- a/CRM/Event/DAO/ParticipantPayment.php +++ b/CRM/Event/DAO/ParticipantPayment.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Event/ParticipantPayment.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:0c828890e84b791e0432445eb2d01086) + * (GenCodeChecksum:767fb54121e25d39d9b82cfbb4a5355b) */ /** @@ -61,9 +61,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Participant Payments'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Participant Payments') : ts('Participant Payment'); } /** diff --git a/CRM/Event/DAO/ParticipantStatusType.php b/CRM/Event/DAO/ParticipantStatusType.php index 2b6e4f8f030f..cf410c357bcd 100644 --- a/CRM/Event/DAO/ParticipantStatusType.php +++ b/CRM/Event/DAO/ParticipantStatusType.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Event/ParticipantStatusType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4a3012f88c67826cb4264a3340e908ec) + * (GenCodeChecksum:6bf827c56f673a914923346816449255) */ /** @@ -103,9 +103,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Participant Status Types'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Participant Status Types') : ts('Participant Status Type'); } /** diff --git a/CRM/Export/Controller/Standalone.php b/CRM/Export/Controller/Standalone.php index f4bb8ca534f6..f95f4b8d84cf 100644 --- a/CRM/Export/Controller/Standalone.php +++ b/CRM/Export/Controller/Standalone.php @@ -50,7 +50,7 @@ public function __construct($title = NULL, $action = CRM_Core_Action::NONE, $mod // add all the actions $this->addActions(); $dao = CRM_Core_DAO_AllCoreTables::getFullName($entity); - CRM_Utils_System::setTitle(ts('Export %1', [1 => $dao::getEntityTitle()])); + CRM_Utils_System::setTitle(ts('Export %1', [1 => $dao::getEntityTitle(TRUE)])); } /** diff --git a/CRM/Financial/DAO/Currency.php b/CRM/Financial/DAO/Currency.php index f96650badc59..868495d6d782 100644 --- a/CRM/Financial/DAO/Currency.php +++ b/CRM/Financial/DAO/Currency.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Financial/Currency.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:5501c59b453dedfb8bba1f2fab44d9ea) + * (GenCodeChecksum:0347ed61e0dc585d250a83f9fa162f2d) */ /** @@ -75,9 +75,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Currencies'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Currencies') : ts('Currency'); } /** diff --git a/CRM/Financial/DAO/EntityFinancialAccount.php b/CRM/Financial/DAO/EntityFinancialAccount.php index 973a99ca8330..6474da983064 100644 --- a/CRM/Financial/DAO/EntityFinancialAccount.php +++ b/CRM/Financial/DAO/EntityFinancialAccount.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Financial/EntityFinancialAccount.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c1d51696dd326b61f65fd064a355e7fb) + * (GenCodeChecksum:04ae90cfa08b3e1380f48c64e0bcc1ce) */ /** @@ -75,9 +75,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Entity Financial Accounts'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Entity Financial Accounts') : ts('Entity Financial Account'); } /** diff --git a/CRM/Financial/DAO/EntityFinancialTrxn.php b/CRM/Financial/DAO/EntityFinancialTrxn.php index 1f959ad6117b..4935cf9f2c31 100644 --- a/CRM/Financial/DAO/EntityFinancialTrxn.php +++ b/CRM/Financial/DAO/EntityFinancialTrxn.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Financial/EntityFinancialTrxn.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:12eb23afdf6c1208bdc01aa7db52770a) + * (GenCodeChecksum:bbf884307aab01daa88aa7f3c06c2624) */ /** @@ -71,9 +71,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Entity Financial Trxns'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Entity Financial Trxns') : ts('Entity Financial Trxn'); } /** diff --git a/CRM/Financial/DAO/FinancialAccount.php b/CRM/Financial/DAO/FinancialAccount.php index d8f161e9437b..30be03ce47cf 100644 --- a/CRM/Financial/DAO/FinancialAccount.php +++ b/CRM/Financial/DAO/FinancialAccount.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Financial/FinancialAccount.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b9f200ff95d9186180eff484dcd12a57) + * (GenCodeChecksum:09d2e3d6a970fa2413b6f29ab1f580e0) */ /** @@ -145,9 +145,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Financial Accounts'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Financial Accounts') : ts('Financial Account'); } /** diff --git a/CRM/Financial/DAO/FinancialItem.php b/CRM/Financial/DAO/FinancialItem.php index a59dc38e6613..dd75f2405a5d 100644 --- a/CRM/Financial/DAO/FinancialItem.php +++ b/CRM/Financial/DAO/FinancialItem.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Financial/FinancialItem.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8bb63ebee681c2eb4acbf8650b224dc2) + * (GenCodeChecksum:a70adfdd0e2248a7583c5c49ad0a5b85) */ /** @@ -115,9 +115,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Financial Items'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Financial Items') : ts('Financial Item'); } /** diff --git a/CRM/Financial/DAO/FinancialTrxn.php b/CRM/Financial/DAO/FinancialTrxn.php index 157984607fb3..e94ee652f483 100644 --- a/CRM/Financial/DAO/FinancialTrxn.php +++ b/CRM/Financial/DAO/FinancialTrxn.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Financial/FinancialTrxn.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:5a4324ffe222bf724ab9d4cde26eb4c2) + * (GenCodeChecksum:857c64b471d1872d98141aefa56aecb6) */ /** @@ -164,9 +164,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Financial Trxns'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Financial Trxns') : ts('Financial Trxn'); } /** diff --git a/CRM/Financial/DAO/FinancialType.php b/CRM/Financial/DAO/FinancialType.php index cd99c56281eb..d9b1ce7f58e4 100644 --- a/CRM/Financial/DAO/FinancialType.php +++ b/CRM/Financial/DAO/FinancialType.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Financial/FinancialType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:024b000d94adcc65200c00d7cef5e624) + * (GenCodeChecksum:36875c58b3267bdc2def295713858868) */ /** @@ -82,9 +82,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Financial Types'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Financial Types') : ts('Financial Type'); } /** diff --git a/CRM/Financial/DAO/PaymentProcessor.php b/CRM/Financial/DAO/PaymentProcessor.php index 7f1e8334a967..f882e0d0b174 100644 --- a/CRM/Financial/DAO/PaymentProcessor.php +++ b/CRM/Financial/DAO/PaymentProcessor.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Financial/PaymentProcessor.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7e296728147d44cb68a9231c4995e461) + * (GenCodeChecksum:8277056355c9f16ae28f69ba3298730e) */ /** @@ -181,9 +181,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Payment Processors'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Payment Processors') : ts('Payment Processor'); } /** diff --git a/CRM/Financial/DAO/PaymentProcessorType.php b/CRM/Financial/DAO/PaymentProcessorType.php index 597b8d27f0ff..3b0144587633 100644 --- a/CRM/Financial/DAO/PaymentProcessorType.php +++ b/CRM/Financial/DAO/PaymentProcessorType.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Financial/PaymentProcessorType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:624a9a001f451b6eb17930a9abcceb3e) + * (GenCodeChecksum:62dd4d3229f289de4afb3c5e3e39db66) */ /** @@ -175,9 +175,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Payment Processor Types'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Payment Processor Types') : ts('Payment Processor Type'); } /** diff --git a/CRM/Financial/DAO/PaymentToken.php b/CRM/Financial/DAO/PaymentToken.php index cb2498f0832c..1608dab55d72 100644 --- a/CRM/Financial/DAO/PaymentToken.php +++ b/CRM/Financial/DAO/PaymentToken.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Financial/PaymentToken.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ce51f1e6eaf6b29f3adeb67828e85507) + * (GenCodeChecksum:2923b7966bbe25d435eb7b909d35541f) */ /** @@ -129,9 +129,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Payment Tokens'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Payment Tokens') : ts('Payment Token'); } /** diff --git a/CRM/Friend/DAO/Friend.php b/CRM/Friend/DAO/Friend.php index 1513ab9dccc5..690419d0b962 100644 --- a/CRM/Friend/DAO/Friend.php +++ b/CRM/Friend/DAO/Friend.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Friend/Friend.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:3f1c976d43e312175e85da0427f5210d) + * (GenCodeChecksum:1452e2f2b01782f26a30e94bd6af3783) */ /** @@ -106,9 +106,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Friends'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Friends') : ts('Friend'); } /** diff --git a/CRM/Grant/DAO/Grant.php b/CRM/Grant/DAO/Grant.php index 91b816359a75..05d6701011e6 100644 --- a/CRM/Grant/DAO/Grant.php +++ b/CRM/Grant/DAO/Grant.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Grant/Grant.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a2e43b7f0fb8547daf5ed874bf6174c5) + * (GenCodeChecksum:91aecd5b45ba8c5cd6636bb95ddbbfee) */ /** @@ -152,9 +152,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Grants'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Grants') : ts('Grant'); } /** diff --git a/CRM/Mailing/DAO/BouncePattern.php b/CRM/Mailing/DAO/BouncePattern.php index 43822bf7c5e3..8c74d99ecffa 100644 --- a/CRM/Mailing/DAO/BouncePattern.php +++ b/CRM/Mailing/DAO/BouncePattern.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/BouncePattern.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:90abbaf8e68b5749a084a74d77dcc3b7) + * (GenCodeChecksum:65ffd17da4de88b093e578fc52d42800) */ /** @@ -59,9 +59,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Bounce Patterns'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Bounce Patterns') : ts('Bounce Pattern'); } /** diff --git a/CRM/Mailing/DAO/BounceType.php b/CRM/Mailing/DAO/BounceType.php index a31f8324f4fe..6511adf6d2d7 100644 --- a/CRM/Mailing/DAO/BounceType.php +++ b/CRM/Mailing/DAO/BounceType.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/BounceType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4e77659bd433033396e84b6de32c99af) + * (GenCodeChecksum:41a2f33a589d43ba388d36dcf8f9e54f) */ /** @@ -66,9 +66,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Bounce Types'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Bounce Types') : ts('Bounce Type'); } /** diff --git a/CRM/Mailing/DAO/Mailing.php b/CRM/Mailing/DAO/Mailing.php index 084489593dcb..95b4563035c6 100644 --- a/CRM/Mailing/DAO/Mailing.php +++ b/CRM/Mailing/DAO/Mailing.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Mailing.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6a9dc5aaff7aa7f5dcfe3f892255e357) + * (GenCodeChecksum:9cd784dc86cf4f54983f14440be05239) */ /** @@ -342,9 +342,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Mailings'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Mailings') : ts('Mailing'); } /** diff --git a/CRM/Mailing/DAO/MailingAB.php b/CRM/Mailing/DAO/MailingAB.php index 0e2215e1fc5f..1932930dc264 100644 --- a/CRM/Mailing/DAO/MailingAB.php +++ b/CRM/Mailing/DAO/MailingAB.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/MailingAB.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:af0f7d34ddde7f3971aaac5abccfcd8c) + * (GenCodeChecksum:075e135a610dcb619ab3ddc8c23944e1) */ /** @@ -130,9 +130,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Mailing ABs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Mailing ABs') : ts('Mailing AB'); } /** diff --git a/CRM/Mailing/DAO/MailingComponent.php b/CRM/Mailing/DAO/MailingComponent.php index f1b81f15629d..b544c0ea2ef9 100644 --- a/CRM/Mailing/DAO/MailingComponent.php +++ b/CRM/Mailing/DAO/MailingComponent.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/MailingComponent.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ca95f8566048836c03e1dc58eb51ac11) + * (GenCodeChecksum:bb6ab1e7538409ecfa1e72a45bb287a1) */ /** @@ -92,9 +92,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Mailing Components'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Mailing Components') : ts('Mailing Component'); } /** diff --git a/CRM/Mailing/DAO/MailingGroup.php b/CRM/Mailing/DAO/MailingGroup.php index 220bfd13cb08..0d3497c6cdeb 100644 --- a/CRM/Mailing/DAO/MailingGroup.php +++ b/CRM/Mailing/DAO/MailingGroup.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/MailingGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a253e806fcb595ede70c812a10c0dbba) + * (GenCodeChecksum:4ab3dccb8706ec8f03072118ff2e9c12) */ /** @@ -87,9 +87,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Mailing Groups'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Mailing Groups') : ts('Mailing Group'); } /** diff --git a/CRM/Mailing/DAO/MailingJob.php b/CRM/Mailing/DAO/MailingJob.php index bde7e35f391e..edbd20cd5f9a 100644 --- a/CRM/Mailing/DAO/MailingJob.php +++ b/CRM/Mailing/DAO/MailingJob.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/MailingJob.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:0c6e76df20fe3579056c287aeed27cdb) + * (GenCodeChecksum:eb066e39ba2501608759e6ac3dfbcaaf) */ /** @@ -115,9 +115,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Mailing Jobs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Mailing Jobs') : ts('Mailing Job'); } /** diff --git a/CRM/Mailing/DAO/Recipients.php b/CRM/Mailing/DAO/Recipients.php index 1852c7ac022a..7e6bb43ab156 100644 --- a/CRM/Mailing/DAO/Recipients.php +++ b/CRM/Mailing/DAO/Recipients.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Recipients.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ebd2ec177861f8f82a4bc6bc8b33fd9b) + * (GenCodeChecksum:3fb8b7a18899cd7dba958e9e08a944a7) */ /** @@ -73,9 +73,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Recipientses'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Recipientses') : ts('Recipients'); } /** diff --git a/CRM/Mailing/DAO/Spool.php b/CRM/Mailing/DAO/Spool.php index 6027afd1676c..6329553862b5 100644 --- a/CRM/Mailing/DAO/Spool.php +++ b/CRM/Mailing/DAO/Spool.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Spool.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7bd4a9b64175915a43f602f4f9cfb721) + * (GenCodeChecksum:f2aa2d8c8ee3203bf674e6c193818dda) */ /** @@ -87,9 +87,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Spools'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Spools') : ts('Spool'); } /** diff --git a/CRM/Mailing/DAO/TrackableURL.php b/CRM/Mailing/DAO/TrackableURL.php index 3d3963439726..0452ac93ba61 100644 --- a/CRM/Mailing/DAO/TrackableURL.php +++ b/CRM/Mailing/DAO/TrackableURL.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/TrackableURL.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:74f858b4e9e666e05416be884002408b) + * (GenCodeChecksum:ac0ae37c12d442071e726deb75e8a7ba) */ /** @@ -59,9 +59,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Trackable URLs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Trackable URLs') : ts('Trackable URL'); } /** diff --git a/CRM/Mailing/Event/DAO/Bounce.php b/CRM/Mailing/Event/DAO/Bounce.php index 1cd4024fb4d6..7252307252f7 100644 --- a/CRM/Mailing/Event/DAO/Bounce.php +++ b/CRM/Mailing/Event/DAO/Bounce.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Event/Bounce.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8e0590dde97f57494203397255fd4604) + * (GenCodeChecksum:dc1c97304810848da9a9c4255456799f) */ /** @@ -73,9 +73,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Bounces'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Bounces') : ts('Bounce'); } /** diff --git a/CRM/Mailing/Event/DAO/Confirm.php b/CRM/Mailing/Event/DAO/Confirm.php index 343430c1579d..3c757f302d5c 100644 --- a/CRM/Mailing/Event/DAO/Confirm.php +++ b/CRM/Mailing/Event/DAO/Confirm.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Event/Confirm.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:827b011dc50d032e8b74d6d164314d83) + * (GenCodeChecksum:3d1dae89470e79336dab7506dd173499) */ /** @@ -59,9 +59,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Confirms'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Confirms') : ts('Confirm'); } /** diff --git a/CRM/Mailing/Event/DAO/Delivered.php b/CRM/Mailing/Event/DAO/Delivered.php index 147d70fcf82f..93bb85e2d534 100644 --- a/CRM/Mailing/Event/DAO/Delivered.php +++ b/CRM/Mailing/Event/DAO/Delivered.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Event/Delivered.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c983e11b4de5a1c4e6d9765eb7d12755) + * (GenCodeChecksum:6a81ffa6ed10a254979274ea5edbd855) */ /** @@ -59,9 +59,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Delivereds'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Delivereds') : ts('Delivered'); } /** diff --git a/CRM/Mailing/Event/DAO/Forward.php b/CRM/Mailing/Event/DAO/Forward.php index ac1c8d89670b..6650dd0c0cb9 100644 --- a/CRM/Mailing/Event/DAO/Forward.php +++ b/CRM/Mailing/Event/DAO/Forward.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Event/Forward.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:359e0b700860c29a1e809fd4acbf7598) + * (GenCodeChecksum:16bd96b998ed8316250158886d0dc7c8) */ /** @@ -66,9 +66,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Forwards'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Forwards') : ts('Forward'); } /** diff --git a/CRM/Mailing/Event/DAO/Opened.php b/CRM/Mailing/Event/DAO/Opened.php index cbd24372dfa0..9ce62afcf5c2 100644 --- a/CRM/Mailing/Event/DAO/Opened.php +++ b/CRM/Mailing/Event/DAO/Opened.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Event/Opened.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:dddc76ba8461f8b0c1f3c1cdccddd111) + * (GenCodeChecksum:d636f34b01a876a72874c4b86abacff4) */ /** @@ -59,9 +59,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Openeds'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Openeds') : ts('Opened'); } /** diff --git a/CRM/Mailing/Event/DAO/Queue.php b/CRM/Mailing/Event/DAO/Queue.php index 073ef521c997..4dd92f1047ee 100644 --- a/CRM/Mailing/Event/DAO/Queue.php +++ b/CRM/Mailing/Event/DAO/Queue.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Event/Queue.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:091cb300f1b0a67dfaf40f988806e6cf) + * (GenCodeChecksum:f37d1a6e35b4f827ed7daf29b3005fad) */ /** @@ -80,9 +80,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Queues'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Queues') : ts('Queue'); } /** diff --git a/CRM/Mailing/Event/DAO/Reply.php b/CRM/Mailing/Event/DAO/Reply.php index 8fc31b3eb359..3c3cb9cff782 100644 --- a/CRM/Mailing/Event/DAO/Reply.php +++ b/CRM/Mailing/Event/DAO/Reply.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Event/Reply.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b1d572f3d42f6480dc98a2e6f9710fa3) + * (GenCodeChecksum:e4f80247fbee550b68997bd9f264dc66) */ /** @@ -59,9 +59,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Replies'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Replies') : ts('Reply'); } /** diff --git a/CRM/Mailing/Event/DAO/Subscribe.php b/CRM/Mailing/Event/DAO/Subscribe.php index ce577b24d76a..6659fcca1de2 100644 --- a/CRM/Mailing/Event/DAO/Subscribe.php +++ b/CRM/Mailing/Event/DAO/Subscribe.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Event/Subscribe.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:9e1dec99f17dcccde7feeca30b880a85) + * (GenCodeChecksum:b5a2a60211ad6eb943a6aa0db5f88840) */ /** @@ -73,9 +73,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Subscribes'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Subscribes') : ts('Subscribe'); } /** diff --git a/CRM/Mailing/Event/DAO/TrackableURLOpen.php b/CRM/Mailing/Event/DAO/TrackableURLOpen.php index 88c84b0b9201..85f4b96810e1 100644 --- a/CRM/Mailing/Event/DAO/TrackableURLOpen.php +++ b/CRM/Mailing/Event/DAO/TrackableURLOpen.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Event/TrackableURLOpen.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b543a83312f2069a45872939517aa480) + * (GenCodeChecksum:6e1eb0a358ed16c691ea25a741ce6f8c) */ /** @@ -66,9 +66,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Trackable URLOpens'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Trackable URLOpens') : ts('Trackable URLOpen'); } /** diff --git a/CRM/Mailing/Event/DAO/Unsubscribe.php b/CRM/Mailing/Event/DAO/Unsubscribe.php index e5a230b66dc7..71432c59ab11 100644 --- a/CRM/Mailing/Event/DAO/Unsubscribe.php +++ b/CRM/Mailing/Event/DAO/Unsubscribe.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Mailing/Event/Unsubscribe.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:2d080a63032c9dce0331a6ed4f6c3cd2) + * (GenCodeChecksum:48ace30af145c288185e356b721a22d1) */ /** @@ -66,9 +66,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Unsubscribes'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Unsubscribes') : ts('Unsubscribe'); } /** diff --git a/CRM/Member/DAO/Membership.php b/CRM/Member/DAO/Membership.php index 384c69ae6b58..86173b83afcc 100644 --- a/CRM/Member/DAO/Membership.php +++ b/CRM/Member/DAO/Membership.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Member/Membership.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:835c63ea0a55b78d6d115a7a6db5dde2) + * (GenCodeChecksum:d80be256fb175b763047883b8694559c) */ /** @@ -153,9 +153,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Memberships'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Memberships') : ts('Membership'); } /** diff --git a/CRM/Member/DAO/MembershipBlock.php b/CRM/Member/DAO/MembershipBlock.php index 91e65e6eeb2b..9bfff5263961 100644 --- a/CRM/Member/DAO/MembershipBlock.php +++ b/CRM/Member/DAO/MembershipBlock.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Member/MembershipBlock.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8eb2f3a6c818d449da875421b54de619) + * (GenCodeChecksum:6a38472a1103f6f5de9e33fed620060d) */ /** @@ -131,9 +131,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Membership Blocks'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Membership Blocks') : ts('Membership Block'); } /** diff --git a/CRM/Member/DAO/MembershipLog.php b/CRM/Member/DAO/MembershipLog.php index bd0b4f7924e5..1619acbedb59 100644 --- a/CRM/Member/DAO/MembershipLog.php +++ b/CRM/Member/DAO/MembershipLog.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Member/MembershipLog.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4d5744b433ca7bb5385b11945cc0fe10) + * (GenCodeChecksum:d1e55c6d7f0e93b21778fdd36dd6e022) */ /** @@ -101,9 +101,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Membership Logs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Membership Logs') : ts('Membership Log'); } /** diff --git a/CRM/Member/DAO/MembershipPayment.php b/CRM/Member/DAO/MembershipPayment.php index ac386eee5571..67e9bc605714 100644 --- a/CRM/Member/DAO/MembershipPayment.php +++ b/CRM/Member/DAO/MembershipPayment.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Member/MembershipPayment.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:39168603c262c909ebeee2ce821f0f0d) + * (GenCodeChecksum:e9764dc46ae261f8cad8ef1383063d61) */ /** @@ -59,9 +59,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Membership Payments'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Membership Payments') : ts('Membership Payment'); } /** diff --git a/CRM/Member/DAO/MembershipStatus.php b/CRM/Member/DAO/MembershipStatus.php index 5b780e4a5b9d..5857dca3cf5d 100644 --- a/CRM/Member/DAO/MembershipStatus.php +++ b/CRM/Member/DAO/MembershipStatus.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Member/MembershipStatus.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:f0c470d5aca6e3696a0ad8345531f8b8) + * (GenCodeChecksum:6bdad135e9f0e94f085296d68ca59253) */ /** @@ -143,9 +143,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Membership Statuses'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Membership Statuses') : ts('Membership Status'); } /** diff --git a/CRM/Member/DAO/MembershipType.php b/CRM/Member/DAO/MembershipType.php index 4f5cad0e296a..d904024ac477 100644 --- a/CRM/Member/DAO/MembershipType.php +++ b/CRM/Member/DAO/MembershipType.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Member/MembershipType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7c5220444512f1aa499454e977f27814) + * (GenCodeChecksum:db300e3a7f5ca29a1b6ca43e0885bcb5) */ /** @@ -181,9 +181,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Membership Types'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Membership Types') : ts('Membership Type'); } /** diff --git a/CRM/PCP/DAO/PCP.php b/CRM/PCP/DAO/PCP.php index eb5656967545..bd3fedb7800f 100644 --- a/CRM/PCP/DAO/PCP.php +++ b/CRM/PCP/DAO/PCP.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/PCP/PCP.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:286225e46c4e2f3c12b17cd5f83b210d) + * (GenCodeChecksum:2232571099c216fb7a823556f5bd8328) */ /** @@ -138,9 +138,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('PCPs'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('PCPs') : ts('PCP'); } /** diff --git a/CRM/PCP/DAO/PCPBlock.php b/CRM/PCP/DAO/PCPBlock.php index 74f5204bebd1..711dc584c959 100644 --- a/CRM/PCP/DAO/PCPBlock.php +++ b/CRM/PCP/DAO/PCPBlock.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/PCP/PCPBlock.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ea1b4158570c5a79356b1dc0ad80db6a) + * (GenCodeChecksum:2039aecf7902bf0fda7f91081778b1c8) */ /** @@ -129,9 +129,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('PCPBlocks'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('PCPBlocks') : ts('PCPBlock'); } /** diff --git a/CRM/Pledge/DAO/Pledge.php b/CRM/Pledge/DAO/Pledge.php index 5f5a7632ed42..2a2f80900d68 100644 --- a/CRM/Pledge/DAO/Pledge.php +++ b/CRM/Pledge/DAO/Pledge.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Pledge/Pledge.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:27003a5c2de79b60b4114bc92b65cc07) + * (GenCodeChecksum:0447b8be2ab956b77d0d6be123e0bdb4) */ /** @@ -206,9 +206,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Pledges'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Pledges') : ts('Pledge'); } /** diff --git a/CRM/Pledge/DAO/PledgeBlock.php b/CRM/Pledge/DAO/PledgeBlock.php index 989b14383d7a..111d085910e9 100644 --- a/CRM/Pledge/DAO/PledgeBlock.php +++ b/CRM/Pledge/DAO/PledgeBlock.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Pledge/PledgeBlock.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:bf3640355f445e127c25402500d79668) + * (GenCodeChecksum:2b02296af6d4e280950d483d222b20b9) */ /** @@ -117,9 +117,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Pledge Blocks'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Pledge Blocks') : ts('Pledge Block'); } /** diff --git a/CRM/Pledge/DAO/PledgePayment.php b/CRM/Pledge/DAO/PledgePayment.php index 234a5342b2f7..bb88db54a733 100644 --- a/CRM/Pledge/DAO/PledgePayment.php +++ b/CRM/Pledge/DAO/PledgePayment.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Pledge/PledgePayment.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c47a2cbc83c672a8209bc5e725b2f81a) + * (GenCodeChecksum:b3b6719f4f4a8c441f271213ff7f52db) */ /** @@ -106,9 +106,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Pledge Payments'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Pledge Payments') : ts('Pledge Payment'); } /** diff --git a/CRM/Price/DAO/LineItem.php b/CRM/Price/DAO/LineItem.php index 2ad0da2b2957..7e9b779089b6 100644 --- a/CRM/Price/DAO/LineItem.php +++ b/CRM/Price/DAO/LineItem.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Price/LineItem.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7403b3615b0225350d893750a547061a) + * (GenCodeChecksum:5a365a7d4b14c9b5963aeb61576395e4) */ /** @@ -138,9 +138,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Line Items'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Line Items') : ts('Line Item'); } /** diff --git a/CRM/Price/DAO/PriceField.php b/CRM/Price/DAO/PriceField.php index e14f60999bfe..2f5d0d653ae9 100644 --- a/CRM/Price/DAO/PriceField.php +++ b/CRM/Price/DAO/PriceField.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Price/PriceField.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1492c6421f1c3cb49dcab88bc411075c) + * (GenCodeChecksum:fdebc45a7d5c35abb0c9c480dbf201b2) */ /** @@ -157,9 +157,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Price Fields'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Price Fields') : ts('Price Field'); } /** diff --git a/CRM/Price/DAO/PriceFieldValue.php b/CRM/Price/DAO/PriceFieldValue.php index c7c8e0913e40..2057dd655d56 100644 --- a/CRM/Price/DAO/PriceFieldValue.php +++ b/CRM/Price/DAO/PriceFieldValue.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Price/PriceFieldValue.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a1acc613daec86c6049e545af5fc7fd1) + * (GenCodeChecksum:16987d2cc463a2d9290339ad37070f16) */ /** @@ -166,9 +166,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Price Field Values'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Price Field Values') : ts('Price Field Value'); } /** diff --git a/CRM/Price/DAO/PriceSet.php b/CRM/Price/DAO/PriceSet.php index 1dd84351706a..26fa13809836 100644 --- a/CRM/Price/DAO/PriceSet.php +++ b/CRM/Price/DAO/PriceSet.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Price/PriceSet.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:52d1fb1b25eaa8f1c157012bfec0eaae) + * (GenCodeChecksum:c4db3cbda4900e01aeba098a4578b563) */ /** @@ -131,9 +131,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Price Sets'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Price Sets') : ts('Price Set'); } /** diff --git a/CRM/Price/DAO/PriceSetEntity.php b/CRM/Price/DAO/PriceSetEntity.php index 78e479a53a75..4b1d3749a55d 100644 --- a/CRM/Price/DAO/PriceSetEntity.php +++ b/CRM/Price/DAO/PriceSetEntity.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Price/PriceSetEntity.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:f2d6aeda95e4bde969d5ccebe9f26791) + * (GenCodeChecksum:0284d1a184dd635d93dd170e94451e86) */ /** @@ -68,9 +68,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Price Set Entities'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Price Set Entities') : ts('Price Set Entity'); } /** diff --git a/CRM/Queue/DAO/QueueItem.php b/CRM/Queue/DAO/QueueItem.php index a78b613e154d..7ee05fcafa74 100644 --- a/CRM/Queue/DAO/QueueItem.php +++ b/CRM/Queue/DAO/QueueItem.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Queue/QueueItem.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7e484400a7f8cf682b9c85e8b10c7bc7) + * (GenCodeChecksum:32350e9450942f29e7785ceaaed7a1b9) */ /** @@ -78,9 +78,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Queue Items'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Queue Items') : ts('Queue Item'); } /** diff --git a/CRM/Report/DAO/ReportInstance.php b/CRM/Report/DAO/ReportInstance.php index 08a380c76fd3..f4ab9447035f 100644 --- a/CRM/Report/DAO/ReportInstance.php +++ b/CRM/Report/DAO/ReportInstance.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Report/ReportInstance.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d0c9e5593f161f18e7979012c4c13724) + * (GenCodeChecksum:9c05a0739ee0b8250fdc0418b5fb247d) */ /** @@ -192,9 +192,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Reports'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Reports') : ts('Report'); } /** diff --git a/CRM/SMS/DAO/Provider.php b/CRM/SMS/DAO/Provider.php index 3aa49ab0a6da..afdd808359ac 100644 --- a/CRM/SMS/DAO/Provider.php +++ b/CRM/SMS/DAO/Provider.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/SMS/Provider.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6ecda65bd52b36e04764cec8ee81e1b8) + * (GenCodeChecksum:478e0d0caeb2d2a81068db6ad2db67aa) */ /** @@ -107,9 +107,12 @@ public function __construct() { /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() { - return ts('Providers'); + public static function getEntityTitle($plural = FALSE) { + return $plural ? ts('Providers') : ts('Provider'); } /** diff --git a/Civi/Api4/Generic/AbstractEntity.php b/Civi/Api4/Generic/AbstractEntity.php index 269bcc31b3c9..8084614b14ef 100644 --- a/Civi/Api4/Generic/AbstractEntity.php +++ b/Civi/Api4/Generic/AbstractEntity.php @@ -81,9 +81,11 @@ protected static function getEntityName() { /** * Overridable function to return a localized title for this entity. * + * @param bool $plural + * Whether to return a plural title. * @return string */ - protected static function getEntityTitle() { + protected static function getEntityTitle($plural = FALSE) { return static::getEntityName(); } @@ -121,6 +123,7 @@ public static function getInfo() { $info = [ 'name' => static::getEntityName(), 'title' => static::getEntityTitle(), + 'titlePlural' => static::getEntityTitle(TRUE), 'type' => self::stripNamespace(get_parent_class(static::class)), ]; $reflection = new \ReflectionClass(static::class); diff --git a/Civi/Api4/Generic/DAOEntity.php b/Civi/Api4/Generic/DAOEntity.php index a7c47ee2e9a1..c27aac4ce0c3 100644 --- a/Civi/Api4/Generic/DAOEntity.php +++ b/Civi/Api4/Generic/DAOEntity.php @@ -91,12 +91,14 @@ public static function replace($checkPermissions = TRUE) { } /** + * @param bool $plural + * Whether to return a plural title. * @return string */ - protected static function getEntityTitle() { + protected static function getEntityTitle($plural = FALSE) { $name = static::getEntityName(); $dao = \CRM_Core_DAO_AllCoreTables::getFullName($name); - return $dao ? $dao::getEntityTitle() : $name; + return $dao ? $dao::getEntityTitle($plural) : $name; } /** diff --git a/xml/schema/Contribute/ContributionRecur.xml b/xml/schema/Contribute/ContributionRecur.xml index 11e70b6afc49..00411d639be8 100644 --- a/xml/schema/Contribute/ContributionRecur.xml +++ b/xml/schema/Contribute/ContributionRecur.xml @@ -5,7 +5,7 @@ civicrm_contribution_recur 1.6 true - Recurring Contributions + Recurring Contribution id contribution_recur_id diff --git a/xml/schema/Core/IM.xml b/xml/schema/Core/IM.xml index f7738d969cbc..58ca90324dc4 100644 --- a/xml/schema/Core/IM.xml +++ b/xml/schema/Core/IM.xml @@ -8,6 +8,7 @@ 1.1 true Instant Messaging + Instant Messaging fa-comments-o id diff --git a/xml/schema/Report/ReportInstance.xml b/xml/schema/Report/ReportInstance.xml index 028c6fb722e8..d85b605c5d91 100644 --- a/xml/schema/Report/ReportInstance.xml +++ b/xml/schema/Report/ReportInstance.xml @@ -6,7 +6,7 @@ civicrm_report_instance Users can save their report instance and put in a cron tab etc. 2.2 - Reports + Report fa-bar-chart id diff --git a/xml/templates/dao.tpl b/xml/templates/dao.tpl index 87a3463531c9..034137433ed4 100644 --- a/xml/templates/dao.tpl +++ b/xml/templates/dao.tpl @@ -61,9 +61,12 @@ class {$table.className} extends CRM_Core_DAO {ldelim} /** * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. */ - public static function getEntityTitle() {ldelim} - return {$tsFunctionName}('{$table.title}'); + public static function getEntityTitle($plural = FALSE) {ldelim} + return $plural ? {$tsFunctionName}('{$table.titlePlural}') : {$tsFunctionName}('{$table.title}'); {rdelim} From f2425270f169759c09d159bc9dfa1d65530cf0f5 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sun, 13 Sep 2020 13:21:16 -0400 Subject: [PATCH 237/834] Search ext: update to plural entity titles --- ext/search/CRM/Search/Page/Ang.php | 4 ++-- ext/search/ang/search/crmSearch.component.js | 10 +++++----- ext/search/ang/search/crmSearch.html | 2 +- ext/search/ang/search/crmSearchActions.component.js | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/search/CRM/Search/Page/Ang.php b/ext/search/CRM/Search/Page/Ang.php index b6a03e2bbf76..2549f622d5b0 100644 --- a/ext/search/CRM/Search/Page/Ang.php +++ b/ext/search/CRM/Search/Page/Ang.php @@ -83,9 +83,9 @@ private function getOperators() { */ private function getSchema() { $schema = \Civi\Api4\Entity::get() - ->addSelect('name', 'title', 'description', 'icon') + ->addSelect('name', 'titlePlural', 'description', 'icon') ->addWhere('name', '!=', 'Entity') - ->addOrderBy('title') + ->addOrderBy('titlePlural') ->setChain([ 'get' => ['$name', 'getActions', ['where' => [['name', '=', 'get']]], ['params']], ])->execute(); diff --git a/ext/search/ang/search/crmSearch.component.js b/ext/search/ang/search/crmSearch.component.js index 51f2532bc49a..66a14f0f2c10 100644 --- a/ext/search/ang/search/crmSearch.component.js +++ b/ext/search/ang/search/crmSearch.component.js @@ -23,7 +23,7 @@ $scope.controls = {}; $scope.joinTypes = [{k: false, v: ts('Optional')}, {k: true, v: ts('Required')}]; - $scope.entities = formatForSelect2(CRM.vars.search.schema, 'name', 'title', ['description', 'icon']); + $scope.entities = formatForSelect2(CRM.vars.search.schema, 'name', 'titlePlural', ['description', 'icon']); this.perm = { editGroups: CRM.checkPerm('edit groups') }; @@ -40,7 +40,7 @@ if (entity) { joinEntities.push({ id: link.entity + ' AS ' + link.alias, - text: entity.title, + text: entity.titlePlural, description: '(' + link.alias + ')', icon: entity.icon }); @@ -408,7 +408,7 @@ var mainEntity = searchMeta.getEntity(ctrl.entity), result = [{ - text: mainEntity.title, + text: mainEntity.titlePlural, icon: mainEntity.icon, children: formatFields(ctrl.entity, '') }]; @@ -416,7 +416,7 @@ var joinName = join[0].split(' AS '), joinEntity = searchMeta.getEntity(joinName[0]); result.push({ - text: joinEntity.title + ' (' + joinName[1] + ')', + text: joinEntity.titlePlural + ' (' + joinName[1] + ')', icon: joinEntity.icon, children: formatFields(joinEntity.name, joinName[1] + '.') }); @@ -529,7 +529,7 @@ $scope.saveGroup = function() { var selectField = ctrl.entity === 'Contact' ? 'id' : 'contact_id'; if (ctrl.entity !== 'Contact' && !searchMeta.getField('contact_id')) { - CRM.alert(ts('Cannot create smart group from %1.', {1: searchMeta.getEntity(true).title}), ts('Missing contact_id'), 'error', {expires: 5000}); + CRM.alert(ts('Cannot create smart group from %1.', {1: searchMeta.getEntity(true).titlePlural}), ts('Missing contact_id'), 'error', {expires: 5000}); return; } var model = { diff --git a/ext/search/ang/search/crmSearch.html b/ext/search/ang/search/crmSearch.html index bd0feabcba10..3500ef31c7a8 100644 --- a/ext/search/ang/search/crmSearch.html +++ b/ext/search/ang/search/crmSearch.html @@ -1,5 +1,5 @@