Merge pull request #17332 from colemanw/api4Joins
authorEileen McNaughton <emcnaughton@wikimedia.org>
Tue, 19 May 2020 21:21:14 +0000 (09:21 +1200)
committerGitHub <noreply@github.com>
Tue, 19 May 2020 21:21:14 +0000 (09:21 +1200)
APIv4 - Remove implicit multi joins & add explicit joins

120 files changed:
CRM/Activity/Page/AJAX.php
CRM/Admin/Form/Preferences/Mailing.php
CRM/Admin/Page/APIExplorer.php
CRM/Case/Info.php
CRM/Case/Page/Tab.php
CRM/Case/XMLProcessor/Process.php
CRM/Contact/Import/Parser.php
CRM/Contribute/BAO/Contribution.php
CRM/Contribute/Form/ContributionBase.php
CRM/Contribute/Form/Task/Status.php
CRM/Core/Action.php
CRM/Core/BAO/ActionSchedule.php
CRM/Core/BAO/Address.php
CRM/Core/BAO/Block.php
CRM/Core/BAO/Cache.php
CRM/Core/BAO/CustomField.php
CRM/Core/BAO/CustomOption.php
CRM/Core/BAO/CustomValueTable.php
CRM/Core/BAO/Discount.php
CRM/Core/BAO/File.php
CRM/Core/BAO/Job.php
CRM/Core/BAO/LabelFormat.php
CRM/Core/BAO/Location.php
CRM/Core/BAO/Mapping.php
CRM/Core/BAO/PaperSize.php
CRM/Core/BAO/PdfFormat.php
CRM/Core/BAO/PreferencesDate.php
CRM/Core/BAO/RecurringEntity.php
CRM/Core/BAO/SchemaHandler.php
CRM/Core/BAO/StatusPreference.php
CRM/Core/BAO/UFGroup.php
CRM/Core/BAO/UFMatch.php
CRM/Core/BAO/WordReplacement.php
CRM/Core/Component.php
CRM/Core/DAO.php
CRM/Core/DAO/AllCoreTables.php
CRM/Core/DAO/Factory.php
CRM/Core/Form.php
CRM/Core/I18n/Form.php
CRM/Core/Invoke.php
CRM/Core/Menu.php
CRM/Core/Page.php
CRM/Core/Page/Redirect.php
CRM/Core/Payment/BaseIPN.php
CRM/Core/Permission/Base.php
CRM/Core/PseudoConstant.php
CRM/Core/Region.php
CRM/Core/Smarty.php
CRM/Core/Smarty/plugins/block.crmScope.php
CRM/Core/Smarty/plugins/block.icon.php
CRM/Core/Smarty/plugins/function.crmVersion.php
CRM/Core/TemporaryErrorScope.php
CRM/Core/Transaction.php
CRM/Event/Form/Registration/Confirm.php
CRM/Mailing/BAO/Mailing.php
CRM/Queue/ErrorPolicy.php
CRM/Queue/Service.php
CRM/Report/Form/Contribute/Detail.php
CRM/Utils/API/MatchOption.php
CRM/Utils/API/ReloadOption.php
CRM/Utils/AutoClean.php
CRM/Utils/ConsoleTee.php
CRM/Utils/FakeObject.php
CRM/Utils/GlobalStack.php
CRM/Utils/Hook.php
CRM/Utils/Migrate/Export.php
CRM/Utils/SQL/BaseParamQuery.php
CRM/Utils/SQL/Delete.php
CRM/Utils/SQL/Select.php
CRM/Utils/Signer.php
CRM/Utils/String.php
CRM/Utils/System/Drupal8.php
Civi.php
Civi/API/Request.php
Civi/API/Subscriber/ChainSubscriber.php
Civi/API/Subscriber/DynamicFKAuthorization.php
Civi/API/WhitelistRule.php
Civi/ActionSchedule/Event/MailingQueryEvent.php
Civi/ActionSchedule/RecipientBuilder.php
Civi/Angular/AngularLoader.php
Civi/Api4/Generic/AbstractEntity.php
Civi/Core/AssetBuilder.php
Civi/Core/CiviEventInspector.php
Civi/Core/Event/GenericHookEvent.php
Civi/Core/Resolver.php
Civi/Install/Requirements.php
Civi/Test.php
Civi/Test/Api3TestTrait.php
Civi/Test/HookInterface.php
Civi/Token/Event/TokenRegisterEvent.php
Civi/Token/Event/TokenValueEvent.php
Civi/Token/TokenRow.php
api/api.php
api/class.api.php
api/v3/Attachment.php
api/v3/Case.php
api/v3/CustomValue.php
api/v3/GroupContact.php
api/v3/Mailing.php
api/v3/utils.php
i/TreeMinus.gif [deleted file]
i/TreeMinusWhite.gif [deleted file]
i/TreePlus.gif [deleted file]
i/TreePlusWhite.gif [deleted file]
settings/Mailing.setting.php
templates/CRM/Admin/Form/Setting/Smtp.tpl
templates/CRM/Campaign/Form/Task/Interview.tpl
templates/CRM/Case/Form/ActivityToCase.tpl
templates/CRM/Case/Page/DashboardSelector.tpl
templates/CRM/Event/Form/ManageEvent/Fee.tpl
templates/CRM/Mailing/Form/ForwardMailing.tpl
templates/CRM/Report/Form/Actions.tpl
templates/CRM/Report/Form/Tabs/OrderBy.tpl
tests/phpunit/CRM/Activity/Page/AJAXTest.php [new file with mode: 0644]
tests/phpunit/CRM/Core/DAO/AllCoreTablesTest.php
tests/phpunit/CRM/Core/PageTest.php [new file with mode: 0644]
tests/phpunit/CRM/Logging/SchemaTest.php
tests/phpunit/CRM/Report/FormTest.php
tests/phpunit/CRM/Utils/DateTest.php
tests/phpunit/CiviTest/CiviUnitTestCase.php

index e652726a608d756508da8738964649b15401f588..c7fe505bc48a3fce3b4d7af2e075b87c2a799e79 100644 (file)
@@ -353,7 +353,7 @@ class CRM_Activity_Page_AJAX {
     if (!empty($params['assigneeContactIds'])) {
       $assigneeContacts = array_unique(explode(',', $params['assigneeContactIds']));
     }
-    foreach ($assigneeContacts as $key => $value) {
+    foreach ($assigneeContacts as $value) {
       $assigneeParams = [
         'activity_id' => $mainActivityId,
         'contact_id' => $value,
index d786297fe9296893349ddd659e4b0b0638f9b5d3..b2f4a975dc32a8317860a6e3b5a519604e1d1870 100644 (file)
@@ -33,6 +33,8 @@ class CRM_Admin_Form_Preferences_Mailing extends CRM_Admin_Form_Preferences {
     'dedupe_email_default' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
     'hash_mailing_url' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
     'auto_recipient_rebuild' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'url_tracking_default' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'open_tracking_default' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
   ];
 
   public function postProcess() {
index 13363ee23122a5dc8e84f61e87f7cc2713acc5b9..c769535be31717c3635ce8370a3e366faba915a8 100644 (file)
@@ -177,7 +177,7 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
     // Fetch block for a specific action
     else {
       $action = strtolower($action);
-      $fnName = 'civicrm_api3_' . _civicrm_api_get_entity_name_from_camel($entity) . '_' . $action;
+      $fnName = 'civicrm_api3_' . CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($entity) . '_' . $action;
       // Support the alternate "1 file per action" structure
       $actionFile = "api/v3/$entity/" . ucfirst($action) . '.php';
       $actionFileContents = file_get_contents("api/v3/$entity/" . ucfirst($action) . '.php', FILE_USE_INCLUDE_PATH);
@@ -205,7 +205,8 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
 
   /**
    * Format a docblock to be a bit more readable
-   * Not a proper doc parser... patches welcome :)
+   *
+   * FIXME: APIv4 uses markdown in code docs. Switch to that.
    *
    * @param string $text
    * @return string
@@ -224,8 +225,8 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
 
     // Extract code blocks - save for later to skip html conversion
     $code = [];
-    preg_match_all('#@code(.*?)@endcode#is', $text, $code);
-    $text = preg_replace('#@code.*?@endcode#is', '<pre></pre>', $text);
+    preg_match_all('#(@code|```)(.*?)(@endcode|```)#is', $text, $code);
+    $text = preg_replace('#(@code|```)(.*?)(@endcode|```)#is', '<pre></pre>', $text);
 
     // Convert @annotations to titles
     $text = preg_replace_callback(
@@ -242,8 +243,8 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
     $text = nl2br($text);
 
     // Add unformatted code blocks back in
-    if ($code && !empty($code[1])) {
-      foreach ($code[1] as $block) {
+    if ($code && !empty($code[2])) {
+      foreach ($code[2] as $block) {
         $text = preg_replace('#<pre></pre>#', "<pre>$block</pre>", $text, 1);
       }
     }
index 0f996ee0c890116dd80d19f72e9ea8fd796bfc33..bf9d4adca5d870e14a1dad5e104b0320f3d828f2 100644 (file)
@@ -228,6 +228,8 @@ class CRM_Case_Info extends CRM_Core_Component_Info {
    *   List of component names.
    * @param array $metadata
    *   Specification of the setting (per *.settings.php).
+   *
+   * @throws \CRM_Core_Exception.
    */
   public static function onToggleComponents($oldValue, $newValue, $metadata) {
     if (
@@ -238,8 +240,7 @@ class CRM_Case_Info extends CRM_Core_Component_Info {
       $pathToCaseSampleTpl = __DIR__ . '/xml/configuration.sample/';
       self::loadCaseSampleData($pathToCaseSampleTpl . 'case_sample.mysql.tpl');
       if (!CRM_Case_BAO_Case::createCaseViews()) {
-        $msg = ts("Could not create the MySQL views for CiviCase. Your mysql user needs to have the 'CREATE VIEW' permission");
-        CRM_Core_Error::fatal($msg);
+        throw new CRM_Core_Exception(ts("Could not create the MySQL views for CiviCase. Your mysql user needs to have the 'CREATE VIEW' permission"));
       }
     }
   }
index e89132c3dedb03b506d74f5d6676659fc1bc4237..12f6cd06714d236ee121602e3451e5eb7c47bf2f 100644 (file)
@@ -63,7 +63,7 @@ class CRM_Case_Page_Tab extends CRM_Core_Page {
     }
     else {
       if ($this->_action & CRM_Core_Action::VIEW) {
-        CRM_Core_Error::fatal('Contact Id is required for view action.');
+        CRM_Core_Error::statusBounce('Contact Id is required for view action.');
       }
     }
 
index 4eb4e8826ef0321526fa62d964d115e18a2ae657..9307f2bb905a61f408e0121b175186bcd3200943 100644 (file)
@@ -23,18 +23,16 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
    * @param string $caseType
    * @param array $params
    *
-   * @return bool
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public function run($caseType, &$params) {
     $xml = $this->retrieve($caseType);
 
     if ($xml === FALSE) {
       $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
-      CRM_Core_Error::fatal(ts("Configuration file could not be retrieved for case type = '%1' %2.",
+      throw new CRM_Core_Exception(ts("Configuration file could not be retrieved for case type = '%1' %2.",
         [1 => $caseType, 2 => $docLink]
       ));
-      return FALSE;
     }
 
     $xmlProcessorProcess = new CRM_Case_XMLProcessor_Process();
@@ -56,10 +54,9 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
     $xml = $this->retrieve($caseType);
     if ($xml === FALSE) {
       $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
-      CRM_Core_Error::fatal(ts("Unable to load configuration file for the referenced case type: '%1' %2.",
+      throw new CRM_Core_Exception(ts("Unable to load configuration file for the referenced case type: '%1' %2.",
         [1 => $caseType, 2 => $docLink]
       ));
-      return FALSE;
     }
 
     switch ($fieldSet) {
@@ -93,8 +90,7 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
               $params
             )
             ) {
-              CRM_Core_Error::fatal();
-              return FALSE;
+              throw new CRM_Core_Exception('Unable to create case relationships');
             }
           }
         }
@@ -190,7 +186,7 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
    * @param array $params
    *
    * @return bool
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public function createRelationships($relationshipTypeXML, &$params) {
 
@@ -198,10 +194,9 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
     list($relationshipType, $relationshipTypeName) = $this->locateNameOrLabel($relationshipTypeXML);
     if ($relationshipType === FALSE) {
       $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
-      CRM_Core_Error::fatal(ts('Relationship type %1, found in case configuration file, is not present in the database %2',
+      throw new CRM_Core_Exception(ts('Relationship type %1, found in case configuration file, is not present in the database %2',
         [1 => $relationshipTypeName, 2 => $docLink]
       ));
-      return FALSE;
     }
 
     $client = $params['clientID'];
@@ -228,8 +223,7 @@ class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
       }
 
       if (!$this->createRelationship($relationshipParams)) {
-        CRM_Core_Error::fatal();
-        return FALSE;
+        throw new CRM_Core_Exception('Unable to create case relationship');
       }
     }
     return TRUE;
@@ -415,10 +409,9 @@ AND        a.is_deleted = 0
 
     if (!$activityTypeInfo) {
       $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
-      CRM_Core_Error::fatal(ts('Activity type %1, found in case configuration file, is not present in the database %2',
+      throw new CRM_Core_Exception(ts('Activity type %1, found in case configuration file, is not present in the database %2',
         [1 => $activityTypeName, 2 => $docLink]
       ));
-      return FALSE;
     }
 
     $activityTypeID = $activityTypeInfo['id'];
@@ -549,8 +542,7 @@ AND        a.is_deleted = 0
     $activity = CRM_Activity_BAO_Activity::create($activityParams);
 
     if (!$activity) {
-      CRM_Core_Error::fatal();
-      return FALSE;
+      throw new CRM_Core_Exception('Unable to create Activity');
     }
 
     // create case activity record
@@ -731,17 +723,16 @@ AND        a.is_deleted = 0
 
   /**
    * @param $caseType
-   * @param null $activityTypeName
+   * @param string|null $activityTypeName
    *
    * @return array|bool|mixed
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public function getMaxInstance($caseType, $activityTypeName = NULL) {
     $xml = $this->retrieve($caseType);
 
     if ($xml === FALSE) {
-      CRM_Core_Error::fatal();
-      return FALSE;
+      throw new CRM_Core_Exception('Unable to locate xml definition for case type ' . $caseType);
     }
 
     $activityInstances = $this->activityTypes($xml->ActivityTypes, TRUE);
index 80d10ff5865d2dd8c3a294e695f5717a8f7af5a3..260c45cfbd7d315ef3b4d046bb43d5cb78a4d656 100644 (file)
@@ -832,6 +832,7 @@ abstract class CRM_Contact_Import_Parser extends CRM_Import_Parser {
               $this->formatLocationBlock($value, $formatted);
             }
             else {
+              // @todo - this is still reachable - e.g. import with related contact info like firstname,lastname,spouse-first-name,spouse-last-name,spouse-home-phone
               CRM_Core_Error::deprecatedFunctionWarning('this is not expected to be reachable now');
               $this->formatContactParameters($value, $formatted);
             }
index 48b01b3e6c1277fa54ba0ee0bccede7c2c7f2985..350bb0ff9211f542ae21c692f944d96e33403f2e 100644 (file)
@@ -1290,7 +1290,8 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution {
       if (empty($resultDAO->payment_processor_id) && CRM_Core_Permission::check('edit contributions')) {
         $links = [
           CRM_Core_Action::UPDATE => [
-            'name' => "<i class='crm-i fa-pencil'></i>",
+            'name' => ts('Edit Payment'),
+            'icon' => 'fa-pencil',
             'url' => 'civicrm/payment/edit',
             'class' => 'medium-popup',
             'qs' => "reset=1&id=%%id%%&contribution_id=%%contribution_id%%",
@@ -2755,7 +2756,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
    * @return bool
    * @throws Exception
    */
-  public function loadRelatedObjects(&$input, &$ids, $loadAll = FALSE) {
+  public function loadRelatedObjects($input, &$ids, $loadAll = FALSE) {
     // @todo deprecate this function - the steps should be
     // 1) add additional functions like 'getRelatedMemberships'
     // 2) switch all calls that refer to ->_relatedObjects to
index db2870fa146a305f725d5a2fe4b7c224fad2bc5d..ca2805d551eb1e0849df356b4d6be2f3501cc2a3 100644 (file)
@@ -371,12 +371,12 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
         $postProfileType = CRM_Core_BAO_UFField::getProfileType($this->_values['custom_post_id']);
       }
 
-      if (((isset($postProfileType) && $postProfileType == 'Membership') ||
-          (isset($preProfileType) && $preProfileType == 'Membership')
+      if (((isset($postProfileType) && $postProfileType === 'Membership') ||
+          (isset($preProfileType) && $preProfileType === 'Membership')
         ) &&
         !$this->_membershipBlock['is_active']
       ) {
-        CRM_Core_Error::fatal(ts('This page includes a Profile with Membership fields - but the Membership Block is NOT enabled. Please notify the site administrator.'));
+        CRM_Core_Error::statusBounce(ts('This page includes a Profile with Membership fields - but the Membership Block is NOT enabled. Please notify the site administrator.'));
       }
 
       $pledgeBlock = CRM_Pledge_BAO_PledgeBlock::getPledgeBlock($this->_id);
@@ -437,7 +437,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
       !$this->_membershipBlock['is_active'] &&
       !$this->_priceSetId
     ) {
-      CRM_Core_Error::fatal(ts('The requested online contribution page is missing a required Contribution Amount section or Membership section or Price Set. Please check with the site administrator for assistance.'));
+      CRM_Core_Error::statusBounce(ts('The requested online contribution page is missing a required Contribution Amount section or Membership section or Price Set. Please check with the site administrator for assistance.'));
     }
 
     if ($this->_values['amount_block_is_active']) {
@@ -577,7 +577,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     //Hence, assign the existing location type email by iterating through the params.
     if ($this->_emailExists && empty($this->_params["email-{$this->_bltID}"])) {
       foreach ($this->_params as $key => $val) {
-        if (substr($key, 0, 6) == 'email-') {
+        if (substr($key, 0, 6) === 'email-') {
           $this->assign('email', $this->_params[$key]);
           break;
         }
@@ -749,6 +749,12 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     CRM_Utils_ReCAPTCHA::enableCaptchaOnForm($this);
   }
 
+  /**
+   * Assign payment field information to the template.
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
+   */
   public function assignPaymentFields() {
     //fix for CRM-3767
     $isMonetary = FALSE;
@@ -765,7 +771,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     // The concept of contributeMode is deprecated.
     // The payment processor object can provide info about the fields it shows.
     if ($isMonetary && is_a($this->_paymentProcessor['object'], 'CRM_Core_Payment')) {
-      /** @var  $paymentProcessorObject \CRM_Core_Payment */
+      /** @var  \CRM_Core_Payment $paymentProcessorObject */
       $paymentProcessorObject = $this->_paymentProcessor['object'];
 
       $paymentFields = $paymentProcessorObject->getPaymentFormFields();
@@ -824,6 +830,8 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
    *
    * @param int $id
    * @param CRM_Core_Form $form
+   *
+   * @throws \CRM_Core_Exception
    */
   public function buildComponentForm($id, $form) {
     if (empty($id)) {
@@ -833,13 +841,13 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     $contactID = $this->getContactID();
 
     foreach (['soft_credit', 'on_behalf'] as $module) {
-      if ($module == 'soft_credit') {
+      if ($module === 'soft_credit') {
         if (empty($form->_values['honoree_profile_id'])) {
           continue;
         }
 
         if (!CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $form->_values['honoree_profile_id'], 'is_active')) {
-          CRM_Core_Error::fatal(ts('This contribution page has been configured for contribution on behalf of honoree and the selected honoree profile is either disabled or not found.'));
+          CRM_Core_Error::statusBounce(ts('This contribution page has been configured for contribution on behalf of honoree and the selected honoree profile is either disabled or not found.'));
         }
 
         $profileContactType = CRM_Core_BAO_UFGroup::getContactType($form->_values['honoree_profile_id']);
@@ -850,7 +858,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
         ];
         $validProfile = CRM_Core_BAO_UFGroup::checkValidProfile($form->_values['honoree_profile_id'], $requiredProfileFields[$profileContactType]);
         if (!$validProfile) {
-          CRM_Core_Error::fatal(ts('This contribution page has been configured for contribution on behalf of honoree and the required fields of the selected honoree profile are disabled or doesn\'t exist.'));
+          CRM_Core_Error::statusBounce(ts('This contribution page has been configured for contribution on behalf of honoree and the required fields of the selected honoree profile are disabled or doesn\'t exist.'));
         }
 
         foreach (['honor_block_title', 'honor_block_text'] as $name) {
@@ -892,7 +900,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
         }
 
         if (!CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $form->_values['onbehalf_profile_id'], 'is_active')) {
-          CRM_Core_Error::fatal(ts('This contribution page has been configured for contribution on behalf of an organization and the selected onbehalf profile is either disabled or not found.'));
+          CRM_Core_Error::statusBounce(ts('This contribution page has been configured for contribution on behalf of an organization and the selected onbehalf profile is either disabled or not found.'));
         }
 
         $member = CRM_Member_BAO_Membership::getMembershipBlock($form->_id);
@@ -911,7 +919,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
                 in_array('Contribution', $onBehalfProfile)
               )
             ) {
-              CRM_Core_Error::fatal($msg);
+              CRM_Core_Error::statusBounce($msg);
             }
           }
         }
@@ -1031,7 +1039,7 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
    */
   public function getTemplateFileName() {
     $fileName = $this->checkTemplateFileExists();
-    return $fileName ? $fileName : parent::getTemplateFileName();
+    return $fileName ?: parent::getTemplateFileName();
   }
 
   /**
@@ -1049,6 +1057,8 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
 
   /**
    * Authenticate pledge user during online payment.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function authenticatePledgeUser() {
     //get the userChecksum and contact id
@@ -1087,12 +1097,12 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     }
 
     if (!$validUser) {
-      CRM_Core_Error::fatal(ts("Oops. It looks like you have an incorrect or incomplete link (URL). Please make sure you've copied the entire link, and try again. Contact the site administrator if this error persists."));
+      CRM_Core_Error::statusBounce(ts("Oops. It looks like you have an incorrect or incomplete link (URL). Please make sure you've copied the entire link, and try again. Contact the site administrator if this error persists."));
     }
 
     //check for valid pledge status.
     if (!in_array($pledgeValues['status_id'], $validStatus)) {
-      CRM_Core_Error::fatal(ts('Oops. You cannot make a payment for this pledge - pledge status is %1.', [1 => CRM_Utils_Array::value($pledgeValues['status_id'], $allStatus)]));
+      CRM_Core_Error::statusBounce(ts('Oops. You cannot make a payment for this pledge - pledge status is %1.', [1 => CRM_Utils_Array::value($pledgeValues['status_id'], $allStatus)]));
     }
   }
 
@@ -1102,6 +1112,8 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
    * In case user cancel recurring contribution,
    * When we get the control back from payment gate way
    * lets delete the recurring and related contribution.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function cancelRecurring() {
     $isCancel = CRM_Utils_Request::retrieve('cancel', 'Boolean');
@@ -1135,6 +1147,9 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
    *
    * @return bool
    *   Is this a separate membership payment
+   *
+   * @throws \CiviCRM_API3_Exception
+   * @throws \CRM_Core_Exception
    */
   protected function buildMembershipBlock(
     $cid,
index 09f1285c99b43ffa64a42b0fb6396d172b6f6a45..c8acc1e8ff88e65a559b25f33d5f0a29e5a1e31c 100644 (file)
@@ -215,8 +215,6 @@ AND    co.id IN ( $contribIDs )";
     $statusID = $params['contribution_status_id'] ?? NULL;
     $baseIPN = new CRM_Core_Payment_BaseIPN();
 
-    $transaction = new CRM_Core_Transaction();
-
     // get the missing pieces for each contribution
     $contribIDs = implode(',', $form->_contributionIds);
     $details = self::getDetails($contribIDs);
@@ -246,11 +244,13 @@ AND    co.id IN ( $contribIDs )";
       );
 
       if ($statusID == array_search('Cancelled', $contributionStatuses)) {
+        $transaction = new CRM_Core_Transaction();
         $baseIPN->cancelled($objects, $transaction);
         $transaction->commit();
         continue;
       }
       elseif ($statusID == array_search('Failed', $contributionStatuses)) {
+        $transaction = new CRM_Core_Transaction();
         $baseIPN->failed($objects, $transaction);
         $transaction->commit();
         continue;
@@ -261,7 +261,6 @@ AND    co.id IN ( $contribIDs )";
           $contributionStatuses
         )
       ) {
-        $transaction->commit();
         continue;
       }
 
@@ -282,8 +281,8 @@ AND    co.id IN ( $contribIDs )";
       $input['trxn_date'] = $params["trxn_date_{$row['contribution_id']}"] . ' ' . date('H:i:s');
       $input['is_email_receipt'] = !empty($params['is_email_receipt']);
 
-      // @todo calling baseIPN like this is a pattern in it's last gasps. Call contribute.completetransaction api.
-      $baseIPN->completeTransaction($input, $ids, $objects, $transaction, FALSE);
+      // @todo calling CRM_Contribute_BAO_Contribution::completeOrder like this is a pattern in it's last gasps. Call contribute.completetransaction api.
+      CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects);
 
       // reset template values before processing next transactions
       $template->clearTemplateVars();
index 0e54b2445b738ebb6384987de42597380d3e3712..e24431a2ef3a9ccaebc2ff004da72566555ed91b 100644 (file)
@@ -247,7 +247,7 @@ class CRM_Core_Action {
           $urlPath,
           $classes,
           !empty($link['title']) ? "title='{$link['title']}' " : '',
-          $link['name']
+          empty($link['icon']) ? $link['name'] : CRM_Core_Page::crmIcon($link['icon'], $link['name'], TRUE, ['title' => ''])
         );
       }
     }
index c1d7ec1a104270d55cf61ca6ab974523641da72c..a53e0e623278acb7ba7a1eb4b8f4df4aa58b8267 100644 (file)
@@ -226,6 +226,7 @@ FROM civicrm_action_schedule cas
    * @param int $id
    *   ID of the Reminder to be deleted.
    *
+   * @throws CRM_Core_Exception
    */
   public static function del($id) {
     if ($id) {
@@ -236,7 +237,7 @@ FROM civicrm_action_schedule cas
         return;
       }
     }
-    CRM_Core_Error::fatal(ts('Invalid value passed to delete function.'));
+    throw new CRM_Core_Exception(ts('Invalid value passed to delete function.'));
   }
 
   /**
index e22a25cecd2c1e61a94eb029b36528920b8f7956..c0f6561c8a41c624b622d2725403b33c9bbb304d 100644 (file)
@@ -1181,7 +1181,7 @@ SELECT is_primary,
     $relTypeId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', 'Household Member of', 'id', 'name_a_b');
 
     if (!$relTypeId) {
-      CRM_Core_Error::fatal(ts("You seem to have deleted the relationship type 'Household Member of'"));
+      throw new CRM_Core_Exception(ts("You seem to have deleted the relationship type 'Household Member of'"));
     }
 
     $relParam = [
index a36b698e66d446923e0ffe0844ac2c7973d3f260..876cd323c5b2d8024ea3d2de1e68afba3bfc2b95 100644 (file)
@@ -40,6 +40,7 @@ class CRM_Core_BAO_Block {
    *
    * @return array
    *   Array of $block objects.
+   * @throws CRM_Core_Exception
    */
   public static function &getValues($blockName, $params) {
     if (empty($params)) {
@@ -52,7 +53,7 @@ class CRM_Core_BAO_Block {
     if (!isset($params['entity_table'])) {
       $block->contact_id = $params['contact_id'];
       if (!$block->contact_id) {
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception('Invalid Contact ID parameter passed');
       }
       $blocks = self::retrieveBlock($block, $blockName);
     }
index ee678348dd60813e4ec3a6575532bbacf8647c9f..a13a6fee8588cfa91a32a185e93cc35e45477f6a 100644 (file)
@@ -149,6 +149,7 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
    * @param int $componentID
    *   The optional component ID (so componenets can share the same name space).
    * @deprecated
+   * @throws CRM_Core_Exception
    */
   public static function setItem(&$data, $group, $path, $componentID = NULL) {
     CRM_Core_Error::deprecatedFunctionWarning(
@@ -167,7 +168,7 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
     // CRM-11234
     $lock = Civi::lockManager()->acquire("cache.{$group}_{$path}._{$componentID}");
     if (!$lock->isAcquired()) {
-      CRM_Core_Error::fatal();
+      throw new CRM_Core_Exception('Cannot acquire database lock');
     }
 
     $table = self::getTableName();
index e66aaf6f0b98218e42b1a4492af4b06b68aa3bdf..a682676e749a904aea770edfa666d7e5aafe13ca 100644 (file)
@@ -608,6 +608,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
    *
    * @return CRM_Core_BAO_CustomField
    *   The field object.
+   * @throws CRM_Core_Exception
    */
   public static function getFieldObject($fieldID) {
     $field = new CRM_Core_BAO_CustomField();
@@ -619,7 +620,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     if (empty($fieldValues)) {
       $field->id = $fieldID;
       if (!$field->find(TRUE)) {
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception('Cannot find Custom Field');
       }
 
       $fieldValues = [];
@@ -1777,11 +1778,13 @@ WHERE  id IN ( %1, %2 )
    *   FK to civicrm_custom_field.
    * @param int $newGroupID
    *   FK to civicrm_custom_group.
+   *
+   * @throws CRM_Core_Exception
    */
   public static function moveField($fieldID, $newGroupID) {
     $validation = self::_moveFieldValidate($fieldID, $newGroupID);
     if (TRUE !== $validation) {
-      CRM_Core_Error::fatal(implode(' ', $validation));
+      throw new CRM_Core_Exception(implode(' ', $validation));
     }
     $field = new CRM_Core_DAO_CustomField();
     $field->id = $fieldID;
@@ -2070,7 +2073,7 @@ WHERE  id IN ( %1, %2 )
    *
    * @return array
    *   fatal is fieldID does not exists, else array of tableName, columnName
-   * @throws \Exception
+   * @throws \CRM_Core_Exception
    */
   public static function getTableColumnGroup($fieldID, $force = FALSE) {
     $cacheKey = "CRM_Core_DAO_CustomField_CustomGroup_TableColumn_{$fieldID}";
@@ -2087,7 +2090,7 @@ AND    cf.id = %1";
       $dao = CRM_Core_DAO::executeQuery($query, $params);
 
       if (!$dao->fetch()) {
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception("Cannot find table and column information for Custom Field " . $fieldID);
       }
       $fieldValues = [$dao->table_name, $dao->column_name, $dao->id];
       $cache->set($cacheKey, $fieldValues);
index 9009cce0ac78f5bdc2e39ef9bc38a15ececb23a3..e876436f75ff03a34da1d192348282ea46563921 100644 (file)
@@ -277,7 +277,7 @@ SET    {$dao->columnName} = REPLACE( {$dao->columnName}, %1, %2 )";
           break;
 
         default:
-          CRM_Core_Error::fatal();
+          throw new CRM_Core_Exception('Invalid HTML Type');
       }
       $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
     }
index 1fa9a508a82df987e017fcb2d3b8e9bea2380330..0a724557b86e4a90eef27b879bf604b0fe3193df 100644 (file)
@@ -153,7 +153,7 @@ class CRM_Core_BAO_CustomValueTable {
 
             case 'File':
               if (!$field['file_id']) {
-                CRM_Core_Error::fatal();
+                throw new CRM_Core_Exception('Missing parameter file_id');
               }
 
               // need to add/update civicrm_entity_file
@@ -318,7 +318,7 @@ class CRM_Core_BAO_CustomValueTable {
         return 'datetime';
 
       default:
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception('Invalid Field Type');
     }
   }
 
@@ -415,12 +415,13 @@ class CRM_Core_BAO_CustomValueTable {
    *   Array of custom values for the entity with key=>value
    *                                   pairs specified as civicrm_custom_field.id => custom value.
    *                                   Empty array if no custom values found.
+   * @throws CRM_Core_Exception
    */
   public static function &getEntityValues($entityID, $entityType = NULL, $fieldIDs = NULL, $formatMultiRecordField = FALSE, $DTparams = NULL) {
     if (!$entityID) {
       // adding this here since an empty contact id could have serious repurcussions
       // like looping forever
-      CRM_Core_Error::fatal('Please file an issue with the backtrace');
+      throw new CRM_Core_Exception('Please file an issue with the backtrace');
       return NULL;
     }
 
index ce24c32e4e4888b3e9c601534f007a44dffa50b5..812747eb42466b68edfc2893c488e73535e688bd 100644 (file)
@@ -98,11 +98,12 @@ class CRM_Core_BAO_Discount extends CRM_Core_DAO_Discount {
    * @return int
    *   $dao->id       discount id of the set which matches
    *                                 the date criteria
+   * @throws CRM_Core_Exception
    */
   public static function findSet($entityID, $entityTable) {
     if (empty($entityID) || empty($entityTable)) {
       // adding this here, to trap errors if values are not sent
-      CRM_Core_Error::fatal();
+      throw new CRM_Core_Exception('Invalid parameters passed to findSet function');
       return NULL;
     }
 
index 3e78e3ffeaa9a8be4466f13650fcf60269b068e1..e8c2ad60525a62f3fc4042c3fb2c86f7267f4e47 100644 (file)
@@ -677,7 +677,7 @@ AND       CEF.entity_id    = %2";
 
   /**
    * Delete a file attachment from an entity table / entity ID
-   *
+   * @throws CRM_Core_Exception
    */
   public static function deleteAttachment() {
     $params = [];
@@ -689,7 +689,7 @@ AND       CEF.entity_id    = %2";
 
     $signer = new CRM_Utils_Signer(CRM_Core_Key::privateKey(), self::$_signableFields);
     if (!$signer->validate($signature, $params)) {
-      CRM_Core_Error::fatal('Request signature is invalid');
+      throw new CRM_Core_Exception('Request signature is invalid');
     }
 
     self::deleteEntityFile($params['entityTable'], $params['entityID'], NULL, $params['fileID']);
index 5609b3075f0270f4133a8bcb1ccfc427782aad97..5cbcd121fda9316c245988c6cce3b00c65c6f730 100644 (file)
@@ -88,10 +88,11 @@ class CRM_Core_BAO_Job extends CRM_Core_DAO_Job {
    *   ID of the job to be deleted.
    *
    * @return bool|null
+   * @throws CRM_Core_Exception
    */
   public static function del($jobID) {
     if (!$jobID) {
-      CRM_Core_Error::fatal(ts('Invalid value passed to delete function.'));
+      throw new CRM_Core_Exception(ts('Invalid value passed to delete function.'));
     }
 
     $dao = new CRM_Core_DAO_Job();
index 175d599410bfe207560ab21264a09c271cd8fcf0..8678560c9cf30a693035e0d4ea34b5872fda3168 100644 (file)
@@ -527,7 +527,7 @@ class CRM_Core_BAO_LabelFormat extends CRM_Core_DAO_OptionValue {
     // make sure serialized array will fit in the 'value' column
     $attribute = CRM_Core_DAO::getAttribute('CRM_Core_BAO_LabelFormat', 'value');
     if (strlen($this->value) > $attribute['maxlength']) {
-      CRM_Core_Error::fatal(ts('Label Format does not fit in database.'));
+      throw new CRM_Core_Exception(ts('Label Format does not fit in database.'));
     }
     $this->save();
 
index ec50cd15f04c81a462d77632c6e0af0750030625..e2d393976cf114715d0b1406dcd6bf0edfcfda6e 100644 (file)
@@ -260,13 +260,14 @@ WHERE e.id = %1";
    *   Contact id.
    * @param int $locationTypeId
    *   Id of the location to delete.
+   * @throws CRM_Core_Exception
    */
   public static function deleteLocationBlocks($contactId, $locationTypeId) {
     // ensure that contactId has a value
     if (empty($contactId) ||
       !CRM_Utils_Rule::positiveInteger($contactId)
     ) {
-      CRM_Core_Error::fatal();
+      throw new CRM_Core_Exception('Incorrect contact id parameter passed to deleteLocationBlocks');
     }
 
     if (empty($locationTypeId) ||
index 19c6ec2290cd8cff27b851cdc0386c2522be4a1d..56c1d9e02034591b8e1c82a6404eab339f88058c 100644 (file)
@@ -1002,6 +1002,7 @@ class CRM_Core_BAO_Mapping extends CRM_Core_DAO_Mapping {
    *
    * @return array
    *   formatted associated array of elements
+   * @throws CRM_Core_Exception
    */
   public static function formattedFields(&$params, $row = FALSE) {
     $fields = [];
@@ -1016,7 +1017,7 @@ class CRM_Core_BAO_Mapping extends CRM_Core_DAO_Mapping {
       foreach ($value as $k => $v) {
         if (in_array($v[0], $types)) {
           if ($contactType && $contactType != $v[0]) {
-            CRM_Core_Error::fatal(ts("Cannot have two clauses with different types: %1, %2",
+            throw new CRM_Core_Exception(ts("Cannot have two clauses with different types: %1, %2",
               [1 => $contactType, 2 => $v[0]]
             ));
           }
index c14709e855258815f7aa39e74acf61e076ae5311..f7456bc50df0b5665ca5b919c8cb06137fdda573 100644 (file)
@@ -72,12 +72,13 @@ class CRM_Core_BAO_PaperSize extends CRM_Core_DAO_OptionValue {
    *
    * @return int
    *   Group ID (null if Group ID doesn't exist)
+   * @throws CRM_Core_Exception
    */
   private static function _getGid() {
     if (!self::$_gid) {
       self::$_gid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'paper_size', 'id', 'name');
       if (!self::$_gid) {
-        CRM_Core_Error::fatal(ts('Paper Size Option Group not found in database.'));
+        throw new CRM_Core_Exception(ts('Paper Size Option Group not found in database.'));
       }
     }
     return self::$_gid;
@@ -268,6 +269,7 @@ class CRM_Core_BAO_PaperSize extends CRM_Core_DAO_OptionValue {
    * @param array $values associative array of name/value pairs
    * @param int $id
    *   Id of the database record (null = new record).
+   * @throws CRM_Core_Exception
    */
   public function savePaperSize(&$values, $id) {
     // get the Option Group ID for Paper Sizes (create one if it doesn't exist)
@@ -311,7 +313,7 @@ class CRM_Core_BAO_PaperSize extends CRM_Core_DAO_OptionValue {
     // make sure serialized array will fit in the 'value' column
     $attribute = CRM_Core_DAO::getAttribute('CRM_Core_BAO_PaperSize', 'value');
     if (strlen($this->value) > $attribute['maxlength']) {
-      CRM_Core_Error::fatal(ts('Paper Size does not fit in database.'));
+      throw new CRM_Core_Exception(ts('Paper Size does not fit in database.'));
     }
     $this->save();
 
@@ -325,7 +327,7 @@ class CRM_Core_BAO_PaperSize extends CRM_Core_DAO_OptionValue {
    *
    * @param int $id
    *   ID of the Paper Size to be deleted.
-   *
+   * @throws CRM_Core_Exception
    */
   public static function del($id) {
     if ($id) {
@@ -340,7 +342,7 @@ class CRM_Core_BAO_PaperSize extends CRM_Core_DAO_OptionValue {
         }
       }
     }
-    CRM_Core_Error::fatal(ts('Invalid value passed to delete function.'));
+    throw new CRM_Core_Exception(ts('Invalid value passed to delete function.'));
   }
 
 }
index 0703108a7f0a60326c3486966b10b3a1f578ba09..33783d7306c4a87a091f3e4bf5e8e229278d18ab 100644 (file)
@@ -127,12 +127,13 @@ class CRM_Core_BAO_PdfFormat extends CRM_Core_DAO_OptionValue {
    *
    * @return int
    *   Group ID (null if Group ID doesn't exist)
+   * @throws CRM_Core_Exception
    */
   private static function _getGid() {
     if (!self::$_gid) {
       self::$_gid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'pdf_format', 'id', 'name');
       if (!self::$_gid) {
-        CRM_Core_Error::fatal(ts('PDF Format Option Group not found in database.'));
+        throw new CRM_Core_Exception(ts('PDF Format Option Group not found in database.'));
       }
     }
     return self::$_gid;
@@ -325,6 +326,7 @@ class CRM_Core_BAO_PdfFormat extends CRM_Core_DAO_OptionValue {
    * @param array $values associative array of name/value pairs
    * @param int $id
    *   Id of the database record (null = new record).
+   * @throws CRM_Core_Exception
    */
   public function savePdfFormat(&$values, $id = NULL) {
     // get the Option Group ID for PDF Page Formats (create one if it doesn't exist)
@@ -364,7 +366,7 @@ class CRM_Core_BAO_PdfFormat extends CRM_Core_DAO_OptionValue {
     // make sure serialized array will fit in the 'value' column
     $attribute = CRM_Core_DAO::getAttribute('CRM_Core_BAO_PdfFormat', 'value');
     if (strlen($this->value) > $attribute['maxlength']) {
-      CRM_Core_Error::fatal(ts('PDF Page Format does not fit in database.'));
+      throw new CRM_Core_Exception(ts('PDF Page Format does not fit in database.'));
     }
     $this->save();
 
@@ -378,7 +380,7 @@ class CRM_Core_BAO_PdfFormat extends CRM_Core_DAO_OptionValue {
    *
    * @param int $id
    *   ID of the PDF Page Format to be deleted.
-   *
+   * @throws CRM_Core_Exception
    */
   public static function del($id) {
     if ($id) {
@@ -393,7 +395,7 @@ class CRM_Core_BAO_PdfFormat extends CRM_Core_DAO_OptionValue {
         }
       }
     }
-    CRM_Core_Error::fatal(ts('Invalid value passed to delete function.'));
+    throw new CRM_Core_Exception(ts('Invalid value passed to delete function.'));
   }
 
 }
index 513bba5dd5174d8ded6b8f16cd1c454ddec12863..b05bbf4a15488ec5981828e45e496bda0868da3c 100644 (file)
@@ -57,18 +57,20 @@ class CRM_Core_BAO_PreferencesDate extends CRM_Core_DAO_PreferencesDate {
    *   Id of the database record.
    * @param bool $is_active
    *   Value we want to set the is_active field.
+   * @throws CRM_Core_Exception
    */
   public static function setIsActive($id, $is_active) {
-    CRM_Core_Error::fatal();
+    throw new CRM_Core_Exception('Cannot call setIsActive function');
   }
 
   /**
    * Delete preference dates.
    *
    * @param int $id
+   * @throws CRM_Core_Exception
    */
   public static function del($id) {
-    CRM_Core_Error::fatal();
+    throw new CRM_Core_Exception('Cannot call del function');
   }
 
   /**
index f4b1622de908feadb54a23f5d4a7937a15ad93d4..fceb1237b154f3e87b3518d2b1ff09c01cc7bf75 100644 (file)
@@ -224,6 +224,7 @@ class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
    * Generate new DAOs and along with entries in civicrm_recurring_entity table.
    *
    * @return array
+   * @throws CRM_Core_Exception
    */
   public function generateEntities() {
     self::setStatus(self::RUNNING);
@@ -241,7 +242,7 @@ class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
         }
       }
       if (empty($findCriteria)) {
-        CRM_Core_Error::fatal("Find criteria missing to generate form. Make sure entity_id and table is set.");
+        throw new CRM_Core_Exception("Find criteria missing to generate form. Make sure entity_id and table is set.");
       }
 
       $count = 0;
@@ -547,11 +548,12 @@ class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
    *
    *
    * @return object
+   * @throws new CRM_Core_Exception
    */
   public static function copyCreateEntity($entityTable, $fromCriteria, $newParams, $createRecurringEntity = TRUE) {
     $daoName = self::$_tableDAOMapper[$entityTable];
     if (!$daoName) {
-      CRM_Core_Error::fatal("DAO Mapper missing for $entityTable.");
+      throw new CRM_Core_Exception("DAO Mapper missing for $entityTable.");
     }
     $newObject = CRM_Core_DAO::copyGeneric($daoName, $fromCriteria, $newParams);
 
@@ -631,7 +633,7 @@ class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
         $updateDAO = CRM_Core_DAO::cascadeUpdate($daoName, $obj->id, $entityID, $skipData);
       }
       else {
-        CRM_Core_Error::fatal("DAO Mapper missing for $entityTable.");
+        throw new CRM_Core_Exception("DAO Mapper missing for $entityTable.");
       }
     }
     // done with processing. lets unset static var.
@@ -811,7 +813,7 @@ class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
         foreach (self::$_linkedEntitiesInfo as $linkedTable => $linfo) {
           $daoName = self::$_tableDAOMapper[$linkedTable];
           if (!$daoName) {
-            CRM_Core_Error::fatal("DAO Mapper missing for $linkedTable.");
+            throw new CRM_Core_Exception("DAO Mapper missing for $linkedTable.");
           }
 
           $linkedDao = new $daoName();
index c6757015f02e7bca13c0fd03be7cb58a25b60257..48a820916ad21af0af5f1ef2dd77d5a0c5cea1cb 100644 (file)
@@ -502,7 +502,7 @@ ADD UNIQUE INDEX `unique_entity_id` ( `entity_id` )";
    * @param string $columnName
    * @param $length
    *
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public static function alterFieldLength($customFieldID, $tableName, $columnName, $length) {
     // first update the custom field tables
@@ -543,7 +543,7 @@ MODIFY      {$columnName} varchar( $length )
       CRM_Core_DAO::executeQuery($sql);
     }
     else {
-      CRM_Core_Error::fatal(ts('Could Not Find Custom Field Details for %1, %2, %3',
+      throw new CRM_Core_Exception(ts('Could Not Find Custom Field Details for %1, %2, %3',
         [
           1 => $tableName,
           2 => $columnName,
index 2f225ee9fe2e7241d00cfed80cf3d2ff337f0d65..d4e182eadc31ad5f76900919f303299451233e39 100644 (file)
@@ -28,6 +28,7 @@ class CRM_Core_BAO_StatusPreference extends CRM_Core_DAO_StatusPreference {
    * @param array $params
    *
    * @return array
+   * @throws CRM_Core_Exception
    */
   public static function create($params) {
     $statusPreference = new CRM_Core_BAO_StatusPreference();
@@ -42,11 +43,11 @@ class CRM_Core_BAO_StatusPreference extends CRM_Core_DAO_StatusPreference {
       $params['ignore_severity'] = CRM_Utils_Check::severityMap($params['ignore_severity']);
     }
     if ($params['ignore_severity'] > 7) {
-      CRM_Core_Error::fatal(ts('You can not pass a severity level higher than 7.'));
+      throw new CRM_Core_Exception(ts('You can not pass a severity level higher than 7.'));
     }
     // If severity is now blank, you have an invalid severity string.
     if (is_null($params['ignore_severity'])) {
-      CRM_Core_Error::fatal(ts('Invalid string passed as severity level.'));
+      throw new CRM_Core_Exception(ts('Invalid string passed as severity level.'));
     }
 
     // Check if this StatusPreference already exists.
index d23e665637ea55e7b63d2553699a71ed16f5a2fe..5841ab0d72451a4c9b3585fb0e582a30fc7ce960 100644 (file)
@@ -2700,7 +2700,7 @@ AND    ( entity_id IS NULL OR entity_id <= 0 )
 
     if (!$domainEmailAddress || $domainEmailAddress == 'info@EXAMPLE.ORG') {
       $fixUrl = CRM_Utils_System::url('civicrm/admin/domain', 'action=update&reset=1');
-      CRM_Core_Error::fatal(ts('The site administrator needs to enter a valid \'FROM Email Address\' in <a href="%1">Administer CiviCRM &raquo; Communications &raquo; FROM Email Addresses</a>. The email address used may need to be a valid mail account with your email service provider.', [1 => $fixUrl]));
+      CRM_Core_Error::statusBounce(ts('The site administrator needs to enter a valid \'FROM Email Address\' in <a href="%1">Administer CiviCRM &raquo; Communications &raquo; FROM Email Addresses</a>. The email address used may need to be a valid mail account with your email service provider.', [1 => $fixUrl]));
     }
 
     foreach ($emailList as $emailTo) {
index 116ebd141c874a9139e35fb7d86b6390d7457fc9..b7dd9b2c80187275e9022d123fe3c6f4d9038b07 100644 (file)
@@ -57,12 +57,14 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
    *
    * @param $ctype
    * @param bool $isLogin
+   *
+   * @throws CRM_Core_Exception
    */
   public static function synchronize(&$user, $update, $uf, $ctype, $isLogin = FALSE) {
     $userSystem = CRM_Core_Config::singleton()->userSystem;
     $session = CRM_Core_Session::singleton();
     if (!is_object($session)) {
-      CRM_Core_Error::fatal('wow, session is not an object?');
+      throw new CRM_Core_Exception('wow, session is not an object?');
       return;
     }
 
index d8fe9df6b359fa05c4dcaa8629eaae7d5a35f058..ba4f09d7fa43d4d8dc3a3807728a8305eb4fb87d 100644 (file)
@@ -51,6 +51,7 @@ class CRM_Core_BAO_WordReplacement extends CRM_Core_DAO_WordReplacement {
    * @param null $reset
    *
    * @return null|CRM_Core_BAO_WordReplacement
+   * @throws CRM_Core_Exception
    */
   public static function getWordReplacement($reset = NULL) {
     static $wordReplacement = NULL;
@@ -58,7 +59,7 @@ class CRM_Core_BAO_WordReplacement extends CRM_Core_DAO_WordReplacement {
       $wordReplacement = new CRM_Core_BAO_WordReplacement();
       $wordReplacement->id = CRM_Core_Config::wordReplacementID();
       if (!$wordReplacement->find(TRUE)) {
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception('Unable to find word replacement');
       }
     }
     return $wordReplacement;
index b96694a43d5a38c4f6cc74d24d9a085b99163b45..c594c3b6d8ab8edc725eaccd1e2da35193ccdf81 100644 (file)
@@ -70,7 +70,7 @@ class CRM_Core_Component {
    * @param bool $force
    *
    * @return CRM_Core_Component_Info[]
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public static function &getComponents($force = FALSE) {
     if (!isset(Civi::$statics[__CLASS__]['all']) || $force) {
@@ -87,7 +87,7 @@ class CRM_Core_Component {
         require_once $infoClassFile;
         $infoObject = new $infoClass($cr->name, $cr->namespace, $cr->id);
         if ($infoObject->info['name'] !== $cr->name) {
-          CRM_Core_Error::fatal("There is a discrepancy between name in component registry and in info file ({$cr->name}).");
+          throw new CRM_Core_Exception("There is a discrepancy between name in component registry and in info file ({$cr->name}).");
         }
         Civi::$statics[__CLASS__]['all'][$cr->name] = $infoObject;
         unset($infoObject);
index a72e5cdf33df691715fd599909023168ddca2011..6e9995ff23a2bfa30a061bac0b1b279c72fa5cac 100644 (file)
@@ -1736,7 +1736,7 @@ FROM   civicrm_domain
       if (!$blockCopyofCustomValues) {
         $newObject->copyCustomFields($object->id, $newObject->id);
       }
-      CRM_Utils_Hook::post('create', CRM_Core_DAO_AllCoreTables::getBriefName(str_replace('_BAO_', '_DAO_', $daoName)), $newObject->id, $newObject);
+      CRM_Utils_Hook::post('create', CRM_Core_DAO_AllCoreTables::getBriefName($daoName), $newObject->id, $newObject);
     }
 
     return $newObject;
@@ -2858,11 +2858,11 @@ SELECT contact_id
    * Generates acl clauses suitable for adding to WHERE or ON when doing an api.get for this entity
    *
    * Return format is in the form of fieldname => clauses starting with an operator. e.g.:
-   * @code
+   * ```
    *   array(
    *     'location_type_id' => array('IS NOT NULL', 'IN (1,2,3)')
    *   )
-   * @endcode
+   * ```
    *
    * Note that all array keys must be actual field names in this entity. Use subqueries to filter on other tables e.g. custom values.
    *
index f6a8e2b657f0cd4b4b113010e2bc4f17fa809f40..9e292fdb5a4f6e9b372d183253527344c46c7a9d 100644 (file)
@@ -208,6 +208,66 @@ class CRM_Core_DAO_AllCoreTables {
     return class_exists($baoName) ? $baoName : $daoName;
   }
 
+  /**
+   * Convert possibly underscore separated words to camel case with special handling for 'UF'
+   * e.g membership_payment returns MembershipPayment
+   *
+   * @param string $name
+   * @param bool $legacyV3
+   * @return string
+   */
+  public static function convertEntityNameToCamel(string $name, $legacyV3 = FALSE): string {
+    // This map only applies to APIv3
+    $map = [
+      'acl' => 'Acl',
+      'ACL' => 'Acl',
+      'im' => 'Im',
+      'IM' => 'Im',
+    ];
+    if ($legacyV3 && isset($map[$name])) {
+      return $map[$name];
+    }
+
+    $fragments = explode('_', $name);
+    foreach ($fragments as & $fragment) {
+      $fragment = ucfirst($fragment);
+      // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in without underscores)
+      if (strpos($fragment, 'Uf') === 0 && strlen($name) > 2) {
+        $fragment = 'UF' . ucfirst(substr($fragment, 2));
+      }
+    }
+    // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in underscore-separated)
+    if ($fragments[0] === 'Uf') {
+      $fragments[0] = 'UF';
+    }
+    return implode('', $fragments);
+  }
+
+  /**
+   * Convert CamelCase to snake_case, with special handling for some entity names.
+   *
+   * Eg. Activity returns activity
+   *     UFGroup returns uf_group
+   *     OptionValue returns option_value
+   *
+   * @param string $name
+   *
+   * @return string
+   */
+  public static function convertEntityNameToLower(string $name): string {
+    if ($name === strtolower($name)) {
+      return $name;
+    }
+    if ($name === 'PCP' || $name === 'IM' || $name === 'ACL') {
+      return strtolower($name);
+    }
+    return strtolower(ltrim(str_replace('U_F',
+      'uf',
+      // That's CamelCase, beside an odd UFCamel that is expected as uf_camel
+      preg_replace('/(?=[A-Z])/', '_$0', $name)
+    ), '_'));
+  }
+
   /**
    * Get a list of all DAO classes.
    *
@@ -254,7 +314,8 @@ class CRM_Core_DAO_AllCoreTables {
    *   Ex: 'Contact'.
    */
   public static function getBriefName($className) {
-    return CRM_Utils_Array::value($className, array_flip(self::daoToClass()));
+    $className = self::getCanonicalClassName($className);
+    return array_search($className, self::daoToClass(), TRUE) ?: NULL;
   }
 
   /**
index 66dd5a9d7495c7fc040beaf803c053c399160e91..1401d2ba1da9cce62416b9df818cf69aa02a7f9f 100644 (file)
@@ -24,12 +24,12 @@ class CRM_Core_DAO_Factory {
    * @param string $className
    *
    * @return mixed
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public static function create($className) {
     $type = self::$_classes[$className] ?? NULL;
     if (!$type) {
-      CRM_Core_Error::fatal("class $className not found");
+      throw new CRM_Core_Exception("class $className not found");
     }
 
     $class = self::$_prefix[$type] . $className;
index 416085c733a52fb2fb08a4557d3b9eaeb7505575..a3c4023c0edb89e82936b4486cf82de9151d5042 100644 (file)
@@ -421,7 +421,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
 
     $element = $this->addElement($type, $name, CRM_Utils_String::purifyHTML($label), $attributes, $extra);
     if (HTML_QuickForm::isError($element)) {
-      CRM_Core_Error::fatal(HTML_QuickForm::errorMessage($element));
+      CRM_Core_Error::statusBounce(HTML_QuickForm::errorMessage($element));
     }
 
     if ($inputType == 'color') {
@@ -436,7 +436,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
         $error = $this->addRule($name, ts('%1 is a required field.', [1 => $label]), 'required');
       }
       if (HTML_QuickForm::isError($error)) {
-        CRM_Core_Error::fatal(HTML_QuickForm::errorMessage($element));
+        CRM_Core_Error::statusBounce(HTML_QuickForm::errorMessage($element));
       }
     }
 
@@ -2015,7 +2015,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   public function addEntityRef($name, $label = '', $props = [], $required = FALSE) {
     // Default properties
     $props['api'] = CRM_Utils_Array::value('api', $props, []);
-    $props['entity'] = CRM_Utils_String::convertStringToCamel(CRM_Utils_Array::value('entity', $props, 'Contact'));
+    $props['entity'] = CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel(CRM_Utils_Array::value('entity', $props, 'Contact'));
     $props['class'] = ltrim(CRM_Utils_Array::value('class', $props, '') . ' crm-form-entityref');
 
     if (array_key_exists('create', $props) && empty($props['create'])) {
index 2c36e246397c53381cfb3da7e3cda940b2b6d534..6beb1d669dafc6cf9c783d1e31944a3558206060 100644 (file)
@@ -27,7 +27,7 @@ class CRM_Core_I18n_Form extends CRM_Core_Form {
     $id = CRM_Utils_Request::retrieve('id', 'Int', $this);
     $this->_structure = CRM_Core_I18n_SchemaStructure::columns();
     if (!isset($this->_structure[$table][$field])) {
-      CRM_Core_Error::fatal("$table.$field is not internationalized.");
+      CRM_Core_Error::statusBounce("$table.$field is not internationalized.");
     }
 
     $this->addElement('hidden', 'table', $table);
@@ -106,7 +106,7 @@ class CRM_Core_I18n_Form extends CRM_Core_Form {
 
     // validate table and field
     if (!isset($this->_structure[$table][$field])) {
-      CRM_Core_Error::fatal("$table.$field is not internationalized.");
+      CRM_Core_Error::statusBounce("$table.$field is not internationalized.");
     }
 
     $cols = [];
index b0262dfda7f7bc9a0f86344dcd769e6f506a8689..780ae182fa8933155968e5feb193f2e11e5cc8af 100644 (file)
@@ -102,7 +102,7 @@ class CRM_Core_Invoke {
         return CRM_Utils_System::redirect();
       }
       else {
-        CRM_Core_Error::fatal('You do not have permission to execute this url');
+        CRM_Core_Error::statusBounce('You do not have permission to execute this url');
       }
     }
   }
@@ -224,7 +224,7 @@ class CRM_Core_Invoke {
 
       if (!array_key_exists('page_callback', $item)) {
         CRM_Core_Error::debug('Bad item', $item);
-        CRM_Core_Error::fatal(ts('Bad menu record in database'));
+        CRM_Core_Error::statusBounce(ts('Bad menu record in database'));
       }
 
       // check that we are permissioned to access this page
@@ -307,7 +307,7 @@ class CRM_Core_Invoke {
           $object = new $item['page_callback']($title, TRUE, $mode, NULL, $addSequence);
         }
         else {
-          CRM_Core_Error::fatal();
+          throw new CRM_Core_Exception('Execute supplied menu action');
         }
         $result = $object->run($newArgs, $pageArgs);
       }
index 79e4f552f3d9fe6e6a6625c4819972e3aabb9ece..bd9900df2f984e7b47e71854520c3e39f6d156f4 100644 (file)
@@ -105,13 +105,15 @@ class CRM_Core_Menu {
    *   An XML document defining a list of menu items.
    * @param array $menu
    *   An alterable list of menu items.
+   *
+   * @throws CRM_Core_Exception
    */
   public static function readXML($xml, &$menu) {
     $config = CRM_Core_Config::singleton();
     foreach ($xml->item as $item) {
       if (!(string ) $item->path) {
         CRM_Core_Error::debug('i', $item);
-        CRM_Core_Error::fatal();
+        throw new CRM_Core_Exception('Unable to read XML file');
       }
       $path = (string ) $item->path;
       $menu[$path] = array();
@@ -204,7 +206,7 @@ class CRM_Core_Menu {
    * @param array $menu
    * @param string $path
    *
-   * @throws Exception
+   * @throws CRM_Core_Exception
    */
   public static function fillMenuValues(&$menu, $path) {
     $fieldsToPropagate = array(
@@ -240,15 +242,15 @@ class CRM_Core_Menu {
       return;
     }
 
-    $messages = array();
+    $messages = [];
     foreach ($fieldsToPropagate as $field) {
       if (!$fieldsPresent[$field]) {
         $messages[] = ts("Could not find %1 in path tree",
-          array(1 => $field)
+          [1 => $field]
         );
       }
     }
-    CRM_Core_Error::fatal("'$path': " . implode(', ', $messages));
+    throw new CRM_Core_Exception("'$path': " . implode(', ', $messages));
   }
 
   /**
index 5e7844367c50ed240782782796d7cd72f4c07c9b..f83b170f931f309f924b69f530665899213e4355 100644 (file)
@@ -431,23 +431,45 @@ class CRM_Core_Page {
    * @param bool $condition
    *   Whether to display anything at all. This helps simplify code when a
    *   checkmark should appear if something is true.
+   * @param array $attribs
+   *   Attributes to set or override on the icon element.  Any standard
+   *   attribute can be unset by setting the value to an empty string.
    *
    * @return string
    *   The whole bit to drop in.
    */
-  public static function crmIcon($icon, $text = NULL, $condition = TRUE) {
+  public static function crmIcon($icon, $text = NULL, $condition = TRUE, $attribs = []) {
     if (!$condition) {
       return '';
     }
+
+    // Add icon classes to any that might exist in $attribs
+    $classes = array_key_exists('class', $attribs) ? explode(' ', $attribs['class']) : [];
+    $classes[] = 'crm-i';
+    $classes[] = $icon;
+    $attribs['class'] = implode(' ', array_unique($classes));
+
+    $standardAttribs = ['aria-hidden' => 'true'];
     if ($text === NULL || $text === '') {
       $title = $sr = '';
     }
     else {
-      $text = htmlspecialchars($text);
-      $title = " title=\"$text\"";
+      $standardAttribs['title'] = $text;
       $sr = "<span class=\"sr-only\">$text</span>";
     }
-    return "<i class=\"crm-i $icon\"$title></i>$sr";
+
+    // Assemble attribs
+    $attribString = '';
+    // Strip out title if $attribs specifies a blank title
+    $attribs = array_merge($standardAttribs, $attribs);
+    foreach ($attribs as $attrib => $val) {
+      if (strlen($val)) {
+        $val = htmlspecialchars($val);
+        $attribString .= " $attrib=\"$val\"";
+      }
+    }
+
+    return "<i$attribString></i>$sr";
   }
 
 }
index 90be62a20bc28528352935618d5850b7d299e0e2..15141ae4f021258de5af189e17af8310f6fc662c 100644 (file)
@@ -3,13 +3,13 @@
 /**
  * Placeholder page which generates a redirect
  *
- * @code
+ * ```
  * <item>
  *   <path>civicrm/admin/options/case_type</path>
  *   <page_callback>CRM_Core_Page_Redirect</page_callback>
  *   <page_arguments>url=civicrm/foo/bar?whiz=bang&amp;passthru=%%passthru%%</page_arguments>
  * </item>
- * @endcoe
+ * ```
  */
 class CRM_Core_Page_Redirect extends CRM_Core_Page {
 
index 2da96d6cc4926b0f150a3590afac2bc54af9cd9e..2ad490ac4da1ddd2f317c1c5b18c2f3ddd84246d 100644 (file)
@@ -85,7 +85,7 @@ class CRM_Core_Payment_BaseIPN {
    *
    * @return bool
    */
-  public function validateData(&$input, &$ids, &$objects, $required = TRUE, $paymentProcessorID = NULL) {
+  public function validateData($input, &$ids, &$objects, $required = TRUE, $paymentProcessorID = NULL) {
 
     // Check if the contribution exists
     // make sure contribution exists and is valid
@@ -158,7 +158,7 @@ class CRM_Core_Payment_BaseIPN {
    *
    * @return bool|array
    */
-  public function loadObjects(&$input, &$ids, &$objects, $required, $paymentProcessorID, $error_handling = NULL) {
+  public function loadObjects($input, &$ids, &$objects, $required, $paymentProcessorID, $error_handling = NULL) {
     if (empty($error_handling)) {
       // default options are that we log an error & echo it out
       // note that we should refactor this error handling into error code @ some point
index a91c126b8c113271110d79e4bbbb0aea66ab7808..b3f5a066f17114eeb6382021a20882e145dda3ea 100644 (file)
@@ -170,9 +170,10 @@ class CRM_Core_Permission_Base {
    * @param string $permissionName
    *   Name of the permission we are interested in.
    *
+   * @throws CRM_Core_Exception.
    */
   public function permissionEmails($permissionName) {
-    CRM_Core_Error::fatal("this function only works in Drupal 6 at the moment");
+    throw new CRM_Core_Exception("this function only works in Drupal 6 at the moment");
   }
 
   /**
@@ -181,9 +182,10 @@ class CRM_Core_Permission_Base {
    * @param string $roleName
    *   Name of the role we are interested in.
    *
+   * @throws CRM_Core_Exception.
    */
   public function roleEmails($roleName) {
-    CRM_Core_Error::fatal("this function only works in Drupal 6 at the moment");
+    throw new CRM_Core_Exception("this function only works in Drupal 6 at the moment");
   }
 
   /**
index afbb0b5fbb4120b2515db85bd44c6f5d191809dd..911189b5e12f9b4ba7f64cb7b9c0df5dcc01ab76 100644 (file)
@@ -194,7 +194,7 @@ class CRM_Core_PseudoConstant {
       'fresh' => FALSE,
       'context' => $context,
     ];
-    $entity = CRM_Core_DAO_AllCoreTables::getBriefName(CRM_Core_DAO_AllCoreTables::getCanonicalClassName($daoName));
+    $entity = CRM_Core_DAO_AllCoreTables::getBriefName($daoName);
 
     // Custom fields are not in the schema
     if (strpos($fieldName, 'custom_') === 0 && is_numeric($fieldName[7])) {
index b1809af63629f209e129148ec10e6e2f5659782d..5d101d1d70ff0cc9e682fe5044c6a951cb2dd743 100644 (file)
@@ -68,7 +68,7 @@ class CRM_Core_Region {
   /**
    * Add a snippet of content to a region.
    *
-   * @code
+   * ```
    * CRM_Core_Region::instance('page-header')->add(array(
    *   'markup' => '<div style="color:red">Hello!</div>',
    * ));
@@ -81,7 +81,7 @@ class CRM_Core_Region {
    * CRM_Core_Region::instance('page-header')->add(array(
    *   'callback' => 'myextension_callback_function',
    * ));
-   * @endcode
+   * ```
    *
    * 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.
index e45873d4dbbf55e53919283b75428c7e1e78ee72..86fcab419aa1565c46634f7cdddcc6ab71caabb0 100644 (file)
@@ -258,14 +258,14 @@ class CRM_Core_Smarty extends Smarty {
   /**
    * Temporarily assign a list of variables.
    *
-   * @code
+   * ```
    * $smarty->pushScope(array(
    *   'first_name' => 'Alice',
    *   'last_name' => 'roberts',
    * ));
    * $html = $smarty->fetch('view-contact.tpl');
    * $smarty->popScope();
-   * @endcode
+   * ```
    *
    * @param array $vars
    *   (string $name => mixed $value).
index 66bb8bcb4fdac8098422820dd4d87ae1d74c588a..3e0e0280038625d4fbed68b8edbee3b8340e52df 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Example:
  *
- * @code
+ * ```
  * {tsScope x=1}
  *   Expect {$x}==1
  *   {tsScope x=2}
@@ -13,7 +13,7 @@
  *   {/tsScope}
  *   Expect {$x}==1
  * {/tsScope}
- * @endcode
+ * ```
  *
  * @param array $params
  *   Must define 'name'.
index 0cdad70186ec367da3a5f16fe5a6a76b0839be87..8fb0213b497c68c2e5446f7ffc0eec0ccc5fa473 100644 (file)
@@ -25,6 +25,7 @@
  * @param $params
  *   - condition: if present and falsey, return empty
  *   - icon: the icon class to display instead of fa-check
+ *   - anything else is passed along as attributes for the icon
  *
  * @param $text
  *   The translated text to include in the icon's title and screen-reader text.
@@ -36,5 +37,9 @@
 function smarty_block_icon($params, $text, &$smarty) {
   $condition = array_key_exists('condition', $params) ? $params['condition'] : 1;
   $icon = $params['icon'] ?? 'fa-check';
-  return CRM_Core_Page::crmIcon($icon, $text, $condition);
+  $dontPass = [
+    'condition' => 1,
+    'icon' => 1,
+  ];
+  return CRM_Core_Page::crmIcon($icon, $text, $condition, array_diff_key($params, $dontPass));
 }
index e25693c93a56fec4216f073dde485613e4057036..6f9ab7c617055fffd29104b4418b12a8548098a2 100644 (file)
 /**
  * Display the CiviCRM version
  *
- * @code
+ * ```
  * The version is {crmVersion}.
  *
  * {crmVersion redact=auto assign=ver}The version is {$ver}.
- * @endcode
+ * ```
  *
  * @param $params
  * @param $smarty
index d8853b8ef8cdff4b6ea0802a32e832b9d2d630d9..ab0cf4b3f800fed1bcbab1e5a1d246283c277cdd 100644 (file)
@@ -14,9 +14,9 @@
  *
  * To ensure that they throw exceptions, use:
  *
- * @code
+ * ```
  * $errorScope = CRM_Core_TemporaryErrorScope::useException();
- * @endcode
+ * ```
  *
  * Note that relying on this is a code-smell: it can be
  * safe to temporarily switch to exception
index 04be629fb36f7ce17b522544cc93b79f8f1cf7fd..a2297bee1f2db42c7164fe64ffc046bc5da6d340 100644 (file)
@@ -28,7 +28,7 @@
  *
  * Examples:
  *
- * @code
+ * ```
  * // Some business logic using the helper functions
  * function my_business_logic() {
  *   CRM_Core_Transaction::create()->run(function($tx) {
@@ -60,7 +60,7 @@
  *   }
  * }
  *
- * @endcode
+ * ```
  *
  * Note: As of 4.6, the transaction manager supports both reference-counting and nested
  * transactions (SAVEPOINTs). In the past, it only supported reference-counting. The two cases
index 4547e4c173de2faaa05f8ce4a0a5695cd680ea58..14afb06fe5ba23bdd9b3f502c2eddab132cd7b3e 100644 (file)
@@ -439,33 +439,13 @@ class CRM_Event_Form_Registration_Confirm extends CRM_Event_Form_Registration {
     $fields = [];
     foreach ($params as $key => $value) {
       CRM_Event_Form_Registration_Confirm::fixLocationFields($value, $fields, $this);
-      //unset the billing parameters if it is pay later mode
-      //to avoid creation of billing location
-      // @todo - the reasoning for this is unclear - elsewhere we check what fields are provided by
-      // the form & if billing fields exist we create the address, relying on the form to collect
-      // only information we intend to store.
       if ($this->_allowWaitlist
         || $this->_requireApproval
         || (!empty($value['is_pay_later']) && !$this->_isBillingAddressRequiredForPayLater)
         || empty($value['is_primary'])
       ) {
-        $billingFields = [
-          "email-{$this->_bltID}",
-          'billing_first_name',
-          'billing_middle_name',
-          'billing_last_name',
-          "billing_street_address-{$this->_bltID}",
-          "billing_city-{$this->_bltID}",
-          "billing_state_province-{$this->_bltID}",
-          "billing_state_province_id-{$this->_bltID}",
-          "billing_postal_code-{$this->_bltID}",
-          "billing_country-{$this->_bltID}",
-          "billing_country_id-{$this->_bltID}",
-          "address_name-{$this->_bltID}",
-        ];
-        foreach ($billingFields as $field) {
-          unset($value[$field]);
-        }
+        // This is confusing because unnecessary code around it has been removed. It is not
+        // clear why we do this / whether we should.
         if (!empty($value['is_pay_later'])) {
           $this->_values['params']['is_pay_later'] = TRUE;
         }
index 46ee694de39a6fe71101dbb11a04b4b14faa4751..455a6a88248d5fa4f3a7a7072140ef28ab88d0bb 100644 (file)
@@ -1558,8 +1558,8 @@ ORDER BY   civicrm_email.is_bulkmail DESC
         // correct template IDs here
         'override_verp' => TRUE,
         'forward_replies' => FALSE,
-        'open_tracking' => TRUE,
-        'url_tracking' => TRUE,
+        'open_tracking' => Civi::settings()->get('open_tracking_default'),
+        'url_tracking' => Civi::settings()->get('url_tracking_default'),
         'visibility' => 'Public Pages',
         'replyto_email' => $domain_email,
         'header_id' => CRM_Mailing_PseudoConstant::defaultComponent('header_id', ''),
index 110e07bc919987a9038a1ecb378ca9bfd99b8e04..f7bc774add4f107ae9ece1ce8d6864d90a7d0295 100644 (file)
  * To ensure that PHP errors or unhandled exceptions are reported in JSON
  * format, wrap this around your code. For example:
  *
- * @code
+ * ```
  * $errorContainer = new CRM_Queue_ErrorPolicy();
  * $errorContainer->call(function() {
  *    ...include some files, do some work, etc...
  * });
- * @endcode
+ * ```
  *
  * Note: Most of the code in this class is pretty generic vis-a-vis error
  * handling -- except for 'reportError', whose message format is only
index c86f790ad4690abba7f467de7c754eefdfba3f35..60d2521cba1507852862a7cc55f2e201ea9a3777 100644 (file)
@@ -15,7 +15,7 @@
  * different queue-providers may store the queue content in different
  * ways (in memory, in SQL, or in an external service).
  *
- * @code
+ * ```
  * $queue = CRM_Queue_Service::singleton()->create(array(
  *   'type' => 'interactive',
  *   'name' => 'upgrade-tasks',
@@ -31,7 +31,7 @@
  *     $queue->releaseItem($item);
  *   }
  * }
- * @endcode
+ * ```
  */
 class CRM_Queue_Service {
 
index 9d4c12d22a02607edeaaa8e4f8a28d04b4704ece..73c1369b3a9f06be969dd9d0f954563ba163a136 100644 (file)
@@ -640,7 +640,8 @@ UNION ALL
     $contributionTypes = CRM_Contribute_PseudoConstant::financialType();
     $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'label');
     $paymentInstruments = CRM_Contribute_PseudoConstant::paymentInstrument();
-    $contributionPages = CRM_Contribute_PseudoConstant::contributionPage();
+    // We pass in TRUE as 2nd param so that even disabled contribution page titles are returned and replaced in the report
+    $contributionPages = CRM_Contribute_PseudoConstant::contributionPage(NULL, TRUE);
     $batches = CRM_Batch_BAO_Batch::getBatches();
     foreach ($rows as $rowNum => $row) {
       if (!empty($this->_noRepeats) && $this->_outputMode != 'csv') {
index f92723c33726ff4845aeda86e1b5f8fd8fc708f1..fa87a1cf8b03de2b87eb7741b1e317ca4b6edfd1 100644 (file)
@@ -21,7 +21,7 @@
  *   - "match-mandatory" will generate an error
  *   - "match" will allow action to proceed -- thus inserting a new record
  *
- * @code
+ * ```
  * $result = civicrm_api('contact', 'create', array(
  *   'options' => array(
  *     'match' => array('last_name', 'first_name')
@@ -30,7 +30,7 @@
  *   'last_name' => 'Lebowski',
  *   'nick_name' => 'The Dude',
  * ));
- * @endcode
+ * ```
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
index 764d81bf80c5cc153792e3ae14ce87dae2ae7ee0..3e320d5c1408d94c277b966765c4603f41fbafb4 100644 (file)
  * Implement the "reload" option. This option can be used with "create" to force
  * the API to reload a clean copy of the entity before returning the result.
  *
- * @code
+ * ```
  * $clean = civicrm_api('myentity', 'create', array(
  *   'options' => array(
  *     'reload' => 1
  *   ),
  * ));
- * @endcode
+ * ```
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
index 48e0cf9e2df2da96e23b618e45fd25b7d4995099..1be38b0ece20ac11d069018901a3d69458914395 100644 (file)
@@ -29,14 +29,14 @@ class CRM_Utils_AutoClean {
   /**
    * Call a cleanup function when the current context shuts down.
    *
-   * @code
+   * ```
    * function doStuff() {
    *   $ac = CRM_Utils_AutoClean::with(function(){
    *     MyCleanup::doIt();
    *   });
    *   ...
    * }
-   * @endcode
+   * ```
    *
    * @param mixed $callback
    * @return CRM_Utils_AutoClean
@@ -52,12 +52,12 @@ class CRM_Utils_AutoClean {
    * Temporarily swap values using callback functions, and cleanup
    * when the current context shuts down.
    *
-   * @code
+   * ```
    * function doStuff() {
    *   $ac = CRM_Utils_AutoClean::swap('My::get', 'My::set', 'tmpValue');
    *   ...
    * }
-   * @endcode
+   * ```
    *
    * @param mixed $getter
    *   Function to lookup current value.
index 75cb7eaec82df2092869cdc4cd54c5b259badfbc..f7adb10695b1c6a6272bfe0fec1d3cc707a91752 100644 (file)
 /**
  * Capture the output from the console, copy it to a file, and pass it on.
  *
- * @code
+ * ```
  * $tee = CRM_Utils_ConsoleTee::create()->start();
  * echo "hello world";
  * $tee->stop();
  * assertEquals("hello world", file_get_contents($tee->getFileName()));
- * @endCode
+ * ```
  *
  * Loosely speaking, it serves a similar purpose to Unix `tee`.
  *
index fef296edbcdcf0a548534ef565e97a66f2ec38bc..a20246fe6b200ecc87f11514c4e31c375de1e637 100644 (file)
  * This is a quick-and-dirty way to define a vaguely-class-ish structure. It's non-performant, abnormal,
  * and not a complete OOP system. Only use for testing/mocking.
  *
- * @code
+ * ```
  * $object = new CRM_Utils_FakeObject(array(
  *   'doIt' => function() {  print "It!\n"; }
  * ));
  * $object->doIt();
- * @endcode
+ * ```
  */
 class CRM_Utils_FakeObject {
 
index 047de04c3c7d1038f12fe87ea6408546468626c5..50403d9546ea2018b1ca2cd62b91dc849918c4fb 100644 (file)
@@ -17,7 +17,7 @@
 /**
  * Temporarily change a global variable.
  *
- * @code
+ * ```
  * $globals = CRM_Utils_GlobalStack::singleton();
  * $globals->push(array(
  *   '_GET' => array(
@@ -26,7 +26,7 @@
  * ));
  * ...do stuff...
  * $globals->pop();
- * @endcode
+ * ```
  *
  * Note: for purposes of this class, we'll refer to the array passed into
  * push() as a frame.
index 6e8a7b59100384c26592473f327d15b69daddd1a..edad19c9b58661b625dda6c4d413e307db6dcfd4 100644 (file)
@@ -2234,10 +2234,8 @@ abstract class CRM_Utils_Hook {
    *      If omitted, default to "array('civicrm/a')" for backward compat.
    *      For a utility that should only be loaded on-demand, use "array()".
    *      For a utility that should be loaded in all pages use, "array('*')".
-   * @return null
-   *   the return value is ignored
    *
-   * @code
+   * ```
    * function mymod_civicrm_angularModules(&$angularModules) {
    *   $angularModules['myAngularModule'] = array(
    *     'ext' => 'org.example.mymod',
@@ -2252,7 +2250,10 @@ abstract class CRM_Utils_Hook {
    *     'basePages' => array('civicrm/a'),
    *   );
    * }
-   * @endcode
+   * ```
+   *
+   * @return null
+   *   the return value is ignored
    */
   public static function angularModules(&$angularModules) {
     return self::singleton()->invoke(['angularModules'], $angularModules,
@@ -2266,7 +2267,7 @@ abstract class CRM_Utils_Hook {
    *
    * @param \Civi\Angular\Manager $angular
    *
-   * @code
+   * ```
    * function example_civicrm_alterAngular($angular) {
    *   $changeSet = \Civi\Angular\ChangeSet::create('mychanges')
    *     ->alterHtml('~/crmMailing/EditMailingCtrl/2step.html', function(phpQueryObject $doc) {
@@ -2275,7 +2276,7 @@ abstract class CRM_Utils_Hook {
    *   );
    *   $angular->add($changeSet);
    * }
-   * @endCode
+   * ```
    */
   public static function alterAngular($angular) {
     $event = \Civi\Core\Event\GenericHookEvent::create([
@@ -2365,7 +2366,7 @@ abstract class CRM_Utils_Hook {
   /**
    * Modify the CiviCRM container - add new services, parameters, extensions, etc.
    *
-   * @code
+   * ```
    * use Symfony\Component\Config\Resource\FileResource;
    * use Symfony\Component\DependencyInjection\Definition;
    *
@@ -2373,7 +2374,7 @@ abstract class CRM_Utils_Hook {
    *   $container->addResource(new FileResource(__FILE__));
    *   $container->setDefinition('mysvc', new Definition('My\Class', array()));
    * }
-   * @endcode
+   * ```
    *
    * Tip: The container configuration will be compiled/cached. The default cache
    * behavior is aggressive. When you first implement the hook, be sure to
index 76a440e26a2cd2bbc6dc3e7b07701db8e9ee1337..7dc6b17d7f67b6babea222205437a2a21f9414bb 100644 (file)
@@ -22,7 +22,7 @@ class CRM_Utils_Migrate_Export {
    * @var array
    * Description of export field mapping
    *
-   * @code
+   * ```
    * 'exampleEntityMappingName' => array(
    *   'data' => array(),                     // placeholder; this will get filled-in during execution
    *   'name' => 'CustomGroup',               // per-item XML tag name
@@ -31,7 +31,7 @@ class CRM_Utils_Migrate_Export {
    *   'idNameFields' => array('id', 'name'), // name of the (local/autogenerated) "id" and (portable) "name" columns
    *   'idNameMap' => array(),                // placeholder; this will get filled-in during execution
    * ),
-   * @endcode
+   * ```
    */
   protected $_xml;
 
index 159f56a5fad60f2832034fc54a100a36023f2d06..20125db2bdad4d4f45e991306069888f5655abd0 100644 (file)
@@ -191,11 +191,11 @@ class CRM_Utils_SQL_BaseParamQuery implements ArrayAccess {
   /**
    * Get the value of a SQL parameter.
    *
-   * @code
+   * ```
    *   $select['cid'] = 123;
    *   $select->where('contact.id = #cid');
    *   echo $select['cid'];
-   * @endCode
+   * ```
    *
    * @param string $offset
    * @return mixed
@@ -209,11 +209,11 @@ class CRM_Utils_SQL_BaseParamQuery implements ArrayAccess {
   /**
    * Set the value of a SQL parameter.
    *
-   * @code
+   * ```
    *   $select['cid'] = 123;
    *   $select->where('contact.id = #cid');
    *   echo $select['cid'];
-   * @endCode
+   * ```
    *
    * @param string $offset
    * @param mixed $value
index 6bbc076691c71146d757d848c3d1fdddd0ee7bdc..fcb5443030cc6afe153a3548f977cad9dba35cb3 100644 (file)
@@ -13,7 +13,7 @@
  * Dear God Why Do I Have To Write This (Dumb SQL Builder)
  *
  * Usage:
- * @code
+ * ```
  * $del = CRM_Utils_SQL_Delete::from('civicrm_activity act')
  *     ->where('activity_type_id = #type', array('type' => 234))
  *     ->where('status_id IN (#statuses)', array('statuses' => array(1,2,3))
@@ -24,7 +24,7 @@
  *        'value' => $form['foo']
  *      ))
  * echo $del->toSQL();
- * @endcode
+ * ```
  *
  * Design principles:
  *  - Portable
@@ -48,7 +48,7 @@
  * xor output. The notations for input and output interpolation are a bit different,
  * and they may not be mixed.
  *
- * @code
+ * ```
  * // Interpolate on input. Set params when using them.
  * $select->where('activity_type_id = #type', array(
  *   'type' => 234,
@@ -58,7 +58,7 @@
  * $select
  *     ->where('activity_type_id = #type')
  *     ->param('type', 234),
- * @endcode
+ * ```
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
index 47707a316fd8727a4d31f95eddcd6e064325e8e7..4aa6b7e5166974cd334df51fb6b1f27c0dd986b5 100644 (file)
@@ -13,7 +13,7 @@
  * Dear God Why Do I Have To Write This (Dumb SQL Builder)
  *
  * Usage:
- * @code
+ * ```
  * $select = CRM_Utils_SQL_Select::from('civicrm_activity act')
  *     ->join('absence', 'inner join civicrm_activity absence on absence.id = act.source_record_id')
  *     ->where('activity_type_id = #type', array('type' => 234))
@@ -25,7 +25,7 @@
  *        'value' => $form['foo']
  *      ))
  * echo $select->toSQL();
- * @endcode
+ * ```
  *
  * Design principles:
  *  - Portable
@@ -49,7 +49,7 @@
  * xor output. The notations for input and output interpolation are a bit different,
  * and they may not be mixed.
  *
- * @code
+ * ```
  * // Interpolate on input. Set params when using them.
  * $select->where('activity_type_id = #type', array(
  *   'type' => 234,
@@ -59,7 +59,7 @@
  * $select
  *     ->where('activity_type_id = #type')
  *     ->param('type', 234),
- * @endcode
+ * ```
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
index eb902bc18949f199adac6968755bad5d42c4c706..2b6491f9d0b778b7d1bc931a045a1c3393cf68d6 100644 (file)
@@ -20,7 +20,7 @@
  *
  * FIXME: Add TTL support?
  *
- * @code
+ * ```
  * $signer = new CRM_Utils_Signer('myprivatekey', array('param1','param2'));
  * $params = array(
  *   'param1' => 'hello',
@@ -29,7 +29,7 @@
  * $token = $signer->sign($params);
  * ...
  * assertTrue($signer->validate($token, $params));
- * @endcode
+ * ```
  */
 class CRM_Utils_Signer {
   /**
index 18d4904b1f161c5c99df730b435eb535a86c57e4..e0cac9a763ba167433a6c45677831032d4f5e4cf 100644 (file)
@@ -86,37 +86,17 @@ class CRM_Utils_String {
   }
 
   /**
-   * Convert possibly underscore separated words to camel case with special handling for 'UF'
-   * e.g membership_payment returns MembershipPayment
-   *
-   * @param string $string
+   * Convert possibly underscore separated words to camel case.
    *
+   * @param string $str
+   * @param bool $ucFirst
+   *   Should the first letter be capitalized like `CamelCase` or lower like `camelCase`
    * @return string
    */
-  public static function convertStringToCamel($string) {
-    $map = [
-      'acl' => 'Acl',
-      'ACL' => 'Acl',
-      'im' => 'Im',
-      'IM' => 'Im',
-    ];
-    if (isset($map[$string])) {
-      return $map[$string];
-    }
-
-    $fragments = explode('_', $string);
-    foreach ($fragments as & $fragment) {
-      $fragment = ucfirst($fragment);
-      // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in without underscores)
-      if (strpos($fragment, 'Uf') === 0 && strlen($string) > 2) {
-        $fragment = 'UF' . ucfirst(substr($fragment, 2));
-      }
-    }
-    // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in underscore-separated)
-    if ($fragments[0] === 'Uf') {
-      $fragments[0] = 'UF';
-    }
-    return implode('', $fragments);
+  public static function convertStringToCamel($str, $ucFirst = TRUE) {
+    $fragments = explode('_', $str);
+    $camel = implode('', array_map('ucfirst', $fragments));
+    return $ucFirst ? $camel : lcfirst($camel);
   }
 
   /**
index 82fe4d85e47fd4f8b6442937310e08d081b29574..6e42f703b8814c1f36bd5ecf6372bcea2ff57ae9 100644 (file)
@@ -654,13 +654,13 @@ class CRM_Utils_System_Drupal8 extends CRM_Utils_System_DrupalBase {
    *
    * For example, 'civicrm/contact/view?reset=1&cid=66' will be returned as:
    *
-   * @code
+   * ```
    * array(
    *   'path' => 'civicrm/contact/view',
    *   'route' => 'civicrm.civicrm_contact_view',
    *   'query' => array('reset' => '1', 'cid' => '66'),
    * );
-   * @endcode
+   * ```
    *
    * @param string $url
    *   The url to parse.
index ebd09e8afec737e387d067a398a7a6c79e51f439..e52dd1614849e230584e55ccfbdb6b1733d67e3a 100644 (file)
--- a/Civi.php
+++ b/Civi.php
@@ -19,9 +19,9 @@ class Civi {
   /**
    * A central location for static variable storage.
    * @var array
-   * @code
+   * ```
    * `Civi::$statics[__CLASS__]['foo'] = 'bar';
-   * @endcode
+   * ```
    */
   public static $statics = array();
 
index 22fa382a7104c953555db1fa02cde56b6e8dc1ca..5d0457ef7d2933bed191870593027c0349824ff5 100644 (file)
@@ -74,7 +74,7 @@ class Request {
    * @return string
    */
   public static function normalizeEntityName($entity) {
-    return \CRM_Utils_String::convertStringToCamel(\CRM_Utils_String::munge($entity));
+    return \CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel(\CRM_Utils_String::munge($entity), TRUE);
   }
 
   /**
index ff4f79256e1a23456f107aa9c9febf708240991a..e76220ca0dc0785a4723b000073865fbb0760e93 100644 (file)
@@ -18,7 +18,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  * The ChainSubscriber looks for API parameters which specify a nested or
  * chained API call. For example:
  *
- * @code
+ * ```
  * $result = civicrm_api('Contact', 'create', array(
  *   'version' => 3,
  *   'first_name' => 'Amy',
@@ -27,7 +27,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  *     'location_type_id' => 123,
  *   ),
  * ));
- * @endcode
+ * ```
  *
  * The ChainSubscriber looks for any parameters of the form "api.Email.create";
  * if found, it issues the nested API call (and passes some extra context --
index 6a19cded035058c8a9d8ac931e7cfd27e95cda45..3aaa1a89c62ff064b2f8a74a01b7e03f6c87bc08 100644 (file)
@@ -12,6 +12,7 @@
 namespace Civi\API\Subscriber;
 
 use Civi\API\Events;
+use CRM_Core_DAO_AllCoreTables as AllCoreTables;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -123,7 +124,7 @@ class DynamicFKAuthorization implements EventSubscriberInterface {
    */
   public function __construct($kernel, $entityName, $actions, $lookupDelegateSql, $lookupCustomFieldSql, $allowedDelegates = NULL) {
     $this->kernel = $kernel;
-    $this->entityName = \CRM_Utils_String::convertStringToCamel($entityName);
+    $this->entityName = AllCoreTables::convertEntityNameToCamel($entityName, TRUE);
     $this->actions = $actions;
     $this->lookupDelegateSql = $lookupDelegateSql;
     $this->lookupCustomFieldSql = $lookupCustomFieldSql;
@@ -138,7 +139,7 @@ class DynamicFKAuthorization implements EventSubscriberInterface {
    */
   public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) {
     $apiRequest = $event->getApiRequest();
-    if ($apiRequest['version'] == 3 && \CRM_Utils_String::convertStringToCamel($apiRequest['entity']) == $this->entityName && in_array(strtolower($apiRequest['action']), $this->actions)) {
+    if ($apiRequest['version'] == 3 && AllCoreTables::convertEntityNameToCamel($apiRequest['entity'], TRUE) == $this->entityName && in_array(strtolower($apiRequest['action']), $this->actions)) {
       if (isset($apiRequest['params']['field_name'])) {
         $fldIdx = \CRM_Utils_Array::index(['field_name'], $this->getCustomFields());
         if (empty($fldIdx[$apiRequest['params']['field_name']])) {
index c17b41cb2e67ecc082c141589e44edb78cfee215..5150b362f82827bbb946d5dbe1bc8cc990aef636 100644 (file)
@@ -14,14 +14,14 @@ namespace Civi\API;
  * A WhitelistRule is used to determine if an API call is authorized.
  * For example:
  *
- * @code
+ * ```
  * new WhitelistRule(array(
  *   'entity' => 'Contact',
  *   'actions' => array('get','getsingle'),
  *   'required' => array('contact_type' => 'Organization'),
  *   'fields' => array('id', 'display_name', 'sort_name', 'created_date'),
  * ));
- * @endcode
+ * ```
  *
  * This rule would allow API requests that attempt to get contacts of type "Organization",
  * but only a handful of fields ('id', 'display_name', 'sort_name', 'created_date')
index 0158b41e7d43b2440d4f824b3104d010576aca9b..873951416995823b18a972b6d6f636c5f59d0e35 100644 (file)
@@ -13,20 +13,20 @@ use Symfony\Component\EventDispatcher\Event;
  *
  * The basic mailing query looks a bit like this (depending on configuration):
  *
- * @code
+ * ```
  * SELECT reminder.id AS reminderID, reminder.contact_id as contactID, ...
  * FROM `civicrm_action_log` reminder
  * ... JOIN `target_entity` e ON e.id = reminder.entity_id ...
  * WHERE reminder.action_schedule_id = #casActionScheduleId
- * @endcode
+ * ```
  *
  * Listeners may modify the query. For example, suppose we want to load
  * additional fields from the related 'foo' entity:
  *
- * @code
+ * ```
  * $event->query->join('foo', '!casMailingJoinType civicrm_foo foo ON foo.myentity_id = e.id')
  *   ->select('foo.bar_value AS bar');
- * @endcode
+ * ```
  *
  * There are several parameters pre-set for use in queries:
  *  - 'casActionScheduleId'
index a54bca3b83ea1ccb150c94f8218498c7f14c4fd9..c8a32dd97e3756e8125fb2e31b881a12f0fe4b0d 100644 (file)
@@ -37,7 +37,7 @@ namespace Civi\ActionSchedule;
  * to fire the reminders X days after the registration date. The
  * MappingInterface::createQuery() could return a query like:
  *
- * @code
+ * ```
  * CRM_Utils_SQL_Select::from('civicrm_participant e')
  *   ->join('event', 'INNER JOIN civicrm_event event ON e.event_id = event.id')
  *   ->where('e.is_pay_later = 1')
@@ -46,7 +46,7 @@ namespace Civi\ActionSchedule;
  *   ->param('casDateField', 'e.register_date')
  *   ->param($defaultParams)
  *   ...etc...
- * @endcode
+ * ```
  *
  * In the RELATION_FIRST phase, RecipientBuilder adds a LEFT-JOIN+WHERE to find
  * participants who have *not* yet received any reminder, and filters those
index 58e69987004adee665fd985cc26657b44038c291..1dca6bcd9e1d9c61bb30e4620ee8381675620d1e 100644 (file)
@@ -8,12 +8,12 @@ namespace Civi\Angular;
  * The AngularLoader stops short of bootstrapping AngularJS. You may
  * need to `<div ng-app="..."></div>` or `angular.bootstrap(...)`.
  *
- * @code
+ * ```
  * $loader = new AngularLoader();
  * $loader->setPageName('civicrm/case/a');
  * $loader->setModules(array('crmApp'));
  * $loader->load();
- * @endCode
+ * ```
  *
  * @link https://docs.angularjs.org/guide/bootstrap
  */
index 74d28bfb33041ef5f1e90596131253369c08c52f..8a1ed4e86286838b887f5b35a54644ea97c34a4d 100644 (file)
@@ -66,10 +66,9 @@ abstract class AbstractEntity {
     $permissions = \CRM_Core_Permission::getEntityActionPermissions();
 
     // For legacy reasons the permissions are keyed by lowercase entity name
-    // Note: Convert to camel & back in order to circumvent all the api3 naming oddities
-    $lcentity = _civicrm_api_get_entity_name_from_camel(\CRM_Utils_String::convertStringToCamel(self::getEntityName()));
+    $lcentity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToLower(self::getEntityName());
     // Merge permissions for this entity with the defaults
-    return \CRM_Utils_Array::value($lcentity, $permissions, []) + $permissions['default'];
+    return ($permissions[$lcentity] ?? []) + $permissions['default'];
   }
 
   /**
index 82e2141461b1e15e8ab74ce669683b9eac168a0a..155c32c77970df4d7c2462c046c23770009e2010 100644 (file)
@@ -20,7 +20,7 @@ use Civi\Core\Exception\UnknownAssetException;
  * named "api-fields.json" which lists all the fields of
  * all the API entities.
  *
- * @code
+ * ```
  * // Build a URL to `api-fields.json`.
  * $url = \Civi::service('asset_builder')->getUrl('api-fields.json');
  *
@@ -37,7 +37,7 @@ use Civi\Core\Exception\UnknownAssetException;
  *   $mimeType = 'application/json';
  *   $content = json_encode($fields);
  * }
- * @endCode
+ * ```
  *
  * Assets can be parameterized. Each combination of ($asset,$params)
  * will be cached separately. For example, we might want a copy of
@@ -45,7 +45,7 @@ use Civi\Core\Exception\UnknownAssetException;
  * Simply pass the chosen entities into `getUrl()`, then update
  * the definition to use `$params['entities']`, as in:
  *
- * @code
+ * ```
  * // Build a URL to `api-fields.json`.
  * $url = \Civi::service('asset_builder')->getUrl('api-fields.json', array(
  *   'entities' => array('Contact', 'Phone', 'Email', 'Address'),
@@ -63,7 +63,7 @@ use Civi\Core\Exception\UnknownAssetException;
  *   $mimeType = 'application/json';
  *   $content = json_encode($fields);
  * }
- * @endCode
+ * ```
  *
  * Note: These assets are designed to hold non-sensitive data, such as
  * aggregated JS or common metadata. There probably are ways to
index 77d91684e25b2b25426a10e243c8153bd673118b..34514f9bf750dffa23203502a5133a5a22e6d0ed 100644 (file)
@@ -7,10 +7,10 @@ namespace Civi\Core;
  * The event inspector is a development tool which provides metadata about events.
  * It can be used for code-generators and documentation-generators.
  *
- * @code
+ * ```
  * $i = new CiviEventInspector();
  * print_r(CRM_Utils_Array::collect('name', $i->getAll()));
- * @endCode
+ * ```
  *
  * An event definition includes these fields:
  *  - type: string, required. Ex: 'hook' or 'object'
index 6c62ea498afdf49140190647915545626d45f4d8..1b29ff76e9a9db9e28ef8d1cf32a3e856d407f6f 100644 (file)
@@ -26,7 +26,7 @@ namespace Civi\Core\Event;
  * and methods. This requires some kind of mapping. `GenericHookEvent`
  * maps each parameter to a field (using magic methods):
  *
- * @code
+ * ```
  * // Creating an event object.
  * $event = GenericHookEvent::create(array(
  *   'bar' => 'abc',
@@ -41,7 +41,7 @@ namespace Civi\Core\Event;
  *
  * // Dispatching an event.
  * Civi::dispatcher()->dispatch('hook_civicrm_foo', $event);
- * @endCode
+ * ```
  *
  * Design Discussion:
  *
@@ -56,10 +56,10 @@ namespace Civi\Core\Event;
  * as an array, and all the returned values are merged into one big array.
  * You can add and retrieve return-values using these methods:
  *
- * @code
+ * ```
  * $event->addReturnValues(array(...));
  * foreach ($event->getReturnValues() as $retVal) { ... }
- * @endCode
+ * ```
  */
 class GenericHookEvent extends \Symfony\Component\EventDispatcher\Event {
 
index 379f94d64c3766f609c800f2a8187d72307b045a..a1ebfe2b0fdde0156742fc8276184a5e36ac24db 100644 (file)
@@ -222,11 +222,11 @@ class ResolverApi {
   /**
    * Recursively interpolate values.
    *
-   * @code
+   * ```
    * $params = array('foo' => '@1');
    * $this->interpolate($params, array('@1'=> $object))
    * assert $data['foo'] == $object;
-   * @endcode
+   * ```
    *
    * @param array $array
    *   Array which may or many not contain a mix of tokens.
index 4e94c8568cc3260812f95026d2fbfdd4b0d795fb..0381768218aca43d2adb5ac52b82ad088bf7930b 100644 (file)
@@ -291,7 +291,7 @@ class Requirements {
    * @return array
    */
   public function checkMysqlVersion(array $db_config) {
-    $min = CRM_Upgrade_Incremental_General::MIN_INSTALL_MYSQL_VER;
+    $min = \CRM_Upgrade_Incremental_General::MIN_INSTALL_MYSQL_VER;
     $results = [
       'title' => 'CiviCRM MySQL Version',
       'severity' => $this::REQUIREMENT_OK,
index 50d6ebf31c4a39ee1d97fa1071b29516757eec2d..1d9db12079521969e442e86d704ae96fc8fd90ff 100644 (file)
@@ -100,12 +100,12 @@ class Test {
   /**
    * Create a builder for the headless environment.
    *
-   * @return \Civi\Test\CiviEnvBuilder
-   *
-   * @code
+   * ```
    * \Civi\Test::headless()->apply();
    * \Civi\Test::headless()->sqlFile('ex.sql')->apply();
-   * @endCode
+   * ```
+   *
+   * @return \Civi\Test\CiviEnvBuilder
    */
   public static function headless() {
     $civiRoot = dirname(__DIR__);
@@ -130,12 +130,12 @@ class Test {
   /**
    * Create a builder for end-to-end testing on the live environment.
    *
-   * @return \Civi\Test\CiviEnvBuilder
-   *
-   * @code
+   * ```
    * \Civi\Test::e2e()->apply();
    * \Civi\Test::e2e()->install('foo.bar')->apply();
-   * @endCode
+   * ```
+   *
+   * @return \Civi\Test\CiviEnvBuilder
    */
   public static function e2e() {
     $builder = new \Civi\Test\CiviEnvBuilder('CiviEnvBuilder');
index 6d5421053dcb75835c34c503a2007fad3a06a64f..fbe73cd0fe909038df590eceae14fdc313bdae40 100644 (file)
@@ -302,7 +302,7 @@ trait Api3TestTrait {
    * @throws \Exception
    */
   public function runApi4Legacy($v3Entity, $v3Action, $v3Params = []) {
-    $v4Entity = self::convertEntityNameToApi4($v3Entity);
+    $v4Entity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel($v3Entity);
     $v4Action = $v3Action = strtolower($v3Action);
     $v4Params = ['checkPermissions' => isset($v3Params['check_permissions']) ? (bool) $v3Params['check_permissions'] : FALSE];
     $sequential = !empty($v3Params['sequential']);
@@ -641,9 +641,9 @@ trait Api3TestTrait {
 
     // Handle single api call
     list(, $chainEntity, $chainAction) = explode('.', $key);
-    $lcChainEntity = \_civicrm_api_get_entity_name_from_camel($chainEntity);
-    $chainEntity = self::convertEntityNameToApi4($chainEntity);
-    $lcMainEntity = \_civicrm_api_get_entity_name_from_camel($mainEntity);
+    $lcChainEntity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($chainEntity);
+    $chainEntity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToCamel($chainEntity);
+    $lcMainEntity = \CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($mainEntity);
     $params = is_array($params) ? $params : [];
 
     // Api3 expects this to be inherited
@@ -677,19 +677,4 @@ trait Api3TestTrait {
     return $this->runApi4Legacy($chainEntity, $chainAction, $params);
   }
 
-  /**
-   * Fix the naming differences between api3 & api4 entities.
-   *
-   * @param string $legacyName
-   * @return string
-   */
-  public static function convertEntityNameToApi4($legacyName) {
-    $api4Name = \CRM_Utils_String::convertStringToCamel($legacyName);
-    $map = [
-      'Im' => 'IM',
-      'Acl' => 'ACL',
-    ];
-    return $map[$api4Name] ?? $api4Name;
-  }
-
 }
index 53d5c8cd11226fbeb572ac879dd5a1e69bd872a3..4885cd06772525e0610ac326716ea60dd2c169c4 100644 (file)
@@ -9,13 +9,13 @@ namespace Civi\Test;
  * This interface allows you to subscribe to hooks as part of the test.
  * Simply create an eponymous hook function (e.g. `hook_civicrm_post()`).
  *
- * @code
+ * ```
  * class MyTest extends \PHPUnit_Framework_TestCase implements \Civi\Test\HookInterface {
  *   public function hook_civicrm_post($op, $objectName, $objectId, &$objectRef) {
  *     echo "Running hook_civicrm_post\n";
  *   }
  * }
- * @endCode
+ * ```
  *
  * At time of writing, there are a few limitations in how HookInterface is handled
  * by CiviTestListener:
index ee867b949e895594206032a8223b045ddb7a992b..730ddb616c87c662f83d0be2d0b10eb15ec18264 100644 (file)
@@ -8,7 +8,7 @@ namespace Civi\Token\Event;
  * The TokenRegisterEvent is fired when constructing a list of available
  * tokens. Listeners may register by specifying the entity/field/label for the token.
  *
- * @code
+ * ```
  * $ev->entity('profile')
  *    ->register('viewUrl', ts('Default Profile URL (View Mode)')
  *    ->register('editUrl', ts('Default Profile URL (Edit Mode)');
@@ -17,7 +17,7 @@ namespace Civi\Token\Event;
  *   'field' => 'viewUrl',
  *   'label' => ts('Default Profile URL (View Mode)'),
  * ));
- * @endcode
+ * ```
  *
  * Event name: 'civi.token.list'
  */
index c9a251ccc752ae05c24770372f7c5a1e4ce6470e..ed7a3e2504bb55e0ea096b1f517b4d90ed8db481 100644 (file)
@@ -8,7 +8,7 @@ namespace Civi\Token\Event;
  * A TokenValueEvent is fired to convert raw query data into mergeable
  * tokens. For example:
  *
- * @code
+ * ```
  * $event = new TokenValueEvent($myContext, 'text/html', array(
  *   array('contact_id' => 123),
  *   array('contact_id' => 456),
index cb38ead05b985a388b8458285c5deb2de0979d87..536b6ab237245674db3f83319a4b2ebd482e7e1d 100644 (file)
@@ -11,25 +11,25 @@ namespace Civi\Token;
  * (1) When setting up a job, you may specify general/baseline info.
  * This is called the "context" data. Here, we create two rows:
  *
- * @code
+ * ```
  * $proc->addRow()->context('contact_id', 123);
  * $proc->addRow()->context('contact_id', 456);
- * @endCode
+ * ```
  *
  * (2) When defining a token (eg `{profile.viewUrl}`), you might read the
  * context-data (`contact_id`) and set the token-data (`profile => viewUrl`):
  *
- * @code
+ * ```
  * foreach ($proc->getRows() as $row) {
  *   $row->tokens('profile', [
  *     'viewUrl' => 'http://example.com/profile?cid=' . urlencode($row->context['contact_id'];
  *   ]);
  * }
- * @endCode
+ * ```
  *
  * The context and tokens can be accessed using either methods or attributes.
  *
- * @code
+ * ```
  * # Setting context data
  * $row->context('contact_id', 123);
  * $row->context(['contact_id' => 123]);
@@ -43,7 +43,7 @@ namespace Civi\Token;
  *
  * # Reading token data
  * echo $row->tokens['profile']['viewUrl'];
- * @endCode
+ * ```
  *
  * Note: The methods encourage a "fluent" style. They were written for PHP 5.3
  * (eg before short-array syntax was supported) and are fairly flexible about
index 8208af08db30d36f42b8a9e653f983f18bd69e62..be1946db3735796f65dd091d9d3aece9908d98e6 100644 (file)
@@ -281,33 +281,25 @@ function _civicrm_api_replace_variable($value, $parentResult, $separator) {
  *
  * @return string
  *   Entity name in underscore separated format.
+ *
+ * @deprecated
  */
 function _civicrm_api_get_entity_name_from_camel($entity) {
-  if (!$entity || $entity === strtolower($entity)) {
-    return $entity;
-  }
-  elseif ($entity == 'PCP') {
-    return 'pcp';
-  }
-  else {
-    $entity = ltrim(strtolower(str_replace('U_F',
-          'uf',
-          // That's CamelCase, beside an odd UFCamel that is expected as uf_camel
-          preg_replace('/(?=[A-Z])/', '_$0', $entity)
-        )), '_');
+  if (!$entity) {
+    // @todo - this should not be called when empty.
+    return '';
   }
-  return $entity;
+  return CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($entity);
 }
 
 /**
  * Having a DAO object find the entity name.
  *
- * @param object $bao
+ * @param CRM_Core_DAO $bao
  *   DAO being passed in.
  *
  * @return string
  */
 function _civicrm_api_get_entity_name_from_dao($bao) {
-  $daoName = str_replace("BAO", "DAO", get_class($bao));
-  return CRM_Core_DAO_AllCoreTables::getBriefName($daoName);
+  return CRM_Core_DAO_AllCoreTables::getBriefName(get_class($bao));
 }
index 3e93b97a586b61c37b8e4254b5c9270c70fea061..4ba61dd30bde6c5d50e8e25d839a895f07d596fb 100644 (file)
@@ -4,36 +4,36 @@
  *
  * This class allows to consume the API, either from within a module that knows civicrm already:
  *
- * @code
+ * ```
  *   require_once('api/class.api.php');
  *   $api = new civicrm_api3();
- * @endcode
+ * ```
  *
  * or from any code on the same server as civicrm
  *
- * @code
+ * ```
  *   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));
- * @endcode
+ * ```
  *
  * or to query a remote server via the rest api
  *
- * @code
+ * ```
  *   $api = new civicrm_api3 (array ('server' => 'http://example.org',
  *                                   'api_key'=>'theusersecretkey',
  *                                   'key'=>'thesitesecretkey'));
- * @endcode
+ * ```
  *
  * No matter how initialised and if civicrm is local or remote, you use the class the same way.
  *
- * @code
+ * ```
  *   $api->{entity}->{action}($params);
- * @endcode
+ * ```
  *
  * So, to get the individual contacts:
  *
- * @code
+ * ```
  *   if ($api->Contact->Get(array('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;
  *   } else {
  *     echo $api->errorMsg();
  *   }
- * @endcode
+ * ```
  *
  * Or, to create an event:
  *
- * @code
+ * ```
  *   if ($api->Event->Create(array('title'=>'Test','event_type_id' => 1,'is_public' => 1,'start_date' => 19430429))) {
  *     echo "created event id:". $api->id;
  *   } else {
  *     echo $api->errorMsg();
  *   }
- * @endcode
+ * ```
  *
  * To make it easier, the Actions can either take for input an
  * associative array $params, or simply an id. The following two lines
  * are equivalent.
  *
- * @code
+ * ```
  *   $api->Activity->Get (42);
  *   $api->Activity->Get (array('id'=>42));
- * @endcode
+ * ```
  *
  *
  * You can also get the result like civicrm_api does, but as an object
  * instead of an array (eg $entity->attribute instead of
  * $entity['attribute']).
  *
- * @code
+ * ```
  *   $result = $api->result;
  *   // is the json encoded result
  *   echo $api;
- * @endcode
+ * ```
  */
 class civicrm_api3 {
 
index 1e78d6796bbae0faf4e7d805d300ea2cc5d30454..8583f0b8c5acab4aa7679a2d886b12eabf743e8c 100644 (file)
@@ -15,7 +15,7 @@
  * file content.
  * For core fields use "entity_table", for custom fields use "field_name"
  *
- * @code
+ * ```
  * // Create an attachment for a core field
  * $result = civicrm_api3('Attachment', 'create', array(
  *   'entity_table' => 'civicrm_activity',
@@ -26,9 +26,9 @@
  * ));
  * $attachment = $result['values'][$result['id']];
  * echo sprintf("<a href='%s'>View %s</a>", $attachment['url'], $attachment['name']);
- * @endcode
+ * ```
  *
- * @code
+ * ```
  * // Create an attachment for a custom file field
  * $result = civicrm_api3('Attachment', 'create', array(
  *   'field_name' => 'custom_6',
@@ -39,9 +39,9 @@
  * ));
  * $attachment = $result['values'][$result['id']];
  * echo sprintf("<a href='%s'>View %s</a>", $attachment['url'], $attachment['name']);
- * @endcode
+ * ```
  *
- * @code
+ * ```
  * // Move an existing file and save as an attachment
  * $result = civicrm_api3('Attachment', 'create', array(
  *   'entity_table' => 'civicrm_activity',
@@ -54,7 +54,7 @@
  * ));
  * $attachment = $result['values'][$result['id']];
  * echo sprintf("<a href='%s'>View %s</a>", $attachment['url'], $attachment['name']);
- * @endcode
+ * ```
  *
  * Notes:
  *  - File content is not returned by default. One must specify 'return => content'.
index 1f5b9b6ae46a9bf2cfe290692499795b25332b6c..5249010c45b372c98e6b31117e6af0cadf9cff09 100644 (file)
@@ -21,7 +21,7 @@
  *
  * @param array $params
  *
- * @code
+ * ```
  * // REQUIRED for create:
  * 'case_type_id' => int OR
  * 'case_type' => str (provide one or the other)
@@ -38,7 +38,7 @@
  * 'start_date' => str datestamp // defaults to: date('YmdHis')
  * 'duration' => int // in minutes
  * 'details' => str // html format
- * @endcode
+ * ```
  *
  * @throws API_Exception
  * @return array
@@ -556,13 +556,13 @@ function civicrm_api3_case_update($params) {
  *
  * @param array $params
  *
- * @code
+ * ```
  *   //REQUIRED:
  *   'id' => int
  *
  *   //OPTIONAL
  *   'move_to_trash' => bool (defaults to false)
- * @endcode
+ * ```
  *
  * @throws API_Exception
  * @return mixed
index 0711224f0021e52fa9fa0d979a068caf86a01472..34f96a9f2daa350e2c3115255a74d9a0b3ccaf27 100644 (file)
@@ -22,7 +22,7 @@
  *   Expected keys are in format custom_fieldID:recordID or custom_groupName:fieldName:recordID.
  *
  * @example:
- * @code
+ * ```
  *   // entity ID. You do not need to specify entity type, we figure it out based on the fields you're using
  *   'entity_id' => 123,
  *   // (omitting :id) inserts or updates a field in a single-valued group
@@ -39,7 +39,7 @@
  *   'custom_some_group:my_field' => 'myinfo',
  *   // updates record ID 8 in my_other_field in multi-valued some_big_group
  *   'custom_some_big_group:my_other_field:8' => 'myinfo',
- * @endcode
+ * ```
  *
  * @throws Exception
  * @return array
index 250438378f774e038337164192bc3a8e3c9cc9ae..ef1121ccb9f96766a9d49bd2e13c00a62767e079 100644 (file)
@@ -72,7 +72,7 @@ function _civicrm_api3_group_contact_create_spec(&$params) {
  *
  * This api has a legacy/nonstandard signature.
  * On success, the return array will be structured as follows:
- * @code
+ * ```
  * array(
  *   "is_error" => 0,
  *   "version"  => 3,
@@ -83,16 +83,16 @@ function _civicrm_api3_group_contact_create_spec(&$params) {
  *     "total_count" => integer
  *   )
  * )
- * @endcode
+ * ```
  *
  * On failure, the return array will be structured as follows:
- * @code
+ * ```
  * array(
  *   'is_error' => 1,
  *   'error_message' = string,
  *   'error_data' = mixed or undefined
  * )
- * @endcode
+ * ```
  *
  * @param array $params
  *   Input parameters:
index 185b3cd374ad063926d3d56efa66f56d2e1ad448..011a19f149ef88d2570850dbc7f3166d4ec59ce7 100644 (file)
@@ -134,8 +134,8 @@ function _civicrm_api3_mailing_create_spec(&$params) {
 
   $params['forward_replies']['api.default'] = FALSE;
   $params['auto_responder']['api.default'] = FALSE;
-  $params['open_tracking']['api.default'] = TRUE;
-  $params['url_tracking']['api.default'] = TRUE;
+  $params['open_tracking']['api.default'] = Civi::settings()->get('open_tracking_default');
+  $params['url_tracking']['api.default'] = Civi::settings()->get('url_tracking_default');
 
   $params['header_id']['api.default'] = CRM_Mailing_PseudoConstant::defaultComponent('Header', '');
   $params['footer_id']['api.default'] = CRM_Mailing_PseudoConstant::defaultComponent('Footer', '');
index 3101e29480174cb7c6b0bc9222d6f418b61c0c63..40f73f2871683e8eddf4f252af1b8ae74a1663e8 100644 (file)
@@ -1237,7 +1237,7 @@ function formatCheckBoxField(&$checkboxFieldValue, $customFieldLabel, $entity) {
  * @return array
  */
 function _civicrm_api3_basic_get($bao_name, $params, $returnAsSuccess = TRUE, $entity = "", $sql = NULL, $uniqueFields = FALSE) {
-  $entity = $entity ?: CRM_Core_DAO_AllCoreTables::getBriefName(str_replace('_BAO_', '_DAO_', $bao_name));
+  $entity = $entity ?: CRM_Core_DAO_AllCoreTables::getBriefName($bao_name);
   $options = _civicrm_api3_get_options_from_params($params);
 
   $query = new \Civi\API\Api3SelectQuery($entity, CRM_Utils_Array::value('check_permissions', $params, FALSE));
@@ -2367,7 +2367,7 @@ function _civicrm_api3_api_resolve_alias($entity, $fieldName, $action = 'create'
   if (strpos($fieldName, 'custom_') === 0 && is_numeric($fieldName[7])) {
     return $fieldName;
   }
-  if ($fieldName == _civicrm_api_get_entity_name_from_camel($entity) . '_id') {
+  if ($fieldName === (CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($entity) . '_id')) {
     return 'id';
   }
   $result = civicrm_api($entity, 'getfields', [
diff --git a/i/TreeMinus.gif b/i/TreeMinus.gif
deleted file mode 100644 (file)
index 964739a..0000000
Binary files a/i/TreeMinus.gif and /dev/null differ
diff --git a/i/TreeMinusWhite.gif b/i/TreeMinusWhite.gif
deleted file mode 100644 (file)
index f5ae57e..0000000
Binary files a/i/TreeMinusWhite.gif and /dev/null differ
diff --git a/i/TreePlus.gif b/i/TreePlus.gif
deleted file mode 100644 (file)
index a07d4c5..0000000
Binary files a/i/TreePlus.gif and /dev/null differ
diff --git a/i/TreePlusWhite.gif b/i/TreePlusWhite.gif
deleted file mode 100644 (file)
index 69c21a3..0000000
Binary files a/i/TreePlusWhite.gif and /dev/null differ
index a2a5bc06c8e2e4328c0f13fd2d75afed16e172a5..2e2640956a7fc7bb5fc93b1ccb939007c0640a68 100644 (file)
@@ -341,4 +341,32 @@ return [
     'description' => ts('Allow sending email from the logged in contact\'s email address.'),
     'help_text' => 'CiviCRM allows you to send email from the domain from email addresses and the logged in contact id addresses by default. Disable this if you only want to allow the domain from addresses to be used.',
   ],
+  'url_tracking_default' => [
+    'group_name' => 'Mailing Preferences',
+    'group' => 'mailing',
+    'name' => 'url_tracking_default',
+    'type' => 'Boolean',
+    'html_type' => 'checkbox',
+    'quick_form_type' => 'CheckBox',
+    'default' => '1',
+    'title' => ts('Enable click-through tracking by default'),
+    'is_domain' => 1,
+    'is_contact' => 0,
+    'description' => ts('If checked, mailings will have click-through tracking enabled by default.'),
+    'help_text' => NULL,
+  ],
+  'open_tracking_default' => [
+    'group_name' => 'Mailing Preferences',
+    'group' => 'mailing',
+    'name' => 'open_tracking_default',
+    'type' => 'Boolean',
+    'html_type' => 'checkbox',
+    'quick_form_type' => 'CheckBox',
+    'default' => '1',
+    'title' => ts('Enable open tracking by default'),
+    'is_domain' => 1,
+    'is_contact' => 0,
+    'description' => ts('If checked, mailings will have open tracking enabled by default.'),
+    'help_text' => NULL,
+  ],
 ];
index bde961cb5a93597df711a38cc0e3be48eb522cc4..789a85bd570be41cb68fc7d137be15f45de09090 100644 (file)
@@ -93,7 +93,7 @@
     CRM.$(function($) {
       var mailSetting = $("input[name='outBound_option']:checked").val( );
 
-      var archiveWarning = "{/literal}{ts escape='js'}WARNING: You are switching from a testing mode (Redirect to Database) to a live mode. Check Mailings > Archived Mailings, and delete any test mailings that are not in Completed status prior to running the mailing cron job for the first time. This will ensure that test mailings are not actually sent out.{/ts}{literal}"
+      var archiveWarning = "{/literal}{ts escape='js'}WARNING: You are switching from a testing mode (Redirect to Database) to a live mode. Check Mailings > Archived Mailings, and delete any test mailings that are not in Completed status prior to running the mailing cron job for the first time. This will ensure that test mailings are not actually sent out.{/ts}{literal}";
 
         showHideMailOptions( $("input[name='outBound_option']:checked").val( ) ) ;
 
index f723097862d8a4e628e293020716895a551d01ba..9c927d7b4cc295242060c07da56debdf731fc616 100644 (file)
@@ -51,7 +51,7 @@
           <tr id="optionField_{$index}" class="form-item {cycle values="odd-row,even-row"}">
             <td>
               {if $index GT 1}
-                <a onclick="hideRow({$index}); return false;" name="orderBy_{$index}" href="#" class="form-link"><img src="{$config->resourceBase}i/TreeMinus.gif" class="action-icon" alt="{ts}hide field or section{/ts}"/></a>
+                <a onclick="hideRow({$index}); return false;" name="orderBy_{$index}" href="#" class="form-link">{icon icon="fa-trash"}{ts}hide field or section{/ts}{/icon}</a>
               {/if}
             </td>
             <td> {$form.order_bys.$index.column.html}</td>
@@ -65,7 +65,7 @@
         {/section}
       </table>
       <div id="optionFieldLink" class="add-remove-link">
-        <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><img src="{$config->resourceBase}i/TreePlus.gif" class="action-icon" alt="{ts}show field or section{/ts}"/>{ts}another column{/ts}</a>
+        <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><i class="crm-i fa-plus action-icon" aria-hidden="true"></i> {ts}another column{/ts}</a>
       </div>
 
       <script type="text/javascript">
@@ -311,7 +311,7 @@ var surveyActivityIds = {/literal}{$surveyActivityIds}{literal};
         if (interview.errors[error]) errorList =  errorList + '<li>' + interview.errors[error] + '</li>';
       }
       if ( errorList ) {
-        var allErrors = '<i class="crm-i fa-exclamation-triangle crm-i-red"></i> {/literal}{ts}Please correct the following errors in the survey fields below:{/ts}{literal}<ul>' + errorList + '</ul>';
+        var allErrors = '<i class="crm-i fa-exclamation-triangle crm-i-red" aria-hidden="true"></i> {/literal}{ts}Please correct the following errors in the survey fields below:{/ts}{literal}<ul>' + errorList + '</ul>';
         CRM.$('#responseErrors').show( ).html(allErrors);
       }
     }
index 0bc1ad634b2079ef8b4a20aae2639796e27051fa..8d9b69525263d0d31157b7c6e3de8ba78470e1de 100644 (file)
@@ -73,7 +73,7 @@
             var caseUrl = destUrl + selectedCaseId + '&cid=' + contactId + context;
 
             var statusMsg = {/literal}'{ts escape='js' 1='%1'}Activity has been filed to %1 case.{/ts}'{literal};
-            CRM.alert(ts(statusMsg, {1: '<a href="' + caseUrl + '">' + caseTitle + '</a>'}), '{/literal}{ts escape="js"}Saved{/ts}{literal}', 'success');
+            CRM.alert(ts(statusMsg, {1: '<a href="' + caseUrl + '">' + CRM._.escape(caseTitle) + '</a>'}), '{/literal}{ts escape="js"}Saved{/ts}{literal}', 'success');
             CRM.refreshParent(a);
           }
         }
index c9d46d9b467b276cdf356cb067d3e71500cb9b15..30392c8d53c46ecf68f002d655a3ba9f18d33cdb 100644 (file)
@@ -7,7 +7,6 @@
  | and copyright information, see https://civicrm.org/licensing       |
  +--------------------------------------------------------------------+
 *}
-{capture assign=expandIconURL}<img src="{$config->resourceBase}i/TreePlus.gif" alt="{ts}open section{/ts}"/>{/capture}
 {strip}
 <table class="case-selector-{$list} crm-ajax-table" data-page-length='10'>
 <thead>
index 2e93012d0192d505a6eb86d87022a71062258c5d..e397febb8cc1fdd9c6637c0af207eee75225ccff 100644 (file)
   {section name=rowLoop start=1 loop=6}
      {assign var=index value=$smarty.section.rowLoop.index}
      <tr id="discount_{$index}" class=" crm-event-manage-fee-form-block-discount_{$index} {if $index GT 1 AND empty( $form.discount_name[$index].value) } hiddenElement {/if} form-item {cycle values="odd-row,even-row"}">
-           <td>{if $index GT 1} <a onclick="showHideDiscountRow('discount_{$index}', false, {$index}); return false;" name="discount_{$index}" href="#" class="form-link"><img src="{$config->resourceBase}i/TreeMinus.gif" class="action-icon" alt="{ts}hide field or section{/ts}"/></a>{/if}
+           <td>{if $index GT 1} <a onclick="showHideDiscountRow('discount_{$index}', false, {$index}); return false;" name="discount_{$index}" href="#" class="form-link">{icon icon="fa-trash"}{ts}remove discount set{/ts}{/icon}</span></a>{/if}
            </td>
            <td class="crm-event-manage-fee-form-block-discount_name"> {$form.discount_name.$index.html}</td>
            <td class="crm-event-manage-fee-form-block-discount_start_date"> {$form.discount_start_date.$index.html} </td>
     {/section}
     </table>
         <div id="discountLink" class="add-remove-link">
-           <a onclick="showHideDiscountRow( 'discount', true);return false;" id="discountLink" href="#" class="form-link"><img src="{$config->resourceBase}i/TreePlus.gif" class="action-icon" alt="{ts}show field or section{/ts}"/>{ts}another discount set{/ts}</a>
+           <a onclick="showHideDiscountRow( 'discount', true);return false;" id="discountLink" href="#" class="form-link"><i class="crm-i fa-plus action-icon" aria-hidden="true"></i> {ts}another discount set{/ts}</a>
         </div>
         {$form._qf_Fee_submit.html}
 
index 72c3fa34083fe99f1a285f46ea75817c22c7d9af..449d7a33d5b80a81e5210f44756a6cfd49c5a710 100644 (file)
@@ -25,7 +25,7 @@
 </div><div class="spacer"></div>
 <div id="comment" style="display:none">
             <table class="form-layout">
-            <tr class="crm-mailing-forward-form-block-forward_comment"><td><a href="#" onclick="cj('#comment').hide(); cj('#comment_show').show(); return false;"><img src="{$config->resourceBase}i/TreeMinus.gif" class="action-icon" alt="{ts}close section{/ts}"/></a>
+            <tr class="crm-mailing-forward-form-block-forward_comment"><td><a href="#" onclick="cj('#comment').hide(); cj('#comment_show').show(); return false;">{icon icon="fa-caret-down action-icon"}{ts}close section{/ts}{/icon}</a>
                 <label>{$form.forward_comment.label}</label></td>
                 <td>{$form.forward_comment.html}<br /><br />
               &nbsp;{$form.html_comment.html}<br /></td>
index 3e44fef13e1ac132ae3a1cdeb8c1dd25bd1340e6..7b00728147f94fb5c137b760080d7c203753e976 100644 (file)
@@ -42,7 +42,7 @@
                       (function($) {
                         $('#groups').val('').change(function() {
                           CRM.confirm({
-                            message: ts({/literal}'{ts escape='js' 1='<em>%1</em>'}Add all contacts to %1 group?{/ts}'{literal}, {1: $('option:selected', '#groups').text()})
+                            message: ts({/literal}'{ts escape='js' 1='<em>%1</em>'}Add all contacts to %1 group?{/ts}'{literal}, {1: CRM._.escape($('option:selected', '#groups').text())})
                           })
                             .on({
                               'crmConfirm:yes': function() {
index 413d66e14e446c6934035d0af99246e8fc265315..f881c58d2ae3d4f993fcea4042acc9bad02f5c31 100644 (file)
@@ -24,7 +24,7 @@
         <tr id="optionField_{$index}" class="form-item {cycle values="odd-row,even-row"}">
           <td>
             {if $index GT 1}
-              <a onclick="hideRow({$index}); return false;" name="orderBy_{$index}" href="#" class="form-link"><img src="{$config->resourceBase}i/TreeMinus.gif" class="action-icon" alt="{ts}hide field or section{/ts}"/></a>
+              <a onclick="hideRow({$index}); return false;" name="orderBy_{$index}" href="#" class="form-link">{icon icon="fa-trash"}{ts}remove sort by column{/ts}{/icon}</a>
             {/if}
           </td>
           <td> {$form.order_bys.$index.column.html}</td>
@@ -35,7 +35,7 @@
       {/section}
     </table>
     <div id="optionFieldLink" class="add-remove-link">
-      <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><img src="{$config->resourceBase}i/TreePlus.gif" class="action-icon" alt="{ts}show field or section{/ts}"/>{ts}another column{/ts}</a>
+      <a onclick="showHideRow(); return false;" name="optionFieldLink" href="#" class="form-link"><i class="crm-i fa-plus action-icon" aria-hidden="true"></i> {ts}another column{/ts}</a>
     </div>
     <script type="text/javascript">
       var showRows   = new Array({$showBlocks});
diff --git a/tests/phpunit/CRM/Activity/Page/AJAXTest.php b/tests/phpunit/CRM/Activity/Page/AJAXTest.php
new file mode 100644 (file)
index 0000000..f9e9f75
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @group headless
+ */
+class CRM_Activity_Page_AJAXTest extends CiviUnitTestCase {
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->loadAllFixtures();
+
+    CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
+
+    $this->loggedInUser = $this->createLoggedInUser();
+    $this->target = $this->individualCreate();
+  }
+
+  /**
+   * Test the underlying function that implements file-on-case.
+   *
+   * The UI is a quickform but it's only realized as a popup ajax form that
+   * doesn't have its own postProcess. Instead the values are ultimately
+   * passed to the function this test is testing. So there's no form or ajax
+   * being tested here, just the final act of filing the activity.
+   */
+  public function testConvertToCaseActivity() {
+    $activity = $this->callAPISuccess('Activity', 'create', [
+      'source_contact_id' => $this->loggedInUser,
+      'activity_type_id' => 'Meeting',
+      'subject' => 'test file on case',
+      'status_id' => 'Completed',
+      'target_id' => $this->target,
+    ]);
+
+    $case = $this->callAPISuccess('Case', 'create', [
+      'contact_id' => $this->target,
+      'case_type_id' => 'housing_support',
+      'subject' => 'Needs housing',
+    ]);
+
+    $params = [
+      'activityID' => $activity['id'],
+      'caseID' => $case['id'],
+      'mode' => 'file',
+      'targetContactIds' => $this->target,
+    ];
+    $result = CRM_Activity_Page_AJAX::_convertToCaseActivity($params);
+
+    $this->assertEmpty($result['error_msg']);
+    $newActivityId = $result['newId'];
+
+    $caseActivities = $this->callAPISuccess('Activity', 'get', [
+      'case_id' => $case['id'],
+    ])['values'];
+    $this->assertEquals('test file on case', $caseActivities[$newActivityId]['subject']);
+    // This should be a different physical activity, not the same db record as the original.
+    $this->assertNotEquals($activity['id'], $caseActivities[$newActivityId]['id']);
+  }
+
+}
index e6998025be23f0d36f213d246e8aa029408c2c5f..1bf6542772ee61527e38c3432bb99e143244882e 100644 (file)
@@ -213,4 +213,9 @@ class CRM_Core_DAO_AllCoreTablesTest extends CiviUnitTestCase {
     $this->assertFalse(CRM_Core_DAO_AllCoreTables::isCoreTable('civicrm_invalid_table'), 'civicrm_invalid_table should NOT be a core table');
   }
 
+  public function testGetBriefName() {
+    $this->assertEquals('Contact', CRM_Core_DAO_AllCoreTables::getBriefName('CRM_Contact_BAO_Contact'));
+    $this->assertEquals('Contact', CRM_Core_DAO_AllCoreTables::getBriefName('CRM_Contact_DAO_Contact'));
+  }
+
 }
diff --git a/tests/phpunit/CRM/Core/PageTest.php b/tests/phpunit/CRM/Core/PageTest.php
new file mode 100644 (file)
index 0000000..1566334
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * Class CRM_Core_PageTest
+ * @group headless
+ */
+class CRM_Core_PageTest extends CiviUnitTestCase {
+
+  /**
+   * Test data for testMakeIcons
+   *
+   * @return array
+   */
+  public function iconTestData() {
+    // first item is icon, text, condition, and attribs, second is expected markup
+    return [
+      [
+        '<i aria-hidden="true" title="We have a winner" class="crm-i fa-trophy"></i><span class="sr-only">We have a winner</span>',
+        ['fa-trophy', 'We have a winner', TRUE, []],
+        '{icon icon="fa-trophy"}We have a winner{/icon}',
+      ],
+      [
+        '',
+        ['fa-trophy', 'We have a winner', 0, []],
+        '{icon icon="fa-trophy" condition=0}We have a winner{/icon}',
+      ],
+      [
+        '<i aria-hidden="true" title="Favorite" class="action-icon test-icon crm-i fa-heart"></i><span class="sr-only">Favorite</span>',
+        ['fa-heart', 'Favorite', TRUE, ['class' => 'action-icon test-icon']],
+        '{icon icon="fa-heart" class="action-icon test-icon"}Favorite{/icon}',
+      ],
+      [
+        '<i aria-hidden="true" title="I &quot;choo-choo&quot; choose you" class="crm-i fa-train"></i><span class="sr-only">I "choo-choo" choose you</span>',
+        ['fa-train', 'I "choo-choo" choose you', TRUE, []],
+        '{icon icon="fa-train"}I "choo-choo" choose you{/icon}',
+      ],
+      [
+        '<i aria-hidden="true" class="crm-i fa-trash"></i><span class="sr-only">Trash</span>',
+        ['fa-trash', 'Trash', TRUE, ['title' => '']],
+        '{icon icon="fa-trash" title=""}Trash{/icon}',
+      ],
+      [
+        '<i title="It\'s bedtime" class="crm-i fa-bed"></i><span class="sr-only">It\'s bedtime</span>',
+        ['fa-bed', "It's bedtime", TRUE, ['aria-hidden' => '']],
+        // Ye olde Smarty 2 doesn't support hyphenated function parameters
+      ],
+      [
+        '<i aria-hidden="true" class="crm-i fa-snowflake-o"></i>',
+        ['fa-snowflake-o', NULL, TRUE, []],
+        '{icon icon="fa-snowflake-o"}{/icon}',
+      ],
+    ];
+  }
+
+  /**
+   * Test that icons are formed properly
+   *
+   * @param string $expectedMarkup
+   * @param array $params
+   * @param string $smartyFunc
+   * @dataProvider iconTestData
+   */
+  public function testMakeIcons($expectedMarkup, $params, $smartyFunc = '') {
+    list($icon, $text, $condition, $attribs) = $params;
+    $this->assertEquals($expectedMarkup, CRM_Core_Page::crmIcon($icon, $text, $condition, $attribs));
+    if (!empty($smartyFunc)) {
+      $smarty = CRM_Core_Smarty::singleton();
+      $actual = $smarty->fetch('string:' . $smartyFunc);
+      $this->assertEquals($expectedMarkup, $actual, "Process input=[$smartyFunc]");
+    }
+  }
+
+}
index ecfafeb53c21604c8fbeafebc5c8cdfd794dee01..63269ab3185b6868b1e2dcb4b4b345abceca470b 100644 (file)
@@ -6,7 +6,10 @@
  */
 class CRM_Logging_SchemaTest extends CiviUnitTestCase {
 
+  protected $databaseVersion;
+
   public function setUp() {
+    $this->databaseVersion = CRM_Utils_SQL::getDatabaseVersion();
     parent::setUp();
   }
 
@@ -18,6 +21,7 @@ class CRM_Logging_SchemaTest extends CiviUnitTestCase {
   public function tearDown() {
     $schema = new CRM_Logging_Schema();
     $schema->disableLogging();
+    $this->databaseVersion = NULL;
     parent::tearDown();
     $this->quickCleanup(['civicrm_contact'], TRUE);
     $schema->dropAllLogTables();
@@ -235,13 +239,17 @@ class CRM_Logging_SchemaTest extends CiviUnitTestCase {
     $this->assertEquals('int', $ci['test_id']['DATA_TYPE']);
     $this->assertEquals('NO', $ci['test_id']['IS_NULLABLE']);
     $this->assertEquals('auto_increment', $ci['test_id']['EXTRA']);
-    $this->assertEquals('10', $ci['test_id']['LENGTH']);
+    if (!$this->isMySQL8()) {
+      $this->assertEquals('10', $ci['test_id']['LENGTH']);
+    }
 
     $this->assertEquals('varchar', $ci['test_varchar']['DATA_TYPE']);
     $this->assertEquals('42', $ci['test_varchar']['LENGTH']);
 
     $this->assertEquals('int', $ci['test_integer']['DATA_TYPE']);
-    $this->assertEquals('8', $ci['test_integer']['LENGTH']);
+    if (!$this->isMySQL8()) {
+      $this->assertEquals('8', $ci['test_integer']['LENGTH']);
+    }
     $this->assertEquals('YES', $ci['test_integer']['IS_NULLABLE']);
 
     $this->assertEquals('decimal', $ci['test_decimal']['DATA_TYPE']);
@@ -282,7 +290,9 @@ class CRM_Logging_SchemaTest extends CiviUnitTestCase {
     $schema->fixSchemaDifferences();
     $ci = \Civi::$statics['CRM_Logging_Schema']['columnSpecs'];
     // length should increase
-    $this->assertEquals(6, $ci['log_civicrm_test_length_change']['test_integer']['LENGTH']);
+    if (!$this->isMySQL8()) {
+      $this->assertEquals(6, $ci['log_civicrm_test_length_change']['test_integer']['LENGTH']);
+    }
     $this->assertEquals('22,2', $ci['log_civicrm_test_length_change']['test_decimal']['LENGTH']);
     CRM_Core_DAO::executeQuery(
       "ALTER TABLE civicrm_test_length_change
@@ -292,7 +302,9 @@ class CRM_Logging_SchemaTest extends CiviUnitTestCase {
     $schema->fixSchemaDifferences();
     $ci = \Civi::$statics['CRM_Logging_Schema']['columnSpecs'];
     // length should not decrease
-    $this->assertEquals(6, $ci['log_civicrm_test_length_change']['test_integer']['LENGTH']);
+    if (!$this->isMySQL8()) {
+      $this->assertEquals(6, $ci['log_civicrm_test_length_change']['test_integer']['LENGTH']);
+    }
     $this->assertEquals('22,2', $ci['log_civicrm_test_length_change']['test_decimal']['LENGTH']);
   }
 
@@ -312,4 +324,13 @@ class CRM_Logging_SchemaTest extends CiviUnitTestCase {
     $this->assertEquals("'A','B','C','D'", $ci['civicrm_test_enum_change']['test_enum']['ENUM_VALUES']);
   }
 
+  /**
+   * Determine if we are running on MySQL 8 version 8.0.19 or later.
+   *
+   * @return bool
+   */
+  protected function isMySQL8() {
+    return (bool) (version_compare($this->databaseVersion, '8.0.19', '>=') && stripos($this->databaseVersion, 'mariadb') === FALSE);
+  }
+
 }
index 60b3108e02a162b0c758ca30db51febe83905b72..0127615f6ebb216b135cb4aefbe450e9d81c446f 100644 (file)
@@ -36,42 +36,51 @@ class CRM_Report_FormTest extends CiviUnitTestCase {
     return TRUE;
   }
 
-  public function fromToData() {
+  /**
+   * Used by testGetFromTo
+   */
+  private function fromToData() {
     $cases = [];
     // Absolute dates
-    $cases[] = ['20170901000000', '20170913235959', 0, '09/01/2017', '09/13/2017'];
+    $cases['absolute'] = [
+      'expectedFrom' => '20170901000000',
+      'expectedTo' => '20170913235959',
+      'relative' => 0,
+      'from' => '09/01/2017',
+      'to' => '09/13/2017',
+    ];
     // "Today" relative date filter
     $date = new DateTime();
-    $expectedFrom = $date->format('Ymd') . '000000';
-    $expectedTo = $date->format('Ymd') . '235959';
-    $cases[] = [$expectedFrom, $expectedTo, 'this.day', '', ''];
+    $cases['today'] = [
+      'expectedFrom' => $date->format('Ymd') . '000000',
+      'expectedTo' => $date->format('Ymd') . '235959',
+      'relative' => 'this.day',
+      'from' => '',
+      'to' => '',
+    ];
     // "yesterday" relative date filter
     $date = new DateTime();
     $date->sub(new DateInterval('P1D'));
-    $expectedFrom = $date->format('Ymd') . '000000';
-    $expectedTo = $date->format('Ymd') . '235959';
-    $cases[] = [$expectedFrom, $expectedTo, 'previous.day', '', ''];
+    $cases['yesterday'] = [
+      'expectedFrom' => $date->format('Ymd') . '000000',
+      'expectedTo' => $date->format('Ymd') . '235959',
+      'relative' => 'previous.day',
+      'from' => '',
+      'to' => '',
+    ];
     return $cases;
   }
 
   /**
    * Test that getFromTo returns the correct dates.
-   *
-   * @dataProvider fromToData
-   *
-   * @param string $expectedFrom
-   * @param string $expectedTo
-   * @param string $relative
-   * @param string $from
-   * @param string $to
    */
-  public function testGetFromTo($expectedFrom, $expectedTo, $relative, $from, $to) {
-    $obj = new CRM_Report_Form();
-    if (date('Hi') === '0000') {
-      $this->markTestIncomplete('The date might have changed since the dataprovider was called. Skip to avoid flakiness');
+  public function testGetFromTo() {
+    $cases = $this->fromToData();
+    foreach ($cases as $caseDescription => $case) {
+      $obj = new CRM_Report_Form();
+      list($calculatedFrom, $calculatedTo) = $obj->getFromTo($case['relative'], $case['from'], $case['to']);
+      $this->assertEquals([$case['expectedFrom'], $case['expectedTo']], [$calculatedFrom, $calculatedTo], "fail on data set '{$caseDescription}'. Local php time is " . date('Y-m-d H:i:s') . ' and mysql time is ' . CRM_Core_DAO::singleValueQuery('SELECT NOW()'));
     }
-    list($calculatedFrom, $calculatedTo) = $obj->getFromTo($relative, $from, $to);
-    $this->assertEquals([$expectedFrom, $expectedTo], [$calculatedFrom, $calculatedTo], "fail on data set [ $relative , $from , $to ]. Local php time is " . date('Y-m-d H:i:s') . ' and mysql time is ' . CRM_Core_DAO::singleValueQuery('SELECT NOW()'));
   }
 
 }
index ba2403541868ecdf6f5397afce7d21fd8664a4fe..630e897edf22363279e5f92b8932dfeadc4757cb 100644 (file)
@@ -36,39 +36,52 @@ class CRM_Utils_DateTest extends CiviUnitTestCase {
     return TRUE;
   }
 
-  public function fromToData() {
+  /**
+   * Used by testGetFromTo
+   */
+  private function fromToData() {
     $cases = [];
     // Absolute dates
-    $cases[] = ['20170901000000', '20170913235959', 0, '09/01/2017', '09/13/2017'];
+    $cases['absolute'] = [
+      'expectedFrom' => '20170901000000',
+      'expectedTo' => '20170913235959',
+      'relative' => 0,
+      'from' => '09/01/2017',
+      'to' => '09/13/2017',
+    ];
     // "Today" relative date filter
     $date = new DateTime();
-    $expectedFrom = $date->format('Ymd') . '000000';
-    $expectedTo = $date->format('Ymd') . '235959';
-    $cases[] = [$expectedFrom, $expectedTo, 'this.day', '', ''];
+    $cases['today'] = [
+      'expectedFrom' => $date->format('Ymd') . '000000',
+      'expectedTo' => $date->format('Ymd') . '235959',
+      'relative' => 'this.day',
+      'from' => '',
+      'to' => '',
+    ];
     // "yesterday" relative date filter
     $date = new DateTime();
     $date->sub(new DateInterval('P1D'));
-    $expectedFrom = $date->format('Ymd') . '000000';
-    $expectedTo = $date->format('Ymd') . '235959';
-    $cases[] = [$expectedFrom, $expectedTo, 'previous.day', '', ''];
+    $cases['yesterday'] = [
+      'expectedFrom' => $date->format('Ymd') . '000000',
+      'expectedTo' => $date->format('Ymd') . '235959',
+      'relative' => 'previous.day',
+      'from' => '',
+      'to' => '',
+    ];
     return $cases;
   }
 
   /**
    * Test that getFromTo returns the correct dates.
-   *
-   * @dataProvider fromToData
-   * @param $expectedFrom
-   * @param $expectedTo
-   * @param $relative
-   * @param $from
-   * @param $to
    */
-  public function testGetFromTo($expectedFrom, $expectedTo, $relative, $from, $to) {
-    $obj = new CRM_Utils_Date();
-    list($calculatedFrom, $calculatedTo) = $obj->getFromTo($relative, $from, $to);
-    $this->assertEquals($expectedFrom, $calculatedFrom);
-    $this->assertEquals($expectedTo, $calculatedTo);
+  public function testGetFromTo() {
+    $cases = $this->fromToData();
+    foreach ($cases as $caseDescription => $case) {
+      $obj = new CRM_Utils_Date();
+      list($calculatedFrom, $calculatedTo) = $obj->getFromTo($case['relative'], $case['from'], $case['to']);
+      $this->assertEquals($case['expectedFrom'], $calculatedFrom, "Expected From failed for case $caseDescription");
+      $this->assertEquals($case['expectedTo'], $calculatedTo, "Expected To failed for case $caseDescription");
+    }
   }
 
   /**
index af0f28dd4af099da91d801975ec85013c664b76e..83fff3e4185cae226db4a55ae446b90a74260078 100644 (file)
@@ -495,14 +495,14 @@ class CiviUnitTestCase extends PHPUnit\Framework\TestCase {
    * Create a batch of external API calls which can
    * be executed concurrently.
    *
-   * @code
+   * ```
    * $calls = $this->createExternalAPI()
    *    ->addCall('Contact', 'get', ...)
    *    ->addCall('Contact', 'get', ...)
    *    ...
    *    ->run()
    *    ->getResults();
-   * @endcode
+   * ```
    *
    * @return \Civi\API\ExternalBatch
    * @throws PHPUnit_Framework_SkippedTestError