Merge pull request #13182 from civicrm/5.8
authorEileen McNaughton <eileen@mcnaughty.com>
Fri, 30 Nov 2018 04:48:42 +0000 (17:48 +1300)
committerGitHub <noreply@github.com>
Fri, 30 Nov 2018 04:48:42 +0000 (17:48 +1300)
5.8

109 files changed:
CRM/Activity/BAO/Activity.php
CRM/Activity/Form/Activity.php
CRM/Admin/Form/MessageTemplates.php
CRM/Admin/Form/Setting/Miscellaneous.php
CRM/Batch/BAO/Batch.php
CRM/Case/DAO/Case.php
CRM/Case/Form/Activity.php
CRM/Contact/BAO/Query.php
CRM/Contact/Form/Contact.php
CRM/Contact/Form/Inline/Website.php
CRM/Contact/Form/Search/Criteria.php
CRM/Contact/Import/Parser/Contact.php
CRM/Contact/Page/View/UserDashBoard.php
CRM/Contact/Selector.php
CRM/Contribute/BAO/Contribution.php
CRM/Contribute/BAO/Query.php
CRM/Contribute/DAO/Contribution.php
CRM/Contribute/Form/AbstractEditPayment.php
CRM/Contribute/Form/ContributionBase.php
CRM/Contribute/Form/Search.php
CRM/Core/BAO/CustomField.php
CRM/Core/BAO/CustomGroup.php
CRM/Core/BAO/Mapping.php
CRM/Core/BAO/PrevNextCache.php
CRM/Core/CodeGen/Specification.php
CRM/Core/DAO.php
CRM/Core/DAO/EntityFile.php
CRM/Core/DAO/UFGroup.php
CRM/Core/I18n.php
CRM/Core/JobManager.php
CRM/Core/Payment/ProcessorForm.php
CRM/Core/PrevNextCache/Interface.php
CRM/Core/PrevNextCache/Redis.php [new file with mode: 0644]
CRM/Core/PrevNextCache/Sql.php
CRM/Core/Session.php
CRM/Event/BAO/Event.php
CRM/Event/Form/ManageEvent/EventInfo.php
CRM/Event/Page/ManageEvent.php
CRM/Event/Page/Tab.php
CRM/Export/BAO/Export.php
CRM/Export/BAO/ExportProcessor.php
CRM/Export/Form/Select.php
CRM/Member/Page/DashBoard.php
CRM/Price/BAO/PriceSet.php
CRM/Report/Form.php
CRM/Report/Form/Contribute/Bookkeeping.php
CRM/Report/Form/Contribute/Lybunt.php
CRM/UF/Form/Group.php
CRM/Upgrade/Incremental/php/FiveNine.php [new file with mode: 0644]
CRM/Upgrade/Incremental/sql/5.7.0.mysql.tpl [new file with mode: 0644]
CRM/Upgrade/Incremental/sql/5.9.0.mysql.tpl [new file with mode: 0644]
CRM/Upgrade/Incremental/sql/5.9.alpha1.mysql.tpl [new file with mode: 0644]
CRM/Utils/API/AbstractFieldCoder.php
CRM/Utils/Check/Component/Env.php
CRM/Utils/Date.php
CRM/Utils/SQL/TempTable.php
Civi/Core/Container.php
ang/crmUi.ang.php
ang/crmUi.js
api/v3/Activity.php
api/v3/Contribution.php
api/v3/Profile.php
settings/Mailing.setting.php
settings/Search.setting.php
sql/civicrm_generated.mysql
templates/CRM/Admin/Form/MessageTemplates.tpl
templates/CRM/Contact/Form/Contact.tpl
templates/CRM/Contact/Form/Search/Criteria/Fields/preferred_communication_method.tpl
templates/CRM/Contact/Page/View/CustomDataFieldView.tpl
templates/CRM/Contribute/Form/Contribution/Confirm.tpl
templates/CRM/Contribute/Form/Contribution/Main.tpl
templates/CRM/Contribute/Form/Contribution/OnBehalfOf.tpl
templates/CRM/Contribute/Form/Contribution/ThankYou.tpl
templates/CRM/Contribute/Form/ContributionView.tpl
templates/CRM/Contribute/Form/Search/Common.tpl
templates/CRM/Core/Form/Field.tpl
templates/CRM/Core/I18n/Dialog.tpl
templates/CRM/Event/Form/ManageEvent/EventInfo.hlp
templates/CRM/Event/Form/ManageEvent/Registration.hlp
templates/CRM/Event/Form/Registration/ThankYou.tpl
templates/CRM/Event/Page/UserDashboard.tpl
templates/CRM/Member/Page/DashBoard.tpl
templates/CRM/UF/Form/Group.tpl
tests/phpunit/CRM/Batch/BAO/BatchTest.php
tests/phpunit/CRM/Contact/BAO/QueryTest.php
tests/phpunit/CRM/Contact/Form/Search/CriteriaTest.php [new file with mode: 0644]
tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php
tests/phpunit/CRM/Contact/Page/View/UserDashBoardTest.php
tests/phpunit/CRM/Contribute/Form/SearchTest.php
tests/phpunit/CRM/Core/BAO/CustomFieldTest.php
tests/phpunit/CRM/Core/I18n/LocaleTest.php [new file with mode: 0644]
tests/phpunit/CRM/Event/BAO/AdditionalPaymentTest.php
tests/phpunit/CRM/Export/BAO/ExportTest.php
tests/phpunit/CRM/Financial/Page/AjaxTest.php
tests/phpunit/CRM/Member/Form/MembershipTest.php
tests/phpunit/CRM/Utils/DateTest.php
tests/phpunit/CRM/Utils/TypeTest.php
tests/phpunit/CRMTraits/Page/PageTestTrait.php
tests/phpunit/CiviTest/CiviUnitTestCase.php
tests/phpunit/E2E/Core/PrevNextTest.php
tests/phpunit/api/v3/ContributionPageTest.php
tests/phpunit/api/v3/ContributionTest.php
tests/phpunit/api/v3/OrderTest.php
tests/phpunit/api/v3/ProfileTest.php
xml/schema/Case/Case.xml
xml/schema/Contribute/Contribution.xml
xml/schema/Core/EntityFile.xml
xml/schema/Core/UFGroup.xml
xml/version.xml

index af76376b7d6ab910f67f679d38e6357f413da341..055db5144bb90ca4867aae99b5b770d06d31a326 100644 (file)
@@ -671,59 +671,22 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
    *    - caseId      int            case ID
    *    - context     string         page on which selector is build
    *    - activity_type_id int|string the activitiy types we want to restrict by
-   * @param bool $getCount
-   *    Get count of the activities
    *
-   * @return array|int
+   * @return array
    *   Relevant data object values of open activities
    * @throws \CiviCRM_API3_Exception
    */
-  public static function getActivities($params, $getCount = FALSE) {
+  public static function getActivities($params) {
     $activities = array();
 
     // Activity.Get API params
-    $activityParams = array(
-      'is_deleted' => 0,
-      'is_current_revision' => 1,
-      'is_test' => 0,
-      'contact_id' => CRM_Utils_Array::value('contact_id', $params),
-      'return' => array(
-        'activity_date_time',
-        'source_record_id',
-        'source_contact_id',
-        'source_contact_name',
-        'assignee_contact_id',
-        'target_contact_id',
-        'target_contact_name',
-        'assignee_contact_name',
-        'status_id',
-        'subject',
-        'activity_type_id',
-        'activity_type',
-        'case_id',
-        'campaign_id',
-      ),
-      'check_permissions' => 1,
-      'options' => array(
-        'offset' => CRM_Utils_Array::value('offset', $params, 0),
-      ),
-    );
-
-    if (!empty($params['activity_status_id'])) {
-      $activityParams['activity_status_id'] = array('IN' => explode(',', $params['activity_status_id']));
-    }
-
-    $activityParams['activity_type_id'] = self::filterActivityTypes($params);
+    $activityParams = self::getActivityParamsForDashboardFunctions($params);
 
     if (!empty($params['rowCount']) &&
       $params['rowCount'] > 0
     ) {
       $activityParams['options']['limit'] = $params['rowCount'];
     }
-    // set limit = 0 if we need to fetch the activity count
-    elseif ($getCount) {
-      $activityParams['options']['limit'] = 0;
-    }
 
     if (!empty($params['sort'])) {
       if (is_a($params['sort'], 'CRM_Utils_Sort')) {
@@ -736,13 +699,29 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
 
     $activityParams['options']['sort'] = empty($order) ? "activity_date_time DESC" : str_replace('activity_type ', 'activity_type_id.label ', $order);
 
-    //TODO :
-    // 1. we should use Activity.Getcount for fetching count only, but  in order to check that
-    //    current logged in user has permission to view Case activities we are performing filtering out those activities from list (see below).
-    //    This logic need to be incorporated in Activity.get definition
+    $activityParams['return'] = [
+      'activity_date_time',
+      'source_record_id',
+      'source_contact_id',
+      'source_contact_name',
+      'assignee_contact_id',
+      'target_contact_id',
+      'target_contact_name',
+      'assignee_contact_name',
+      'status_id',
+      'subject',
+      'activity_type_id',
+      'activity_type',
+      'case_id',
+      'campaign_id',
+    ];
+    foreach (['case_id' => 'CiviCase', 'campaign_id' => 'CiviCampaign'] as $attr => $component) {
+      if (in_array($component, self::activityComponents())) {
+        $activityParams['return'][] = $attr;
+      }
+    }
     $result = civicrm_api3('Activity', 'Get', $activityParams);
 
-    $enabledComponents = self::activityComponents();
     $bulkActivityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Bulk Email');
     $allCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE);
 
@@ -768,19 +747,11 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
     );
 
     foreach ($result['values'] as $id => $activity) {
-      // skip case activities if CiviCase is not enabled OR those actvities which are
-      if (!empty($activity['case_id']) && !in_array('CiviCase', $enabledComponents)) {
-        continue;
-      }
 
       $activities[$id] = array();
 
-      // if count is needed, no need to populate the array list with attributes
-      if ($getCount) {
-        continue;
-      }
-
       $isBulkActivity = (!$bulkActivityTypeID || ($bulkActivityTypeID === $activity['activity_type_id']));
+
       foreach ($mappingParams as $apiKey => $expectedName) {
         if (in_array($apiKey, array('assignee_contact_name', 'target_contact_name'))) {
           $activities[$id][$expectedName] = CRM_Utils_Array::value($apiKey, $activity, array());
@@ -826,7 +797,7 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
       $activities[$id]['is_recurring_activity'] = CRM_Core_BAO_RecurringEntity::getParentFor($id, 'civicrm_activity');
     }
 
-    return $getCount ? count($activities) : $activities;
+    return $activities;
   }
 
   /**
@@ -1198,7 +1169,8 @@ ORDER BY    fixed_sort_order
    *   count of activities
    */
   public static function getActivitiesCount($input) {
-    return self::getActivities($input, TRUE);
+    $activityParams = self::getActivityParamsForDashboardFunctions($input);
+    return civicrm_api3('Activity', 'getcount', $activityParams);
   }
 
   /**
@@ -2858,6 +2830,40 @@ INNER JOIN  civicrm_option_group grp ON ( grp.id = val.option_group_id AND grp.n
     return FALSE;
   }
 
+  /**
+   * @param $params
+   * @return array
+   */
+  protected static function getActivityParamsForDashboardFunctions($params) {
+    $activityParams = [
+      'is_deleted' => 0,
+      'is_current_revision' => 1,
+      'is_test' => 0,
+      'contact_id' => CRM_Utils_Array::value('contact_id', $params),
+      'check_permissions' => 1,
+      'options' => [
+        'offset' => CRM_Utils_Array::value('offset', $params, 0),
+      ],
+    ];
+
+    if (!empty($params['activity_status_id'])) {
+      $activityParams['activity_status_id'] = ['IN' => explode(',', $params['activity_status_id'])];
+    }
+
+    $activityParams['activity_type_id'] = self::filterActivityTypes($params);
+    $enabledComponents = self::activityComponents();
+    // @todo - should we move this to activity get api.
+    foreach ([
+               'case_id' => 'CiviCase',
+               'campaign_id' => 'CiviCampaign'
+             ] as $attr => $component) {
+      if (!in_array($component, $enabledComponents)) {
+        $activityParams[$attr] = ['IS NULL' => 1];
+      }
+    }
+    return $activityParams;
+  }
+
   /**
    * Checks if user has permissions to edit inbound e-mails, either bsic info
    * or both basic information and content.
index 71d6ff01956b84be776aed84a30cae32536a688d..c56403f914a2c25780deede07aaa59f367817175 100644 (file)
@@ -523,7 +523,9 @@ class CRM_Activity_Form_Activity extends CRM_Contact_Form_Task {
 
     if ($this->_action & CRM_Core_Action::UPDATE) {
       // We filter out alternatives, in case this is a stored e-mail, before sending to front-end
-      $this->_values['details'] = CRM_Utils_String::stripAlternatives($this->_values['details']) ?: '';
+      if (isset($this->_values['details'])) {
+        $this->_values['details'] = CRM_Utils_String::stripAlternatives($this->_values['details']) ?: '';
+      }
 
       if ($this->_activityTypeName === 'Inbound Email' &&
         !CRM_Core_Permission::check('edit inbound email basic information and content')
index d316d559e26873e28c897d02afb3655863b759ca..f7c77587df2b79d2661cdcd081cde403bb6db553 100644 (file)
@@ -139,6 +139,7 @@ class CRM_Admin_Form_MessageTemplates extends CRM_Admin_Form {
     }
 
     if ($this->_action & CRM_Core_Action::DELETE) {
+      $this->assign('msg_title', $this->_values['msg_title']);
       return;
     }
 
index 31ef53a873c162057187735ac1141d951b8e8ca7..f741af3802cbd6ca2619da6365a0cc2d18abd792 100644 (file)
@@ -56,6 +56,7 @@ class CRM_Admin_Form_Setting_Miscellaneous extends CRM_Admin_Form_Setting {
     'dedupe_default_limit' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
     'remote_profile_submissions' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
     'allow_alert_autodismissal' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
+    'prevNextBackend' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME,
   );
 
   public $_uploadMaxSize;
@@ -77,6 +78,7 @@ class CRM_Admin_Form_Setting_Miscellaneous extends CRM_Admin_Form_Setting {
       'recentItemsMaxCount',
       'recentItemsProviders',
       'dedupe_default_limit',
+      'prevNextBackend',
     ));
   }
 
index 924bb55138eb8459c98cdf0a6087647b24b60618..ad2f096730644fb99a1d431b34f80bc3e797f36c 100644 (file)
@@ -745,19 +745,31 @@ LEFT JOIN civicrm_contribution_soft ON civicrm_contribution_soft.contribution_id
           $values['contribution_date_low'] = $date['from'];
           $values['contribution_date_high'] = $date['to'];
         }
-        $searchParams = CRM_Contact_BAO_Query::convertFormValues($values);
-        // @todo the use of defaultReturnProperties means the search will be inefficient
-        // as slow-unneeded properties are included.
-        $query = new CRM_Contact_BAO_Query($searchParams,
-          CRM_Contribute_BAO_Query::defaultReturnProperties(CRM_Contact_BAO_Query::MODE_CONTRIBUTE,
-            FALSE
-          ), NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTRIBUTE
-        );
-        if ($field == 'contribution_date_high' || $field == 'contribution_date_low') {
-          $query->dateQueryBuilder($params[$field], 'civicrm_contribution', 'contribution_date', 'receive_date', 'Contribution Date');
-        }
       }
     }
+
+    $searchParams = CRM_Contact_BAO_Query::convertFormValues(
+      $values,
+      0,
+      FALSE,
+      NULL,
+      [
+        'financial_type_id',
+        'contribution_soft_credit_type_id',
+        'contribution_status_id',
+        'contribution_page_id',
+        'financial_trxn_card_type_id',
+        'contribution_payment_instrument_id',
+      ]
+    );
+    // @todo the use of defaultReturnProperties means the search will be inefficient
+    // as slow-unneeded properties are included.
+    $query = new CRM_Contact_BAO_Query($searchParams,
+      CRM_Contribute_BAO_Query::defaultReturnProperties(CRM_Contact_BAO_Query::MODE_CONTRIBUTE,
+        FALSE
+      ), NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTRIBUTE
+    );
+
     if (!empty($query->_where[0])) {
       $where = implode(' AND ', $query->_where[0]) .
         " AND civicrm_entity_batch.batch_id IS NULL ";
index 8eb08778b488a43078c9ef36f1c51cd10991b8d6..af1fe06f270a8cfd7a04b122e02c4fc5151f42f5 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Case/Case.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:2a046fd795b19790f45c5d9dde06a538)
+ * (GenCodeChecksum:6b30b1ce81c7eb8088cac1a630c41c37)
  */
 
 /**
@@ -204,6 +204,7 @@ class CRM_Case_DAO_Case extends CRM_Core_DAO {
           'localizable' => 0,
           'html' => [
             'type' => 'Select Date',
+            'formatType' => 'activityDateTime',
           ],
         ],
         'case_end_date' => [
@@ -222,6 +223,7 @@ class CRM_Case_DAO_Case extends CRM_Core_DAO {
           'localizable' => 0,
           'html' => [
             'type' => 'Select Date',
+            'formatType' => 'activityDateTime',
           ],
         ],
         'details' => [
index 7fd6f54d7de6e32d2f0d21cc21ef1eeca86ba13a..629dab3f334c03894960e319f5c672ce3b227f52 100644 (file)
@@ -533,7 +533,7 @@ class CRM_Case_Form_Activity extends CRM_Activity_Form_Activity {
       }
       // copy files attached to old activity if any, to new one,
       // as long as users have not selected the 'delete attachment' option.
-      if (empty($newActParams['is_delete_attachment'])) {
+      if (empty($newActParams['is_delete_attachment']) && ($this->_activityId != $activity->id)) {
         CRM_Core_BAO_File::copyEntityFile('civicrm_activity', $this->_activityId,
           'civicrm_activity', $activity->id
         );
index 0b6fb080bb77192eee8cc9557e0104058dc24656..71497d2e24904e3674d6620e6a228ca91fbf2a48 100644 (file)
@@ -1635,8 +1635,9 @@ class CRM_Contact_BAO_Query {
         }
       }
       elseif ($id == 'email_on_hold') {
-        if ($formValues['email_on_hold']['on_hold']) {
-          $params[] = array('on_hold', '=', $formValues['email_on_hold']['on_hold'], 0, 0);
+        if ($onHoldValue = CRM_Utils_Array::value('email_on_hold', $formValues)) {
+          $onHoldValue = (array) $onHoldValue;
+          $params[] = array('on_hold', 'IN', $onHoldValue, 0, 0);
         }
       }
       elseif (substr($id, 0, 7) == 'custom_'
@@ -2640,176 +2641,167 @@ class CRM_Contact_BAO_Query {
         continue;
       }
 
-      $limitToPrimaryClause = $primaryLocation ? "AND {$name}.is_primary = 1" : '';
+      $from .= self::getEntitySpecificJoins($name, $mode, $side, $primaryLocation);
+    }
+    return $from;
+  }
 
-      switch ($name) {
-        case 'civicrm_address':
-          //CRM-14263 further handling of address joins further down...
-          $from .= " $side JOIN civicrm_address ON ( contact_a.id = civicrm_address.contact_id {$limitToPrimaryClause} )";
-          continue;
+  /**
+   * Get join statements for the from clause depending on entity type
+   *
+   * @param string $name
+   * @param int $mode
+   * @param string $side
+   * @param string $primaryLocation
+   * @return string
+   */
+  protected static function getEntitySpecificJoins($name, $mode, $side, $primaryLocation) {
+    $limitToPrimaryClause = $primaryLocation ? "AND {$name}.is_primary = 1" : '';
+    switch ($name) {
+      case 'civicrm_address':
+        //CRM-14263 further handling of address joins further down...
+        return " $side JOIN civicrm_address ON ( contact_a.id = civicrm_address.contact_id {$limitToPrimaryClause} )";
 
-        case 'civicrm_phone':
-          $from .= " $side JOIN civicrm_phone ON (contact_a.id = civicrm_phone.contact_id {$limitToPrimaryClause}) ";
-          continue;
+      case 'civicrm_phone':
+        return " $side JOIN civicrm_phone ON (contact_a.id = civicrm_phone.contact_id {$limitToPrimaryClause}) ";
 
-        case 'civicrm_email':
-          $from .= " $side JOIN civicrm_email ON (contact_a.id = civicrm_email.contact_id {$limitToPrimaryClause})";
-          continue;
+      case 'civicrm_email':
+        return " $side JOIN civicrm_email ON (contact_a.id = civicrm_email.contact_id {$limitToPrimaryClause})";
 
-        case 'civicrm_im':
-          $from .= " $side JOIN civicrm_im ON (contact_a.id = civicrm_im.contact_id {$limitToPrimaryClause}) ";
-          continue;
+      case 'civicrm_im':
+        return " $side JOIN civicrm_im ON (contact_a.id = civicrm_im.contact_id {$limitToPrimaryClause}) ";
 
-        case 'im_provider':
-          $from .= " $side JOIN civicrm_im ON (contact_a.id = civicrm_im.contact_id) ";
-          $from .= " $side JOIN civicrm_option_group option_group_imProvider ON option_group_imProvider.name = 'instant_messenger_service'";
-          $from .= " $side JOIN civicrm_option_value im_provider ON (civicrm_im.provider_id = im_provider.value AND option_group_imProvider.id = im_provider.option_group_id)";
-          continue;
+      case 'im_provider':
+        $from = " $side JOIN civicrm_im ON (contact_a.id = civicrm_im.contact_id) ";
+        $from .= " $side JOIN civicrm_option_group option_group_imProvider ON option_group_imProvider.name = 'instant_messenger_service'";
+        $from .= " $side JOIN civicrm_option_value im_provider ON (civicrm_im.provider_id = im_provider.value AND option_group_imProvider.id = im_provider.option_group_id)";
+        return $from;
 
-        case 'civicrm_openid':
-          $from .= " $side JOIN civicrm_openid ON ( civicrm_openid.contact_id = contact_a.id {$limitToPrimaryClause} )";
-          continue;
+      case 'civicrm_openid':
+        return " $side JOIN civicrm_openid ON ( civicrm_openid.contact_id = contact_a.id {$limitToPrimaryClause} )";
 
-        case 'civicrm_worldregion':
-          $from .= " $side JOIN civicrm_country ON civicrm_address.country_id = civicrm_country.id ";
-          $from .= " $side JOIN civicrm_worldregion ON civicrm_country.region_id = civicrm_worldregion.id ";
-          continue;
+      case 'civicrm_worldregion':
+        $from = " $side JOIN civicrm_country ON civicrm_address.country_id = civicrm_country.id ";
+        return "$from $side JOIN civicrm_worldregion ON civicrm_country.region_id = civicrm_worldregion.id ";
 
-        case 'civicrm_location_type':
-          $from .= " $side JOIN civicrm_location_type ON civicrm_address.location_type_id = civicrm_location_type.id ";
-          continue;
+      case 'civicrm_location_type':
+        return " $side JOIN civicrm_location_type ON civicrm_address.location_type_id = civicrm_location_type.id ";
 
-        case 'civicrm_group':
-          $from .= " $side JOIN civicrm_group ON civicrm_group.id = civicrm_group_contact.group_id ";
-          continue;
+      case 'civicrm_group':
+        return " $side JOIN civicrm_group ON civicrm_group.id = civicrm_group_contact.group_id ";
 
-        case 'civicrm_group_contact':
-          $from .= " $side JOIN civicrm_group_contact ON contact_a.id = civicrm_group_contact.contact_id ";
-          continue;
+      case 'civicrm_group_contact':
+        return " $side JOIN civicrm_group_contact ON contact_a.id = civicrm_group_contact.contact_id ";
 
-        case 'civicrm_group_contact_cache':
-          $from .= " $side JOIN civicrm_group_contact_cache ON contact_a.id = civicrm_group_contact_cache.contact_id ";
-          continue;
+      case 'civicrm_group_contact_cache':
+        return " $side JOIN civicrm_group_contact_cache ON contact_a.id = civicrm_group_contact_cache.contact_id ";
 
-        case 'civicrm_activity':
-        case 'civicrm_activity_tag':
-        case 'activity_type':
-        case 'activity_status':
-        case 'parent_id':
-        case 'civicrm_activity_contact':
-        case 'source_contact':
-        case 'activity_priority':
-          $from .= CRM_Activity_BAO_Query::from($name, $mode, $side);
-          continue;
-
-        case 'civicrm_entity_tag':
-          $from .= " $side JOIN civicrm_entity_tag ON ( civicrm_entity_tag.entity_table = 'civicrm_contact' AND
-                                                              civicrm_entity_tag.entity_id = contact_a.id ) ";
-          continue;
-
-        case 'civicrm_note':
-          $from .= " $side JOIN civicrm_note ON ( civicrm_note.entity_table = 'civicrm_contact' AND
-                                                        contact_a.id = civicrm_note.entity_id ) ";
-          continue;
-
-        case 'civicrm_subscription_history':
-          $from .= " $side JOIN civicrm_subscription_history
-                                   ON civicrm_group_contact.contact_id = civicrm_subscription_history.contact_id
-                                  AND civicrm_group_contact.group_id =  civicrm_subscription_history.group_id";
-          continue;
-
-        case 'civicrm_relationship':
-          if (self::$_relType == 'reciprocal') {
-            if (self::$_relationshipTempTable) {
-              // we have a temptable to join on
-              $tbl = self::$_relationshipTempTable;
-              $from .= " INNER JOIN {$tbl} civicrm_relationship ON civicrm_relationship.contact_id = contact_a.id";
-            }
-            else {
-              $from .= " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = contact_a.id OR civicrm_relationship.contact_id_a = contact_a.id)";
-              $from .= " $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_a = contact_b.id OR civicrm_relationship.contact_id_b = contact_b.id)";
-            }
-          }
-          elseif (self::$_relType == 'b') {
-            $from .= " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = contact_a.id )";
-            $from .= " $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_a = contact_b.id )";
+      case 'civicrm_activity':
+      case 'civicrm_activity_tag':
+      case 'activity_type':
+      case 'activity_status':
+      case 'parent_id':
+      case 'civicrm_activity_contact':
+      case 'source_contact':
+      case 'activity_priority':
+        return CRM_Activity_BAO_Query::from($name, $mode, $side);
+
+      case 'civicrm_entity_tag':
+        $from = " $side JOIN civicrm_entity_tag ON ( civicrm_entity_tag.entity_table = 'civicrm_contact'";
+        return "$from AND civicrm_entity_tag.entity_id = contact_a.id ) ";
+
+      case 'civicrm_note':
+        $from = " $side JOIN civicrm_note ON ( civicrm_note.entity_table = 'civicrm_contact'";
+        return "$from AND contact_a.id = civicrm_note.entity_id ) ";
+
+      case 'civicrm_subscription_history':
+        $from = " $side JOIN civicrm_subscription_history";
+        $from .= " ON civicrm_group_contact.contact_id = civicrm_subscription_history.contact_id";
+        return "$from AND civicrm_group_contact.group_id =  civicrm_subscription_history.group_id";
+
+      case 'civicrm_relationship':
+        if (self::$_relType == 'reciprocal') {
+          if (self::$_relationshipTempTable) {
+            // we have a temptable to join on
+            $tbl = self::$_relationshipTempTable;
+            return " INNER JOIN {$tbl} civicrm_relationship ON civicrm_relationship.contact_id = contact_a.id";
           }
           else {
-            $from .= " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_a = contact_a.id )";
-            $from .= " $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_b = contact_b.id )";
+            $from = " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = contact_a.id OR civicrm_relationship.contact_id_a = contact_a.id)";
+            $from .= " $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_a = contact_b.id OR civicrm_relationship.contact_id_b = contact_b.id)";
+            return $from;
           }
-          continue;
+        }
+        elseif (self::$_relType == 'b') {
+          $from = " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = contact_a.id )";
+          return "$from $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_a = contact_b.id )";
+        }
+        else {
+          $from = " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_a = contact_a.id )";
+          return "$from $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_b = contact_b.id )";
+        }
 
-        case 'civicrm_log':
-          $from .= " INNER JOIN civicrm_log ON (civicrm_log.entity_id = contact_a.id AND civicrm_log.entity_table = 'civicrm_contact')";
-          $from .= " INNER JOIN civicrm_contact contact_b_log ON (civicrm_log.modified_id = contact_b_log.id)";
-          continue;
+      case 'civicrm_log':
+        $from = " INNER JOIN civicrm_log ON (civicrm_log.entity_id = contact_a.id AND civicrm_log.entity_table = 'civicrm_contact')";
+        return "$from INNER JOIN civicrm_contact contact_b_log ON (civicrm_log.modified_id = contact_b_log.id)";
 
-        case 'civicrm_tag':
-          $from .= " $side  JOIN civicrm_tag ON civicrm_entity_tag.tag_id = civicrm_tag.id ";
-          continue;
+      case 'civicrm_tag':
+        return " $side  JOIN civicrm_tag ON civicrm_entity_tag.tag_id = civicrm_tag.id ";
 
-        case 'civicrm_grant':
-          $from .= CRM_Grant_BAO_Query::from($name, $mode, $side);
-          continue;
+      case 'civicrm_grant':
+        return CRM_Grant_BAO_Query::from($name, $mode, $side);
 
-        case 'civicrm_campaign':
-          //Move to default case if not in either mode.
-          if ($mode & CRM_Contact_BAO_Query::MODE_CONTRIBUTE) {
-            $from .= CRM_Contribute_BAO_Query::from($name, $mode, $side);
-            continue;
-          }
-          elseif ($mode & CRM_Contact_BAO_Query::MODE_MAILING) {
-            $from .= CRM_Mailing_BAO_Query::from($name, $mode, $side);
-            continue;
-          }
-          elseif ($mode & CRM_Contact_BAO_Query::MODE_CAMPAIGN) {
-            $from .= CRM_Campaign_BAO_Query::from($name, $mode, $side);
-            continue;
-          }
+      case 'civicrm_website':
+        return " $side JOIN civicrm_website ON contact_a.id = civicrm_website.contact_id ";
 
-        case 'civicrm_website':
-          $from .= " $side JOIN civicrm_website ON contact_a.id = civicrm_website.contact_id ";
-          continue;
+      case 'civicrm_campaign':
+        //Move to default case if not in either mode.
+        if ($mode & CRM_Contact_BAO_Query::MODE_CONTRIBUTE) {
+          return CRM_Contribute_BAO_Query::from($name, $mode, $side);
+        }
+        elseif ($mode & CRM_Contact_BAO_Query::MODE_MAILING) {
+          return CRM_Mailing_BAO_Query::from($name, $mode, $side);
+        }
+        elseif ($mode & CRM_Contact_BAO_Query::MODE_CAMPAIGN) {
+          return CRM_Campaign_BAO_Query::from($name, $mode, $side);
+        }
 
-        default:
-          $locationTypeName = '';
-          if (strpos($name, '-address') != 0) {
-            $locationTypeName = 'address';
-          }
-          elseif (strpos($name, '-phone') != 0) {
-            $locationTypeName = 'phone';
-          }
-          elseif (strpos($name, '-email') != 0) {
-            $locationTypeName = 'email';
-          }
-          elseif (strpos($name, '-im') != 0) {
-            $locationTypeName = 'im';
-          }
-          elseif (strpos($name, '-openid') != 0) {
-            $locationTypeName = 'openid';
-          }
+      default:
+        $locationTypeName = '';
+        if (strpos($name, '-address') != 0) {
+          $locationTypeName = 'address';
+        }
+        elseif (strpos($name, '-phone') != 0) {
+          $locationTypeName = 'phone';
+        }
+        elseif (strpos($name, '-email') != 0) {
+          $locationTypeName = 'email';
+        }
+        elseif (strpos($name, '-im') != 0) {
+          $locationTypeName = 'im';
+        }
+        elseif (strpos($name, '-openid') != 0) {
+          $locationTypeName = 'openid';
+        }
 
-          if ($locationTypeName) {
-            //we have a join on an location table - possibly in conjunction with search builder - CRM-14263
-            $parts = explode('-', $name);
-            $locationTypes = CRM_Core_DAO_Address::buildOptions('location_type_id', 'validate');
-            foreach ($locationTypes as $locationTypeID => $locationType) {
-              if ($parts[0] == str_replace(' ', '_', $locationType)) {
-                $locationID = $locationTypeID;
-              }
+        if ($locationTypeName) {
+          //we have a join on an location table - possibly in conjunction with search builder - CRM-14263
+          $parts = explode('-', $name);
+          $locationTypes = CRM_Core_DAO_Address::buildOptions('location_type_id', 'validate');
+          foreach ($locationTypes as $locationTypeID => $locationType) {
+            if ($parts[0] == str_replace(' ', '_', $locationType)) {
+              $locationID = $locationTypeID;
             }
-            $from .= " $side JOIN civicrm_{$locationTypeName} `{$name}` ON ( contact_a.id = `{$name}`.contact_id ) and `{$name}`.location_type_id = $locationID ";
-          }
-          else {
-            $from .= CRM_Core_Component::from($name, $mode, $side);
           }
-          $from .= CRM_Contact_BAO_Query_Hook::singleton()->buildSearchfrom($name, $mode, $side);
+          $from = " $side JOIN civicrm_{$locationTypeName} `{$name}` ON ( contact_a.id = `{$name}`.contact_id ) and `{$name}`.location_type_id = $locationID ";
+        }
+        else {
+          $from = CRM_Core_Component::from($name, $mode, $side);
+        }
+        $from .= CRM_Contact_BAO_Query_Hook::singleton()->buildSearchfrom($name, $mode, $side);
 
-          continue;
-      }
+        return $from;
     }
-    return $from;
   }
 
   /**
@@ -4205,6 +4197,13 @@ civicrm_relationship.start_date > {$today}
     }
   }
 
+  /**
+   * Add relationship permission criteria to where clause.
+   *
+   * @param string $grouping
+   * @param array $where Array to add "where" criteria to, in case you are generating a temp table.
+   *   Not the main query.
+   */
   public function addRelationshipPermissionClauses($grouping, &$where) {
     $relPermission = $this->getWhereValues('relation_permission', $grouping);
     if ($relPermission) {
@@ -4215,14 +4214,9 @@ civicrm_relationship.start_date > {$today}
       $where[$grouping][] = "(civicrm_relationship.is_permission_a_b IN (" . implode(",", $relPermission[2]) . "))";
 
       $allRelationshipPermissions = CRM_Contact_BAO_Relationship::buildOptions('is_permission_a_b');
-      $relQill = '';
-      foreach ($relPermission[2] as $rel) {
-        if (!empty($relQill)) {
-          $relQill .= ' OR ';
-        }
-        $relQill .= ts($allRelationshipPermissions[$rel]);
-      }
-      $this->_qill[$grouping][] = ts('Permissioned Relationships') . ' - ' . $relQill;
+
+      $relPermNames = array_intersect_key($allRelationshipPermissions, array_flip($relPermission[2]));
+      $this->_qill[$grouping][] = ts('Permissioned Relationships') . ' - ' . implode(' OR ', $relPermNames);
     }
   }
 
@@ -4955,29 +4949,47 @@ civicrm_relationship.start_date > {$today}
   }
 
   /**
-   * Fetch a list of contacts from the prev/next cache for displaying a search results page
+   * Fetch a list of contacts for displaying a search results page
    *
-   * @param string $cacheKey
-   * @param int $offset
-   * @param int $rowCount
+   * @param array $cids
+   *   List of contact IDs
    * @param bool $includeContactIds
    * @return CRM_Core_DAO
    */
-  public function getCachedContacts($cacheKey, $offset, $rowCount, $includeContactIds) {
+  public function getCachedContacts($cids, $includeContactIds) {
+    CRM_Utils_Type::validateAll($cids, 'Positive');
     $this->_includeContactIds = $includeContactIds;
     $onlyDeleted = in_array(array('deleted_contacts', '=', '1', '0', '0'), $this->_params);
     list($select, $from, $where) = $this->query(FALSE, FALSE, FALSE, $onlyDeleted);
-    $from = " FROM civicrm_prevnext_cache pnc INNER JOIN civicrm_contact contact_a ON contact_a.id = pnc.entity_id1 AND pnc.cacheKey = '$cacheKey' " . substr($from, 31);
-    $order = " ORDER BY pnc.id";
-    $groupByCol = array('contact_a.id', 'pnc.id');
-    $select = self::appendAnyValueToSelect($this->_select, $groupByCol, 'GROUP_CONCAT');
-    $groupBy = " GROUP BY " . implode(', ', $groupByCol);
-    $limit = " LIMIT $offset, $rowCount";
+    $select .= sprintf(", (%s) AS _wgt", $this->createSqlCase('contact_a.id', $cids));
+    $where .= sprintf(' AND contact_a.id IN (%s)', implode(',', $cids));
+    $order = 'ORDER BY _wgt';
+    $groupBy = '';
+    $limit = '';
     $query = "$select $from $where $groupBy $order $limit";
 
     return CRM_Core_DAO::executeQuery($query);
   }
 
+  /**
+   * Construct a SQL CASE expression.
+   *
+   * @param string $idCol
+   *   The name of a column with ID's (eg 'contact_a.id').
+   * @param array $cids
+   *   Array(int $weight => int $id).
+   * @return string
+   *   CASE WHEN id=123 THEN 1 WHEN id=456 THEN 2 END
+   */
+  private function createSqlCase($idCol, $cids) {
+    $buf = "CASE\n";
+    foreach ($cids as $weight => $cid) {
+      $buf .= " WHEN $idCol = $cid THEN $weight \n";
+    }
+    $buf .= "END\n";
+    return $buf;
+  }
+
   /**
    * Populate $this->_permissionWhereClause with permission related clause and update other
    * query related properties.
index 7314693bca2139479fc2ce11c6af0033cece2aa4..9d92c7dd880d64e40a6dadb1176a98884344fa36 100644 (file)
@@ -615,6 +615,7 @@ class CRM_Contact_Form_Contact extends CRM_Core_Form {
       $blocks['Address'] = $otherEditOptions['Address'];
     }
 
+    $website_types = array();
     $openIds = array();
     $primaryID = FALSE;
     foreach ($blocks as $name => $label) {
@@ -629,8 +630,17 @@ class CRM_Contact_Form_Contact extends CRM_Core_Form {
           }
 
           if ($dataExists) {
-            // skip remaining checks for website
             if ($name == 'website') {
+              if (!empty($blockValues['website_type_id'])) {
+                if (empty($website_types[$blockValues['website_type_id']])) {
+                  $website_types[$blockValues['website_type_id']] = $blockValues['website_type_id'];
+                }
+                else {
+                  $errors["{$name}[1][website_type_id]"] = ts('Contacts may only have one website of each type at most.');
+                }
+              }
+
+              // skip remaining checks for website
               continue;
             }
 
index c4664a665193ca8454e1ba6302a0e9db0b3a25cd..14f64d71587ae1b8e5d1a7694f7f30d1e0bd15d4 100644 (file)
@@ -87,6 +87,8 @@ class CRM_Contact_Form_Inline_Website extends CRM_Contact_Form_Inline {
       CRM_Contact_Form_Edit_Website::buildQuickForm($this, $blockId, TRUE);
     }
 
+    $this->addFormRule(array('CRM_Contact_Form_Inline_Website', 'formRule'), $this);
+
   }
 
   /**
@@ -128,4 +130,38 @@ class CRM_Contact_Form_Inline_Website extends CRM_Contact_Form_Inline {
     $this->response();
   }
 
+  /**
+   * Global validation rules for the form.
+   *
+   * @param array $fields
+   *   Posted values of the form.
+   * @param array $errors
+   *   List of errors to be posted back to the form.
+   * @param CRM_Contact_Form_Inline_Website $form
+   *
+   * @return array
+   */
+  public static function formRule($fields, $errors, $form) {
+    $hasData = $errors = array();
+    if (!empty($fields['website']) && is_array($fields['website'])) {
+      $types = array();
+      foreach ($fields['website'] as $instance => $blockValues) {
+        $dataExists = CRM_Contact_Form_Contact::blockDataExists($blockValues);
+
+        if ($dataExists) {
+          $hasData[] = $instance;
+          if (!empty($blockValues['website_type_id'])) {
+            if (empty($types[$blockValues['website_type_id']])) {
+              $types[$blockValues['website_type_id']] = $blockValues['website_type_id'];
+            }
+            else {
+              $errors["website[" . $instance . "][website_type_id]"] = ts('Contacts may only have one website of each type at most.');
+            }
+          }
+        }
+      }
+    }
+    return $errors;
+  }
+
 }
index 845ab7c9a54138c071a4e3882b73e4cbc65054ab..012500df3a472d52542cde342e97abc81cbf84a2 100644 (file)
@@ -231,9 +231,13 @@ class CRM_Contact_Form_Search_Criteria {
     $form->addRadio('privacy_toggle', ts('Privacy Options'), $options, array('allowClear' => FALSE));
 
     // preferred communication method
-
-    $onHold[] = $form->createElement('advcheckbox', 'on_hold', NULL, '');
-    $form->addGroup($onHold, 'email_on_hold', ts('Email On Hold'));
+    if (Civi::settings()->get('civimail_multiple_bulk_emails')) {
+      $form->addSelect('email_on_hold',
+        array('entity' => 'email', 'multiple' => 'multiple', 'label' => ts('Email On Hold'), 'options' => CRM_Core_PseudoConstant::emailOnHoldOptions()));
+    }
+    else {
+      $form->add('advcheckbox', 'email_on_hold', ts('Email On Hold'));
+    }
 
     $form->addSelect('preferred_communication_method',
       array('entity' => 'contact', 'multiple' => 'multiple', 'label' => ts('Preferred Communication Method'), 'option_url' => NULL, 'placeholder' => ts('- any -')));
index 420cb70de2397e9ae851a0e36b2e6f59b25baea7..6dcfa652530933f83019768edad1daf81507e1ea 100644 (file)
@@ -37,8 +37,8 @@ require_once 'api/v3/utils.php';
  * class to parse contact csv files
  */
 class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser {
-  protected $_mapperKeys;
-  protected $_mapperLocType;
+  protected $_mapperKeys = [];
+  protected $_mapperLocType = [];
   protected $_mapperPhoneType;
   protected $_mapperImProvider;
   protected $_mapperWebsiteType;
@@ -109,21 +109,21 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser {
    * Class constructor.
    *
    * @param array $mapperKeys
-   * @param int $mapperLocType
-   * @param int $mapperPhoneType
-   * @param int $mapperImProvider
-   * @param int $mapperRelated
-   * @param int $mapperRelatedContactType
+   * @param array $mapperLocType
+   * @param array $mapperPhoneType
+   * @param array $mapperImProvider
+   * @param array $mapperRelated
+   * @param array $mapperRelatedContactType
    * @param array $mapperRelatedContactDetails
-   * @param int $mapperRelatedContactLocType
-   * @param int $mapperRelatedContactPhoneType
-   * @param int $mapperRelatedContactImProvider
-   * @param int $mapperWebsiteType
-   * @param int $mapperRelatedContactWebsiteType
+   * @param array $mapperRelatedContactLocType
+   * @param array $mapperRelatedContactPhoneType
+   * @param array $mapperRelatedContactImProvider
+   * @param array $mapperWebsiteType
+   * @param array $mapperRelatedContactWebsiteType
    */
   public function __construct(
-    &$mapperKeys, $mapperLocType = NULL, $mapperPhoneType = NULL, $mapperImProvider = NULL, $mapperRelated = NULL, $mapperRelatedContactType = NULL, $mapperRelatedContactDetails = NULL, $mapperRelatedContactLocType = NULL, $mapperRelatedContactPhoneType = NULL, $mapperRelatedContactImProvider = NULL,
-    $mapperWebsiteType = NULL, $mapperRelatedContactWebsiteType = NULL
+    &$mapperKeys, $mapperLocType = [], $mapperPhoneType = [], $mapperImProvider = [], $mapperRelated = [], $mapperRelatedContactType = [], $mapperRelatedContactDetails = [], $mapperRelatedContactLocType = [], $mapperRelatedContactPhoneType = [], $mapperRelatedContactImProvider = [],
+    $mapperWebsiteType = [], $mapperRelatedContactWebsiteType = []
   ) {
     parent::__construct();
     $this->_mapperKeys = &$mapperKeys;
index 2cc99577e1cc6af3dc2254fc33b2997aa464e4c1..a3fb8807de4d8a3e2927354e6035e6510c193979 100644 (file)
@@ -69,9 +69,9 @@ class CRM_Contact_Page_View_UserDashBoard extends CRM_Core_Page {
     $session = CRM_Core_Session::singleton();
     $userID = $session->get('userID');
 
-    $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this);
+    $userChecksum = $this->getUserChecksum();
     $validUser = FALSE;
-    if (empty($userID) && $this->_contactId && $userChecksum) {
+    if ($userChecksum) {
       $this->assign('userChecksum', $userChecksum);
       $validUser = CRM_Contact_BAO_Contact_Utils::validChecksum($this->_contactId, $userChecksum);
       $this->_isChecksumUser = $validUser;
@@ -256,4 +256,17 @@ class CRM_Contact_Page_View_UserDashBoard extends CRM_Core_Page {
     return self::$_links;
   }
 
+  /**
+   * Get the user checksum from the url to use in links.
+   *
+   * @return string
+   */
+  protected function getUserChecksum() {
+    $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this);
+    if (empty($userID) && $this->_contactId) {
+      return $userChecksum;
+    }
+    return FALSE;
+  }
+
 }
index f048612ab7116eb803f6c86ded746d321315ddd2..f60d434fbdf315790c8fa71dc10610422411a08f 100644 (file)
@@ -578,8 +578,11 @@ class CRM_Contact_Selector extends CRM_Core_Selector_Base implements CRM_Core_Se
     // and contain the search criteria (parameters)
     // note that the default action is basic
     if ($rowCount) {
+      /** @var CRM_Core_PrevNextCache_Interface $prevNext */
+      $prevNext = Civi::service('prevnext');
       $cacheKey = $this->buildPrevNextCache($sort);
-      $resultSet = $this->_query->getCachedContacts($cacheKey, $offset, $rowCount, $includeContactIds)->fetchGenerator();
+      $cids = $prevNext->fetch($cacheKey, $offset, $rowCount);
+      $resultSet = empty($cids) ? [] : $this->_query->getCachedContacts($cids, $includeContactIds)->fetchGenerator();
     }
     else {
       $resultSet = $this->_query->searchQuery($offset, $rowCount, $sort, FALSE, $includeContactIds)->fetchGenerator();
index 2ce7c31a30009131ad42a7056783c84a501ee9db..1c6209e79fc1badbec9927aa57251524caa9c3ff 100644 (file)
@@ -3802,6 +3802,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
     $params = array_merge($defaults, $params);
     $params['skipLineItem'] = TRUE;
     $trxnsData['trxn_date'] = !empty($trxnsData['trxn_date']) ? $trxnsData['trxn_date'] : date('YmdHis');
+    $params['payment_instrument_id'] = CRM_Utils_Array::value('payment_instrument_id', $trxnsData, CRM_Utils_Array::value('payment_instrument_id', $params));
     $arAccountId = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($contributionDAO->financial_type_id, 'Accounts Receivable Account is');
 
     $completedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
@@ -3813,6 +3814,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
       $trxnsData['net_amount'] = !empty($trxnsData['net_amount']) ? $trxnsData['net_amount'] : $trxnsData['total_amount'];
       $params['pan_truncation'] = CRM_Utils_Array::value('pan_truncation', $trxnsData);
       $params['card_type_id'] = CRM_Utils_Array::value('card_type_id', $trxnsData);
+      $params['check_number'] = CRM_Utils_Array::value('check_number', $trxnsData);
 
       // record the entry
       $financialTrxn = CRM_Contribute_BAO_Contribution::recordFinancialAccounts($params, $trxnsData);
@@ -4939,7 +4941,8 @@ WHERE eft.financial_trxn_id IN ({$trxnId}, {$baseTrxnId['financialTrxnId']})
   }
 
   /**
-   * Function to check line items.
+   * Checks if line items total amounts
+   * match the contribution total amount.
    *
    * @param array $params
    *  array of order params.
@@ -4955,7 +4958,7 @@ WHERE eft.financial_trxn_id IN ({$trxnId}, {$baseTrxnId['financialTrxnId']})
         if (empty($item['financial_type_id'])) {
           $item['financial_type_id'] = $params['financial_type_id'];
         }
-        $lineItemAmount += $item['line_total'];
+        $lineItemAmount += $item['line_total'] + CRM_Utils_Array::value('tax_amount', $item, 0.00);
       }
     }
 
index abf05f7e7151b8f3e2a7018aab47d34487259fc6..7136fd5a99f9c706f6a7aecc08181a21974f1596 100644 (file)
@@ -506,7 +506,9 @@ class CRM_Contribute_BAO_Query extends CRM_Core_BAO_Query {
           return;
         }
         $whereTable = $fields[$fldName];
-        $value = trim($value);
+        if (!is_array($value)) {
+          $value = trim($value);
+        }
 
         $dataType = "String";
         if (!empty($whereTable['type'])) {
@@ -929,8 +931,7 @@ class CRM_Contribute_BAO_Query extends CRM_Core_BAO_Query {
     $form->add('text', 'contribution_amount_high', ts('To'), array('size' => 8, 'maxlength' => 8));
     $form->addRule('contribution_amount_high', ts('Please enter a valid money value (e.g. %1).', array(1 => CRM_Utils_Money::format('99.99', ' '))), 'money');
 
-    // Adding Cancelled Contribution fields -- CRM-21343
-    $form->add('text', 'contribution_cancel_reason', ts('Cancellation / Refund Reason'), array('size' => 40));
+    $form->addField('cancel_reason', array('entity' => 'Contribution'));
     CRM_Core_Form_Date::buildDateRange($form, 'contribution_cancel_date', 1, '_low', '_high', ts('From:'), FALSE);
     $form->addElement('hidden', 'contribution_cancel_date_range_error');
 
index faa6a8c9db4d1ba57b5bde4bde04d785474b24de..299ef839a49ae50d56b2c274c68af6a3ee227a52 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Contribute/Contribution.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:45a20d00d01766a61687cbac5cef1482)
+ * (GenCodeChecksum:eec525df6c3c6151861750217f3ebe5d)
  */
 
 /**
@@ -584,7 +584,7 @@ class CRM_Contribute_DAO_Contribution extends CRM_Core_DAO {
         'cancel_reason' => [
           'name' => 'cancel_reason',
           'type' => CRM_Utils_Type::T_TEXT,
-          'title' => ts('Cancel Reason'),
+          'title' => ts('Cancellation / Refund Reason'),
           'import' => TRUE,
           'where' => 'civicrm_contribution.cancel_reason',
           'headerPattern' => '/(cancel.?)?reason/i',
index 86c92605d669ac7f298149b46832a24fa206cb48..987479f2598251ed45e8b21003fabbef7b29a5bc 100644 (file)
@@ -753,6 +753,9 @@ WHERE  contribution_id = {$id}
   protected function assignContactEmailDetails() {
     if ($this->_contactID) {
       list($this->userDisplayName, $this->userEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactID);
+      if (empty($this->userDisplayName)) {
+        $this->userDisplayName = civicrm_api3('contact', 'getvalue', ['id' => $this->_contactID, 'return' => 'display_name']);
+      }
       $this->assign('displayName', $this->userDisplayName);
     }
   }
index 45a62b78bb9c024360efbdf737399405507b7a28..137c645f88a8894092dbc2868a0875b9709e52b5 100644 (file)
@@ -484,8 +484,13 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     $this->_defaults = array();
 
     $this->_amount = $this->get('amount');
+    // Assigning this to the template means it will be passed through to the payment form.
+    // This can, for example, by used by payment processors using client side encryption
+    $this->assign('currency', $this->getCurrency());
 
     //CRM-6907
+    // these lines exist to support a non-default currenty on the form but are probably
+    // obsolete & meddling wth the defaultCurrency is not the right approach....
     $config = CRM_Core_Config::singleton();
     $config->defaultCurrency = CRM_Utils_Array::value('currency',
       $this->_values,
index 035b9523e822dd4b7e568e8f37ab062a09eadefa..d8c2640b54101cf094355dbabd498c58f4e54913 100644 (file)
@@ -62,6 +62,14 @@ class CRM_Contribute_Form_Search extends CRM_Core_Form_Search {
    */
   protected $_prefix = "contribute_";
 
+
+  /**
+   * Explicitly declare the entity api name.
+   */
+  public function getDefaultEntity() {
+    return 'Contribution';
+  }
+
   /**
    * Processing needed for buildForm and later.
    */
@@ -165,10 +173,12 @@ class CRM_Contribute_Form_Search extends CRM_Core_Form_Search {
    * Build the form object.
    */
   public function buildQuickForm() {
-    parent::buildQuickForm();
-    $this->addContactSearchFields();
+    if ($this->isFormInViewOrEditMode()) {
+      parent::buildQuickForm();
+      $this->addContactSearchFields();
 
-    CRM_Contribute_BAO_Query::buildSearchForm($this);
+      CRM_Contribute_BAO_Query::buildSearchForm($this);
+    }
 
     $rows = $this->get('rows');
     if (is_array($rows)) {
@@ -377,9 +387,7 @@ class CRM_Contribute_Form_Search extends CRM_Core_Form_Search {
     if ($this->_context == 'user') {
       $query->setSkipPermission(TRUE);
     }
-    $summary = &$query->summaryContribution($this->_context);
-    $this->set('summary', $summary);
-    $this->assign('contributionSummary', $summary);
+
     $controller->run();
   }
 
index 2e7e2311422192a31b49a1f88cfc30ddb3ab4035..bcfd206cf16ce65db8d00461ccc66e9697a9dc9f 100644 (file)
@@ -1217,7 +1217,12 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
       case 'Multi-Select State/Province':
       case 'Multi-Select Country':
         if ($field['data_type'] == 'ContactReference' && $value) {
-          $display = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'display_name');
+          if (is_numeric($value)) {
+            $display = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'display_name');
+          }
+          else {
+            $display = $value;
+          }
         }
         elseif (is_array($value)) {
           $v = array();
index cffdac62c0f416f52e9dbb2758adc3c7f051e4e5..085194fdf5042cdd71fd391c906a57d8d8c2c6c9 100644 (file)
@@ -1564,7 +1564,7 @@ ORDER BY civicrm_custom_group.weight,
 
           case 'File':
             if ($skipFile) {
-              continue;
+              break;
             }
 
             //store the file in d/b
index 3e8b324a360b1e12130cd9b8684af34159874402..29ea4dcf5f70cc4e0a83e94399896ea8eb53566c 100644 (file)
@@ -440,6 +440,7 @@ class CRM_Core_BAO_Mapping extends CRM_Core_DAO_Mapping {
       if (CRM_Core_Permission::access('CiviEvent')) {
         $fields['Participant'] = CRM_Event_BAO_Participant::exportableFields();
         //get the component payment fields
+        // @todo - review this - inconsistent with other entities & hacky.
         if ($exportMode == CRM_Export_Form_Select::EVENT_EXPORT) {
           $componentPaymentFields = array();
           foreach (CRM_Export_BAO_Export::componentPaymentFields() as $payField => $payTitle) {
index c3078140ac421ed494687bb7103e888e8127d6f3..8a902d01c98ac9fdd7729dade3e0fa4506c317ce 100644 (file)
@@ -483,4 +483,18 @@ AND        c.created_date < date_sub( NOW( ), INTERVAL %2 day )
     }
   }
 
+  /**
+   * Get a list of available backend services.
+   *
+   * @return array
+   *   Array(string $id => string $label).
+   */
+  public static function getPrevNextBackends() {
+    return [
+      'default' => ts('Default (Auto-detect)'),
+      'sql' => ts('SQL'),
+      'redis' => ts('Redis'),
+    ];
+  }
+
 }
index 554f3fa27b1db475ccf704af3bd7201f9d36b153..6d15ae5e0ebf59d42166fc8c3c6c38a4dc9c1b6d 100644 (file)
@@ -336,12 +336,13 @@ class CRM_Core_CodeGen_Specification {
         break;
 
       default:
-        $field['sqlType'] = $field['phpType'] = $type;
+        $field['phpType'] = $this->value('phpType', $fieldXML, $type);
+        $field['sqlType'] = $type;
         if ($type == 'int unsigned') {
           $field['crmType'] = 'CRM_Utils_Type::T_INT';
         }
         else {
-          $field['crmType'] = 'CRM_Utils_Type::T_' . strtoupper($type);
+          $field['crmType'] = $this->value('crmType', $fieldXML, 'CRM_Utils_Type::T_' . strtoupper($type));
         }
         break;
     }
index 2892d3289175486a994388462fde2eb2e3f01635..bd850cf2ab9ef0ea4a0adf3217e585d5ae7bf4e9 100644 (file)
@@ -1678,7 +1678,9 @@ FROM   civicrm_domain
         }
       }
       $newObject->save();
+      CRM_Utils_Hook::post('create', CRM_Core_DAO_AllCoreTables::getBriefName($daoName), $newObject->id, $newObject);
     }
+
     return $newObject;
   }
 
index 3a8430b3fa0c2221bd52b3682afae6ae9b901ba5..080e30eaad44557b99596b2fb3ff0a8478023643 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Core/EntityFile.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:354c22131251fde259f5b796e102fccf)
+ * (GenCodeChecksum:d2d6205c8973c5ad7a989ecb674b9f94)
  */
 
 /**
@@ -213,15 +213,16 @@ class CRM_Core_DAO_EntityFile extends CRM_Core_DAO {
         'localizable' => FALSE,
         'sig' => 'civicrm_entity_file::0::entity_table::entity_id',
       ],
-      'index_entity_file_id' => [
-        'name' => 'index_entity_file_id',
+      'UI_entity_table_entity_id_file_id' => [
+        'name' => 'UI_entity_table_entity_id_file_id',
         'field' => [
           0 => 'entity_table',
           1 => 'entity_id',
           2 => 'file_id',
         ],
         'localizable' => FALSE,
-        'sig' => 'civicrm_entity_file::0::entity_table::entity_id::file_id',
+        'unique' => TRUE,
+        'sig' => 'civicrm_entity_file::1::entity_table::entity_id::file_id',
       ],
     ];
     return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
index 1480a27e7b2e7c0f2578b4c27c4a20bab79e3560..4667ad2c48c929b611eba39ee110cd67760326a2 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Core/UFGroup.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a48f9522d0bd2e1d485064ebfc66f9a2)
+ * (GenCodeChecksum:d0a806459507dc6b32ba955e4e899358)
  */
 
 /**
@@ -291,7 +291,7 @@ class CRM_Core_DAO_UFGroup extends CRM_Core_DAO {
         'title' => [
           'name' => 'title',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Title'),
+          'title' => ts('Profile Name'),
           'description' => ts('Form title.'),
           'required' => TRUE,
           'maxlength' => 64,
@@ -300,11 +300,14 @@ class CRM_Core_DAO_UFGroup extends CRM_Core_DAO {
           'entity' => 'UFGroup',
           'bao' => 'CRM_Core_BAO_UFGroup',
           'localizable' => 1,
+          'html' => [
+            'type' => 'Text',
+          ],
         ],
         'frontend_title' => [
           'name' => 'frontend_title',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Frontend Title'),
+          'title' => ts('Public Title'),
           'description' => ts('Profile Form Public title'),
           'maxlength' => 64,
           'size' => CRM_Utils_Type::BIG,
@@ -312,6 +315,9 @@ class CRM_Core_DAO_UFGroup extends CRM_Core_DAO {
           'entity' => 'UFGroup',
           'bao' => 'CRM_Core_BAO_UFGroup',
           'localizable' => 1,
+          'html' => [
+            'type' => 'Text',
+          ],
         ],
         'description' => [
           'name' => 'description',
index 40107e9824c082af67b837882c90f4a6dcd7f6fd..6dfbe23b566c30388fbd1ab89c46543b298c403c 100644 (file)
@@ -649,6 +649,9 @@ class CRM_Core_I18n {
     global $dbLocale;
     $dbLocale = "_{$locale}";
 
+    // For self::getLocale()
+    global $tsLocale;
+    $tsLocale = $locale;
   }
 
   /**
index fccfc18b8cf804567359ba4cf3eb57704a942124..0583f9c25525e5587f7a473b213da3237e8ddf32 100644 (file)
@@ -229,10 +229,21 @@ class CRM_Core_JobManager {
     $dao = new CRM_Core_DAO_JobLog();
 
     $dao->domain_id = $domainID;
-    $dao->description = substr($message, 0, 235);
-    if (strlen($message) > 235) {
-      $dao->description .= " (...)";
+
+    /*
+     * The description is a summary of the message.
+     * HTML tags are stripped from the message.
+     * The description is limited to 240 characters
+     * and has an ellipsis added if it is truncated.
+     */
+    $maxDescription = 240;
+    $ellipsis = " (...)";
+    $description = strip_tags($message);
+    if (strlen($description) > $maxDescription) {
+      $description = substr($description, 0, $maxDescription - strlen($ellipsis)) . $ellipsis;
     }
+    $dao->description = $description;
+
     if ($this->currentJob) {
       $dao->job_id = $this->currentJob->id;
       $dao->name = $this->currentJob->name;
index 4b777356ecc5fb42ab8485f398ddb9714e360cfe..4229a4a8e87fbc409280ff3eb83135b1280f9481 100644 (file)
@@ -72,8 +72,7 @@ class CRM_Core_Payment_ProcessorForm {
 
     $form->assign('suppressSubmitButton', $form->_paymentObject->isSuppressSubmitButtons());
 
-    $currency = $form->getCurrency();
-    $form->assign('currency', $currency);
+    $form->assign('currency', $form->getCurrency());
 
     // also set cancel subscription url
     if (!empty($form->_paymentProcessor['is_recur']) && !empty($form->_values['is_recur'])) {
index edaf0d6778ce9bae1658773d6d249c458f1b7a4c..5f3bfabd1e136982d99a012af3e3fc2c2eb9ecb7 100644 (file)
@@ -114,4 +114,15 @@ interface CRM_Core_PrevNextCache_Interface {
    */
   public function getCount($cacheKey);
 
+  /**
+   * Fetch a list of contacts from the prev/next cache for displaying a search results page
+   *
+   * @param string $cacheKey
+   * @param int $offset
+   * @param int $rowCount
+   * @return array
+   *   List of contact IDs (entity_id1).
+   */
+  public function fetch($cacheKey, $offset, $rowCount);
+
 }
diff --git a/CRM/Core/PrevNextCache/Redis.php b/CRM/Core/PrevNextCache/Redis.php
new file mode 100644 (file)
index 0000000..bc7d05b
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 5                                                  |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2018                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Class CRM_Core_PrevNextCache_Memory
+ *
+ * Store the previous/next cache in a Redis set.
+ *
+ * Each logical prev-next cache corresponds to three distinct items in Redis:
+ *   - "{prefix}/{qfKey}/list" - Sorted set of `entity_id`, with all entities
+ *   - "{prefix}/{qfkey}/sel" - Sorted set of `entity_id`, with only entities marked by user
+ *   - "{prefix}/{qfkey}/data" - Hash mapping from `entity_id` to `data`
+ *
+ * @link https://github.com/phpredis/phpredis
+ */
+class CRM_Core_PrevNextCache_Redis implements CRM_Core_PrevNextCache_Interface {
+
+  const TTL = 21600;
+
+  /**
+   * @var Redis
+   */
+  protected $redis;
+
+  /**
+   * @var string
+   */
+  protected $prefix;
+
+  /**
+   * CRM_Core_PrevNextCache_Redis constructor.
+   * @param array $settings
+   */
+  public function __construct($settings) {
+    $this->redis = CRM_Utils_Cache_Redis::connect($settings);
+    $this->prefix = isset($settings['prefix']) ? $settings['prefix'] : '';
+    $this->prefix .= \CRM_Utils_Cache::DELIMITER . 'prevnext' . \CRM_Utils_Cache::DELIMITER;
+  }
+
+  public function fillWithSql($cacheKey, $sql) {
+    $dao = CRM_Core_DAO::executeQuery($sql, [], FALSE, NULL, FALSE, TRUE, TRUE);
+    if (is_a($dao, 'DB_Error')) {
+      throw new CRM_Core_Exception($dao->message);
+    }
+
+    list($allKey, $dataKey, , $maxScore) = $this->initCacheKey($cacheKey);
+
+    while ($dao->fetch()) {
+      list (, $entity_id, $data) = array_values($dao->toArray());
+      $maxScore++;
+      $this->redis->zAdd($allKey, $maxScore, $entity_id);
+      $this->redis->hSet($dataKey, $entity_id, $data);
+    }
+
+    $dao->free();
+    return TRUE;
+  }
+
+  public function fillWithArray($cacheKey, $rows) {
+    list($allKey, $dataKey, , $maxScore) = $this->initCacheKey($cacheKey);
+
+    foreach ($rows as $row) {
+      $maxScore++;
+      $this->redis->zAdd($allKey, $maxScore, $row['entity_id1']);
+      $this->redis->hSet($dataKey, $row['entity_id1'], $row['data']);
+    }
+
+    return TRUE;
+  }
+
+  public function fetch($cacheKey, $offset, $rowCount) {
+    $allKey = $this->key($cacheKey, 'all');
+    return $this->redis->zRange($allKey, $offset, $offset + $rowCount - 1);
+  }
+
+  public function markSelection($cacheKey, $action, $ids = NULL) {
+    $allKey = $this->key($cacheKey, 'all');
+    $selKey = $this->key($cacheKey, 'sel');
+
+    if ($action === 'select') {
+      foreach ((array) $ids as $id) {
+        $score = $this->redis->zScore($allKey, $id);
+        $this->redis->zAdd($selKey, $score, $id);
+      }
+    }
+    elseif ($action === 'unselect' && $ids === NULL) {
+      $this->redis->delete($selKey);
+      $this->redis->setTimeout($selKey, self::TTL);
+    }
+    elseif ($action === 'unselect' && $ids !== NULL) {
+      foreach ((array) $ids as $id) {
+        $this->redis->zDelete($selKey, $id);
+      }
+    }
+  }
+
+  public function getSelection($cacheKey, $action = 'get') {
+    $allKey = $this->key($cacheKey, 'all');
+    $selKey = $this->key($cacheKey, 'sel');
+
+    if ($action === 'get') {
+      $result = [];
+      foreach ($this->redis->zRange($selKey, 0, -1) as $entity_id) {
+        $result[$entity_id] = 1;
+      }
+      return [$cacheKey => $result];
+    }
+    elseif ($action === 'getall') {
+      $result = [];
+      foreach ($this->redis->zRange($allKey, 0, -1) as $entity_id) {
+        $result[$entity_id] = 1;
+      }
+      return [$cacheKey => $result];
+    }
+    else {
+      throw new \CRM_Core_Exception("Unrecognized action: $action");
+    }
+  }
+
+  public function getPositions($cacheKey, $id1) {
+    $allKey = $this->key($cacheKey, 'all');
+    $dataKey = $this->key($cacheKey, 'data');
+
+    $rank = $this->redis->zRank($allKey, $id1);
+    if (!is_int($rank) || $rank < 0) {
+      return ['foundEntry' => 0];
+    }
+
+    $pos = ['foundEntry' => 1];
+
+    if ($rank > 0) {
+      $pos['prev'] = [];
+      foreach ($this->redis->zRange($allKey, $rank - 1, $rank - 1) as $value) {
+        $pos['prev']['id1'] = $value;
+      }
+      $pos['prev']['data'] = $this->redis->hGet($dataKey, $pos['prev']['id1']);
+    }
+
+    $count = $this->getCount($cacheKey);
+    if ($count > $rank + 1) {
+      $pos['next'] = [];
+      foreach ($this->redis->zRange($allKey, $rank + 1, $rank + 1) as $value) {
+        $pos['next']['id1'] = $value;
+      }
+      $pos['next']['data'] = $this->redis->hGet($dataKey, $pos['next']['id1']);
+    }
+
+    return $pos;
+  }
+
+  public function deleteItem($id = NULL, $cacheKey = NULL) {
+    if ($id === NULL && $cacheKey !== NULL) {
+      // Delete by cacheKey.
+      $allKey = $this->key($cacheKey, 'all');
+      $selKey = $this->key($cacheKey, 'sel');
+      $dataKey = $this->key($cacheKey, 'data');
+      $this->redis->delete($allKey, $selKey, $dataKey);
+    }
+    elseif ($id === NULL && $cacheKey === NULL) {
+      // Delete everything.
+      $keys = $this->redis->keys($this->prefix . '*');
+      $this->redis->del($keys);
+    }
+    elseif ($id !== NULL && $cacheKey !== NULL) {
+      // Delete a specific contact, within a specific cache.
+      $this->redis->zDelete($this->key($cacheKey, 'all'), $id);
+      $this->redis->zDelete($this->key($cacheKey, 'sel'), $id);
+      $this->redis->hDel($this->key($cacheKey, 'data'), $id);
+    }
+    elseif ($id !== NULL && $cacheKey === NULL) {
+      // Delete a specific contact, across all prevnext caches.
+      $allKeys = $this->redis->keys($this->key('*', 'all'));
+      foreach ($allKeys as $allKey) {
+        $parts = explode(\CRM_Utils_Cache::DELIMITER, $allKey);
+        array_pop($parts);
+        $tmpCacheKey = array_pop($parts);
+        $this->deleteItem($id, $tmpCacheKey);
+      }
+    }
+    else {
+      throw new CRM_Core_Exception("Not implemented: Redis::deleteItem");
+    }
+  }
+
+  public function getCount($cacheKey) {
+    $allKey = $this->key($cacheKey, 'all');
+    return $this->redis->zSize($allKey);
+  }
+
+  /**
+   * Construct the full path to a cache item.
+   *
+   * @param string $cacheKey
+   *   Identifier for this saved search.
+   *   Ex: 'abcd1234abcd1234'.
+   * @param string $item
+   *   Ex: 'list', 'rel', 'data'.
+   * @return string
+   *   Ex: 'dmaster/prevnext/abcd1234abcd1234/list'
+   */
+  private function key($cacheKey, $item) {
+    return $this->prefix . $cacheKey . \CRM_Utils_Cache::DELIMITER . $item;
+  }
+
+  /**
+   * Initialize any data-structures or timeouts for the cache-key.
+   *
+   * This is non-destructive -- if data already exists, it's preserved.
+   *
+   * @return array
+   *   0 => string $allItemsCacheKey,
+   *   1 => string $dataItemsCacheKey,
+   *   2 => string $selectedItemsCacheKey,
+   *   3 => int $maxExistingScore
+   */
+  private function initCacheKey($cacheKey) {
+    $allKey = $this->key($cacheKey, 'all');
+    $selKey = $this->key($cacheKey, 'sel');
+    $dataKey = $this->key($cacheKey, 'data');
+
+    $this->redis->setTimeout($allKey, self::TTL);
+    $this->redis->setTimeout($dataKey, self::TTL);
+    $this->redis->setTimeout($selKey, self::TTL);
+
+    $maxScore = 0;
+    foreach ($this->redis->zRange($allKey, -1, -1, TRUE) as $lastElem => $lastScore) {
+      $maxScore = $lastScore;
+    }
+    return array($allKey, $dataKey, $selKey, $maxScore);
+  }
+
+}
index 5a5897353332e0ee6b74fe2d7dcd08d152271beb..8771cb6c39c3ea5992e6b8611bed697fd7829282 100644 (file)
@@ -78,19 +78,19 @@ INSERT INTO civicrm_prevnext_cache (cacheKey, entity_id1, data)
    * @param string $cacheKey
    * @param string $action
    *   Ex: 'select', 'unselect'.
-   * @param array|int|NULL $cIds
+   * @param array|int|NULL $ids
    *   A list of contact IDs to (un)select.
    *   To unselect all contact IDs, use NULL.
    */
-  public function markSelection($cacheKey, $action, $cIds = NULL) {
+  public function markSelection($cacheKey, $action, $ids = NULL) {
     if (!$cacheKey) {
       return;
     }
     $params = array();
 
-    if ($cIds && $cacheKey && $action) {
-      if (is_array($cIds)) {
-        $cIdFilter = "(" . implode(',', $cIds) . ")";
+    if ($ids && $cacheKey && $action) {
+      if (is_array($ids)) {
+        $cIdFilter = "(" . implode(',', $ids) . ")";
         $whereClause = "
 WHERE cacheKey = %1
 AND (entity_id1 IN {$cIdFilter} OR entity_id2 IN {$cIdFilter})
@@ -101,7 +101,7 @@ AND (entity_id1 IN {$cIdFilter} OR entity_id2 IN {$cIdFilter})
 WHERE cacheKey = %1
 AND (entity_id1 = %2 OR entity_id2 = %2)
 ";
-        $params[2] = array("{$cIds}", 'Integer');
+        $params[2] = array("{$ids}", 'Integer');
       }
       if ($action == 'select') {
         $whereClause .= "AND is_selected = 0";
@@ -115,7 +115,7 @@ AND (entity_id1 = %2 OR entity_id2 = %2)
       }
       // default action is reseting
     }
-    elseif (!$cIds && $cacheKey && $action == 'unselect') {
+    elseif (!$ids && $cacheKey && $action == 'unselect') {
       $sql = "
 UPDATE civicrm_prevnext_cache
 SET    is_selected = 0
@@ -244,4 +244,27 @@ ORDER BY id
     return (int) CRM_Core_DAO::singleValueQuery($query, $params, TRUE, FALSE);
   }
 
+  /**
+   * Fetch a list of contacts from the prev/next cache for displaying a search results page
+   *
+   * @param string $cacheKey
+   * @param int $offset
+   * @param int $rowCount
+   * @return array
+   *   List of contact IDs.
+   */
+  public function fetch($cacheKey, $offset, $rowCount) {
+    $cids = array();
+    $dao = CRM_Utils_SQL_Select::from('civicrm_prevnext_cache pnc')
+      ->where('pnc.cacheKey = @cacheKey', ['cacheKey' => $cacheKey])
+      ->select('pnc.entity_id1 as cid')
+      ->orderBy('pnc.id')
+      ->limit($rowCount, $offset)
+      ->execute();
+    while ($dao->fetch()) {
+      $cids[] = $dao->cid;
+    }
+    return $cids;
+  }
+
 }
index 632a9e14ab9a632b767d571f5cdff72ff2167ef9..f618c8d3a1a0ded7e5a1b463dc1f6bacc2110faf 100644 (file)
@@ -114,9 +114,8 @@ class CRM_Core_Session {
         if ($isRead) {
           return;
         }
-        $config =& CRM_Core_Config::singleton();
         // FIXME: This belongs in CRM_Utils_System_*
-        if ($config->userSystem->is_drupal && function_exists('drupal_session_start')) {
+        if (CRM_Core_Config::singleton()->userSystem->is_drupal && function_exists('drupal_session_start')) {
           // https://issues.civicrm.org/jira/browse/CRM-14356
           if (!(isset($GLOBALS['lazy_session']) && $GLOBALS['lazy_session'] == TRUE)) {
             drupal_session_start();
index 6f68cc47aee34c842cc80b33793b36bb1acf078b..5c37bf6464c749c47b06c1aaaa14821fb222d62b 100644 (file)
@@ -920,13 +920,11 @@ WHERE civicrm_event.is_active = 1
    * @param int $id
    *   The event id to copy.
    *        boolean $afterCreate call to copy after the create function
-   * @param null $newEvent
-   * @param bool $afterCreate
    *
    * @return CRM_Event_DAO_Event
+   * @throws \CRM_Core_Exception
    */
-  public static function copy($id, $newEvent = NULL, $afterCreate = FALSE) {
-
+  public static function copy($id) {
     $eventValues = array();
 
     //get the require event values.
@@ -941,30 +939,19 @@ WHERE civicrm_event.is_active = 1
 
     CRM_Core_DAO::commonRetrieve('CRM_Event_DAO_Event', $eventParams, $eventValues, $returnProperties);
 
-    // since the location is sharable, lets use the same loc_block_id.
-    $locBlockId = CRM_Utils_Array::value('loc_block_id', $eventValues);
-
-    $fieldsFix = ($afterCreate) ? array() : array('prefix' => array('title' => ts('Copy of') . ' '));
+    $fieldsFix = array('prefix' => array('title' => ts('Copy of') . ' '));
     if (empty($eventValues['is_show_location'])) {
       $fieldsFix['prefix']['is_show_location'] = 0;
     }
 
-    if ($newEvent && is_a($newEvent, 'CRM_Event_DAO_Event')) {
-      $copyEvent = $newEvent;
-    }
-
-    if (!isset($copyEvent)) {
-      $copyEvent = &CRM_Core_DAO::copyGeneric('CRM_Event_DAO_Event',
-        array('id' => $id),
-        array(
-          'loc_block_id' =>
-          ($locBlockId) ? $locBlockId : NULL,
-        ),
-        $fieldsFix
-      );
-    }
+    $copyEvent = CRM_Core_DAO::copyGeneric('CRM_Event_DAO_Event',
+      array('id' => $id),
+      // since the location is sharable, lets use the same loc_block_id.
+      array('loc_block_id' => CRM_Utils_Array::value('loc_block_id', $eventValues)),
+      $fieldsFix
+    );
     CRM_Price_BAO_PriceSet::copyPriceSet('civicrm_event', $id, $copyEvent->id);
-    $copyUF = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFJoin',
+    CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFJoin',
       array(
         'entity_id' => $id,
         'entity_table' => 'civicrm_event',
@@ -972,7 +959,7 @@ WHERE civicrm_event.is_active = 1
       array('entity_id' => $copyEvent->id)
     );
 
-    $copyTellFriend = &CRM_Core_DAO::copyGeneric('CRM_Friend_DAO_Friend',
+    CRM_Core_DAO::copyGeneric('CRM_Friend_DAO_Friend',
       array(
         'entity_id' => $id,
         'entity_table' => 'civicrm_event',
@@ -980,7 +967,7 @@ WHERE civicrm_event.is_active = 1
       array('entity_id' => $copyEvent->id)
     );
 
-    $copyPCP = &CRM_Core_DAO::copyGeneric('CRM_PCP_DAO_PCPBlock',
+    CRM_Core_DAO::copyGeneric('CRM_PCP_DAO_PCPBlock',
       array(
         'entity_id' => $id,
         'entity_table' => 'civicrm_event',
@@ -995,22 +982,17 @@ WHERE civicrm_event.is_active = 1
     $copyMapping = CRM_Utils_Array::first(CRM_Core_BAO_ActionSchedule::getMappings(array(
       'id' => ($copyEvent->is_template == 1 ? CRM_Event_ActionMapping::EVENT_TPL_MAPPING_ID : CRM_Event_ActionMapping::EVENT_NAME_MAPPING_ID),
     )));
-    $copyReminder = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_ActionSchedule',
+    CRM_Core_DAO::copyGeneric('CRM_Core_DAO_ActionSchedule',
       array('entity_value' => $id, 'mapping_id' => $oldMapping->getId()),
       array('entity_value' => $copyEvent->id, 'mapping_id' => $copyMapping->getId())
     );
-
-    if (!$afterCreate) {
-      // CRM-19302
-      self::copyCustomFields($id, $copyEvent->id);
-    }
+    self::copyCustomFields($id, $copyEvent->id);
 
     $copyEvent->save();
 
     CRM_Utils_System::flushCache();
-    if (!$afterCreate) {
-      CRM_Utils_Hook::copy('Event', $copyEvent);
-    }
+    CRM_Utils_Hook::copy('Event', $copyEvent);
+
     return $copyEvent;
   }
 
index 4ca39edf26c88cbb2a47a67009a15c491d27f630..3ad75531fbe4c3f92021b4bfd75b403e8f5b7b17 100644 (file)
@@ -252,11 +252,12 @@ class CRM_Event_Form_ManageEvent_EventInfo extends CRM_Event_Form_ManageEvent {
       $params = array_merge(CRM_Event_BAO_Event::getTemplateDefaultValues($params['template_id']), $params);
     }
 
-    $event = CRM_Event_BAO_Event::create($params);
-
     // now that we have the event’s id, do some more template-based stuff
     if (!empty($params['template_id'])) {
-      CRM_Event_BAO_Event::copy($params['template_id'], $event, TRUE);
+      $event = CRM_Event_BAO_Event::copy($params['template_id']);
+    }
+    else {
+      $event = CRM_Event_BAO_Event::create($params);
     }
 
     $this->set('id', $event->id);
index 07561e88611097e06660be181e88f7f7b2388912..02ae36f51175db9f1552a14b550776438d794b90 100644 (file)
@@ -452,6 +452,7 @@ ORDER BY start_date desc
    * all the fields in the event wizard
    *
    * @return void
+   * @throws \CRM_Core_Exception
    */
   public function copy() {
     $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, TRUE, 0, 'GET');
index c0b1855dd598b62a8d3bebba8a14ae386e3ffd36..8b11dd5849a1a79878aa06e74a1b969b20fc92de 100644 (file)
@@ -118,6 +118,19 @@ class CRM_Event_Page_Tab extends CRM_Core_Page {
     return $controller->run();
   }
 
+  public function delete() {
+    $controller = new CRM_Core_Controller_Simple(
+      'CRM_Event_Form_Participant',
+      ts('Delete Participant'),
+      $this->_action
+    );
+
+    $controller->setEmbedded(TRUE);
+    $controller->set('id', $this->_id);
+    $controller->set('cid', $this->_contactId);
+    $controller->run();
+  }
+
   public function preProcess() {
     $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this);
     $this->_action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse');
@@ -167,13 +180,12 @@ class CRM_Event_Page_Tab extends CRM_Core_Page {
     if ($this->_action & CRM_Core_Action::VIEW) {
       $this->view();
     }
-    elseif ($this->_action & (CRM_Core_Action::UPDATE |
-        CRM_Core_Action::ADD |
-        CRM_Core_Action::DELETE
-      )
-    ) {
+    elseif ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD)) {
       $this->edit();
     }
+    elseif ($this->_action & (CRM_Core_Action::DELETE | CRM_Core_Action::DETACH)) {
+      $this->delete();
+    }
     else {
       $this->browse();
     }
index 00214262afd44bb750bd7dd05597f0c9d00f125e..f8b0dc44112c93caf4abcd5dbe5351bc887b9841 100644 (file)
@@ -166,14 +166,7 @@ class CRM_Export_BAO_Export {
       $groupBy = "civicrm_activity.id ";
     }
 
-    if (!empty($groupBy)) {
-      if (!Civi::settings()->get('searchPrimaryDetailsOnly')) {
-        CRM_Core_DAO::disableFullGroupByMode();
-      }
-      $groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($query->_select, $groupBy);
-    }
-
-    return $groupBy;
+    return $groupBy ? ' GROUP BY ' . $groupBy : '';
   }
 
   /**
@@ -228,15 +221,8 @@ class CRM_Export_BAO_Export {
 
     $processor = new CRM_Export_BAO_ExportProcessor($exportMode, $fields, $queryOperator, $mergeSameHousehold);
     $returnProperties = array();
-
-    $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
-    // Warning - this imProviders var is used in a somewhat fragile way - don't rename it
-    // without manually testing the export of IM provider still works.
-    $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id');
     self::$relationshipTypes = $processor->getRelationshipTypes();
 
-    $queryMode = $processor->getQueryMode();
-
     if ($fields) {
       foreach ($fields as $key => $value) {
         $fieldName = CRM_Utils_Array::value(1, $value);
@@ -301,6 +287,7 @@ class CRM_Export_BAO_Export {
         if (!array_key_exists($column, $returnProperties)) {
           $returnProperties[$column] = 1;
           $column = $column == 'id' ? 'civicrm_primary_id' : $column;
+          $processor->setColumnAsCalculationOnly($column);
           $exportParams['merge_same_address']['temp_columns'][$column] = 1;
         }
       }
@@ -333,6 +320,7 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
         if (!array_key_exists($column, $returnProperties)) {
           $returnProperties[$column] = 1;
           $exportParams['postal_mailing_export']['temp_columns'][$column] = 1;
+          $processor->setColumnAsCalculationOnly($column);
         }
       }
     }
@@ -432,29 +420,19 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
 
     $paymentDetails = array();
     if ($processor->isExportPaymentFields()) {
-
       // get payment related in for event and members
       $paymentDetails = CRM_Contribute_BAO_Contribution::getContributionDetails($exportMode, $ids);
       //get all payment headers.
       // If we haven't selected specific payment fields, load in all the
       // payment headers.
       if (!$processor->isExportSpecifiedPaymentFields()) {
-        $paymentHeaders = self::componentPaymentFields();
         if (!empty($paymentDetails)) {
           $addPaymentHeader = TRUE;
         }
       }
-      // If we have selected specific payment fields, leave the payment headers
-      // as an empty array; the headers for each selected field will be added
-      // elsewhere.
-      else {
-        $paymentHeaders = array();
-      }
-      $nullContributionDetails = array_fill_keys(array_keys($paymentHeaders), NULL);
     }
 
     $componentDetails = array();
-    $setHeader = TRUE;
 
     $rowCount = self::EXPORT_ROW_COUNT;
     $offset = 0;
@@ -463,15 +441,27 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
 
     $count = -1;
 
-    // for CRM-3157 purposes
-    $i18n = CRM_Core_I18n::singleton();
+    list($outputColumns, $sqlColumns, $metadata) = self::getExportStructureArrays($returnProperties, $processor);
+    $headerRows = $processor->getHeaderRows();
 
-    list($outputColumns, $headerRows, $sqlColumns, $metadata) = self::getExportStructureArrays($returnProperties, $processor);
+    // add payment headers if required
+    if ($addPaymentHeader && $processor->isExportPaymentFields()) {
+      // @todo rather than do this for every single row do it before the loop starts.
+      // where other header definitions take place.
+      $headerRows = array_merge($headerRows, $processor->getPaymentHeaders());
+      foreach (array_keys($processor->getPaymentHeaders()) as $paymentHdr) {
+        self::sqlColumnDefn($processor, $sqlColumns, $paymentHdr);
+      }
+    }
 
+    $exportTempTable = self::createTempTable($sqlColumns);
     $limitReached = FALSE;
+
     while (!$limitReached) {
       $limitQuery = "{$queryString} LIMIT {$offset}, {$rowCount}";
+      CRM_Core_DAO::disableFullGroupByMode();
       $iterationDAO = CRM_Core_DAO::executeQuery($limitQuery);
+      CRM_Core_DAO::reenableFullGroupByMode();
       // If this is less than our limit by the end of the iteration we do not need to run the query again to
       // check if some remain.
       $rowsThisIteration = 0;
@@ -479,99 +469,7 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
       while ($iterationDAO->fetch()) {
         $count++;
         $rowsThisIteration++;
-        $row = array();
-        $query->convertToPseudoNames($iterationDAO);
-
-        //first loop through output columns so that we return what is required, and in same order.
-        foreach ($outputColumns as $field => $value) {
-
-          // add im_provider to $dao object
-          if ($field == 'im_provider' && property_exists($iterationDAO, 'provider_id')) {
-            $iterationDAO->im_provider = $iterationDAO->provider_id;
-          }
-
-          //build row values (data)
-          $fieldValue = NULL;
-          if (property_exists($iterationDAO, $field)) {
-            $fieldValue = $iterationDAO->$field;
-            // to get phone type from phone type id
-            if ($field == 'phone_type_id' && isset($phoneTypes[$fieldValue])) {
-              $fieldValue = $phoneTypes[$fieldValue];
-            }
-            elseif ($field == 'provider_id' || $field == 'im_provider') {
-              $fieldValue = CRM_Utils_Array::value($fieldValue, $imProviders);
-            }
-            elseif (strstr($field, 'master_id')) {
-              $masterAddressId = NULL;
-              if (isset($iterationDAO->$field)) {
-                $masterAddressId = $iterationDAO->$field;
-              }
-              // get display name of contact that address is shared.
-              $fieldValue = CRM_Contact_BAO_Contact::getMasterDisplayName($masterAddressId);
-            }
-          }
-
-          if ($processor->isRelationshipTypeKey($field)) {
-            foreach (array_keys($value) as $property) {
-              if ($property === 'location') {
-                // @todo just undo all this nasty location wrangling!
-                foreach ($value['location'] as $locationKey => $locationFields) {
-                  foreach (array_keys($locationFields) as $locationField) {
-                    $fieldKey = str_replace(' ', '_', $locationKey . '-' . $locationField);
-                    $row[$field . '_' . $fieldKey] = $processor->getRelationshipValue($field, $iterationDAO->contact_id, $fieldKey);
-                  }
-                }
-              }
-              else {
-                $row[$field . '_' . $property] = $processor->getRelationshipValue($field, $iterationDAO->contact_id, $property);
-              }
-            }
-          }
-          else {
-            $row[$field] = self::getTransformedFieldValue($field, $iterationDAO, $fieldValue, $i18n, $metadata, $paymentDetails, $processor);
-          }
-        }
-
-        // add payment headers if required
-        if ($addPaymentHeader && $processor->isExportPaymentFields()) {
-          // @todo rather than do this for every single row do it before the loop starts.
-          // where other header definitions take place.
-          $headerRows = array_merge($headerRows, $paymentHeaders);
-          foreach (array_keys($paymentHeaders) as $paymentHdr) {
-            self::sqlColumnDefn($processor, $sqlColumns, $paymentHdr);
-          }
-        }
-
-        if ($setHeader) {
-          $exportTempTable = self::createTempTable($sqlColumns);
-        }
-
-        //build header only once
-        $setHeader = FALSE;
-
-        // If specific payment fields have been selected for export, payment
-        // data will already be in $row. Otherwise, add payment related
-        // information, if appropriate.
-        if ($addPaymentHeader) {
-          if (!$processor->isExportSpecifiedPaymentFields()) {
-            if ($processor->isExportPaymentFields()) {
-              $paymentData = CRM_Utils_Array::value($row[$paymentTableId], $paymentDetails);
-              if (!is_array($paymentData) || empty($paymentData)) {
-                $paymentData = $nullContributionDetails;
-              }
-              $row = array_merge($row, $paymentData);
-            }
-            elseif (!empty($paymentDetails)) {
-              $row = array_merge($row, $nullContributionDetails);
-            }
-          }
-        }
-        //remove organization name for individuals if it is set for current employer
-        if (!empty($row['contact_type']) &&
-          $row['contact_type'] == 'Individual' && array_key_exists('organization_name', $row)
-        ) {
-          $row['organization_name'] = '';
-        }
+        $row = $processor->buildRow($query, $iterationDAO, $outputColumns, $metadata, $paymentDetails, $addPaymentHeader, $paymentTableId);
 
         // add component info
         // write the row to a file
@@ -597,12 +495,12 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
       if (isset($exportParams['postal_mailing_export']['postal_mailing_export']) &&
         $exportParams['postal_mailing_export']['postal_mailing_export'] == 1
       ) {
-        self::postalMailingFormat($exportTempTable, $headerRows, $sqlColumns, $exportMode);
+        self::postalMailingFormat($exportTempTable, $sqlColumns, $exportMode);
       }
 
       // do merge same address and merge same household processing
       if ($mergeSameAddress) {
-        self::mergeSameAddress($exportTempTable, $headerRows, $sqlColumns, $exportParams);
+        self::mergeSameAddress($exportTempTable, $sqlColumns, $exportParams);
       }
 
       // merge the records if they have corresponding households
@@ -830,11 +728,10 @@ CREATE TABLE {$exportTempTable} (
 
   /**
    * @param string $tableName
-   * @param $headerRows
    * @param $sqlColumns
    * @param array $exportParams
    */
-  public static function mergeSameAddress($tableName, &$headerRows, &$sqlColumns, $exportParams) {
+  public static function mergeSameAddress($tableName, &$sqlColumns, $exportParams) {
     // check if any records are present based on if they have used shared address feature,
     // and not based on if city / state .. matches.
     $sql = "
@@ -931,7 +828,7 @@ WHERE  id IN ( $deleteIDString )
       $unsetKeys = array_keys($sqlColumns);
       foreach ($unsetKeys as $headerKey => $sqlColKey) {
         if (array_key_exists($sqlColKey, $exportParams['merge_same_address']['temp_columns'])) {
-          unset($sqlColumns[$sqlColKey], $headerRows[$headerKey]);
+          unset($sqlColumns[$sqlColKey]);
         }
       }
     }
@@ -1176,7 +1073,9 @@ CREATE TABLE {$exportTempTable}_temp SELECT *
 FROM {$exportTempTable}
 GROUP BY civicrm_primary_id ";
 
+    CRM_Core_DAO::disableFullGroupByMode();
     CRM_Core_DAO::executeQuery($query);
+    CRM_Core_DAO::reenableFullGroupByMode();
 
     $query = "DROP TABLE $exportTempTable";
     CRM_Core_DAO::executeQuery($query);
@@ -1230,30 +1129,14 @@ LIMIT $offset, $limit
     }
   }
 
-  /**
-   * Manipulate header rows for relationship fields.
-   *
-   * @param $headerRows
-   */
-  public static function manipulateHeaderRows(&$headerRows) {
-    foreach ($headerRows as & $header) {
-      $split = explode('-', $header);
-      if ($relationTypeName = CRM_Utils_Array::value($split[0], self::$relationshipTypes)) {
-        $split[0] = $relationTypeName;
-        $header = implode('-', $split);
-      }
-    }
-  }
-
   /**
    * Exclude contacts who are deceased, have "Do not mail" privacy setting,
    * or have no street address
    * @param $exportTempTable
-   * @param $headerRows
    * @param $sqlColumns
    * @param $exportParams
    */
-  public static function postalMailingFormat($exportTempTable, &$headerRows, &$sqlColumns, $exportParams) {
+  public static function postalMailingFormat($exportTempTable, &$sqlColumns, $exportParams) {
     $whereClause = array();
 
     if (array_key_exists('is_deceased', $sqlColumns)) {
@@ -1296,7 +1179,7 @@ WHERE  {$whereClause}";
       $unsetKeys = array_keys($sqlColumns);
       foreach ($unsetKeys as $headerKey => $sqlColKey) {
         if (array_key_exists($sqlColKey, $exportParams['postal_mailing_export']['temp_columns'])) {
-          unset($sqlColumns[$sqlColKey], $headerRows[$headerKey]);
+          unset($sqlColumns[$sqlColKey]);
         }
       }
     }
@@ -1304,6 +1187,11 @@ WHERE  {$whereClause}";
 
   /**
    * Build componentPayment fields.
+   *
+   * This is no longer used by export but BAO_Mapping still calls it & we
+   * should find a generic way to handle this or move this to that class.
+   *
+   * @deprecated
    */
   public static function componentPaymentFields() {
     static $componentPaymentFields;
@@ -1319,42 +1207,6 @@ WHERE  {$whereClause}";
     return $componentPaymentFields;
   }
 
-  /**
-   * Set the definition for the header rows and sql columns based on the field to output.
-   *
-   * @param string $field
-   * @param array $headerRows
-   * @param \CRM_Export_BAO_ExportProcessor $processor
-   *
-   * @return array
-   */
-  public static function setHeaderRows($field, $headerRows, $processor) {
-
-    $queryFields = $processor->getQueryFields();
-    if (substr($field, -11) == 'campaign_id') {
-      // @todo - set this correctly in the xml rather than here.
-      $headerRows[] = ts('Campaign ID');
-    }
-    elseif ($processor->isMergeSameHousehold() && $field === 'id') {
-      $headerRows[] = ts('Household ID');
-    }
-    elseif (isset($queryFields[$field]['title'])) {
-      $headerRows[] = $queryFields[$field]['title'];
-    }
-    elseif ($field == 'provider_id') {
-      // @todo - set this correctly in the xml rather than here.
-      $headerRows[] = ts('IM Service Provider');
-    }
-    elseif ($processor->isExportPaymentFields() && array_key_exists($field, self::componentPaymentFields())) {
-      $headerRows[] = CRM_Utils_Array::value($field, self::componentPaymentFields());
-    }
-    else {
-      $headerRows[] = $field;
-    }
-
-    return $headerRows;
-  }
-
   /**
    * Get the various arrays that we use to structure our output.
    *
@@ -1384,14 +1236,14 @@ WHERE  {$whereClause}";
    *       yet find a way to comment them for posterity.
    */
   public static function getExportStructureArrays($returnProperties, $processor) {
-    $metadata = $headerRows = $outputColumns = $sqlColumns = array();
+    $metadata = $outputColumns = $sqlColumns = array();
     $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
     $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id');
     $queryFields = $processor->getQueryFields();
     foreach ($returnProperties as $key => $value) {
       if (($key != 'location' || !is_array($value)) && !$processor->isRelationshipTypeKey($key)) {
         $outputColumns[$key] = $value;
-        $headerRows = self::setHeaderRows($key, $headerRows, $processor);
+        $processor->addOutputSpecification($key);
         self::sqlColumnDefn($processor, $sqlColumns, $key);
       }
       elseif ($processor->isRelationshipTypeKey($key)) {
@@ -1400,39 +1252,11 @@ WHERE  {$whereClause}";
         foreach ($value as $relationField => $relationValue) {
           // below block is same as primary block (duplicate)
           if (isset($queryFields[$relationField]['title'])) {
-            if ($queryFields[$relationField]['name'] == 'name') {
-              $headerName = $field . '-' . $relationField;
-            }
-            else {
-              if ($relationField == 'current_employer') {
-                $headerName = $field . '-' . 'current_employer';
-              }
-              else {
-                $headerName = $field . '-' . $relationField;
-              }
-            }
-
             if (!$processor->isHouseholdMergeRelationshipTypeKey($field)) {
               // Do not add to header row if we are only generating for merge reasons.
-              $headerRows[] = $headerName;
+              $processor->addOutputSpecification($relationField, $key);
             }
-
-            self::sqlColumnDefn($processor, $sqlColumns, $headerName);
-          }
-          elseif ($relationField == 'phone_type_id') {
-            $headerName = $field . '-' . 'Phone Type';
-            $headerRows[] = $headerName;
-            self::sqlColumnDefn($processor, $sqlColumns, $headerName);
-          }
-          elseif ($relationField == 'provider_id') {
-            $headerName = $field . '-' . 'Im Service Provider';
-            $headerRows[] = $headerName;
-            self::sqlColumnDefn($processor, $sqlColumns, $headerName);
-          }
-          elseif ($relationField == 'state_province_id') {
-            $headerName = $field . '-' . 'state_province_id';
-            $headerRows[] = $headerName;
-            self::sqlColumnDefn($processor, $sqlColumns, $headerName);
+            self::sqlColumnDefn($processor, $sqlColumns, $field . '-' . $relationField);
           }
           elseif (is_array($relationValue) && $relationField == 'location') {
             // fix header for location type case
@@ -1450,14 +1274,12 @@ WHERE  {$whereClause}";
                     $hdr .= "-" . CRM_Core_PseudoConstant::getLabel('CRM_Core_BAO_IM', 'provider_id', $type[1]);
                   }
                 }
-                $headerName = $field . '-' . $hdr;
-                $headerRows[] = $headerName;
-                self::sqlColumnDefn($processor, $sqlColumns, $headerName);
+                $processor->addOutputSpecification($field, $key, $ltype, CRM_Utils_Array::value(1, $type));
+                self::sqlColumnDefn($processor, $sqlColumns, $field . '-' . $hdr);
               }
             }
           }
         }
-        self::manipulateHeaderRows($headerRows);
       }
       else {
         foreach ($value as $locationType => $locationFields) {
@@ -1482,7 +1304,7 @@ WHERE  {$whereClause}";
               $metadata[$daoFieldName]['pseudoconstant']['var'] = 'imProviders';
             }
             self::sqlColumnDefn($processor, $sqlColumns, $outputFieldName);
-            $headerRows = self::setHeaderRows($outputFieldName, $headerRows, $processor);
+            $processor->addOutputSpecification($outputFieldName, NULL, $locationType, CRM_Utils_Array::value(1, $type));
             self::sqlColumnDefn($processor, $sqlColumns, $outputFieldName);
             if ($actualDBFieldName == 'country' || $actualDBFieldName == 'world_region') {
               $metadata[$daoFieldName] = array('context' => 'country');
@@ -1495,7 +1317,7 @@ WHERE  {$whereClause}";
         }
       }
     }
-    return array($outputColumns, $headerRows, $sqlColumns, $metadata);
+    return array($outputColumns, $sqlColumns, $metadata);
   }
 
   /**
@@ -1699,11 +1521,13 @@ WHERE  {$whereClause}";
       $today = date('Ymd');
       $relationActive = " AND (crel.is_active = 1 AND ( crel.end_date is NULL OR crel.end_date >= {$today} ) )";
       $relationWhere = " WHERE contact_a.is_deleted = 0 {$relationshipClause} {$relationActive}";
-      $relationGroupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($relationQuery->_select, "crel.{$contactA}");
+      CRM_Core_DAO::disableFullGroupByMode();
       $relationSelect = "{$relationSelect}, {$contactA} as refContact ";
-      $relationQueryString = "$relationSelect $relationFrom $relationWhere $relationHaving $relationGroupBy";
+      $relationQueryString = "$relationSelect $relationFrom $relationWhere $relationHaving GROUP BY crel.{$contactA}";
 
       $allRelContactDAO = CRM_Core_DAO::executeQuery($relationQueryString);
+      CRM_Core_DAO::reenableFullGroupByMode();
+
       while ($allRelContactDAO->fetch()) {
         $relationQuery->convertToPseudoNames($allRelContactDAO);
         $row = [];
@@ -1728,111 +1552,4 @@ WHERE  {$whereClause}";
     }
   }
 
-  /**
-   * @param $field
-   * @param $iterationDAO
-   * @param $fieldValue
-   * @param $i18n
-   * @param $metadata
-   * @param $paymentDetails
-   *
-   * @param \CRM_Export_BAO_ExportProcessor $processor
-   *
-   * @return string
-   */
-  protected static function getTransformedFieldValue($field, $iterationDAO, $fieldValue, $i18n, $metadata, $paymentDetails, $processor) {
-
-    if ($field == 'id') {
-      return $iterationDAO->contact_id;
-      // special case for calculated field
-    }
-    elseif ($field == 'source_contact_id') {
-      return $iterationDAO->contact_id;
-    }
-    elseif ($field == 'pledge_balance_amount') {
-      return $iterationDAO->pledge_amount - $iterationDAO->pledge_total_paid;
-      // special case for calculated field
-    }
-    elseif ($field == 'pledge_next_pay_amount') {
-      return $iterationDAO->pledge_next_pay_amount + $iterationDAO->pledge_outstanding_amount;
-    }
-    elseif (isset($fieldValue) &&
-      $fieldValue != ''
-    ) {
-      //check for custom data
-      if ($cfID = CRM_Core_BAO_CustomField::getKeyID($field)) {
-        return CRM_Core_BAO_CustomField::displayValue($fieldValue, $cfID);
-      }
-
-      elseif (in_array($field, array(
-        'email_greeting',
-        'postal_greeting',
-        'addressee',
-      ))) {
-        //special case for greeting replacement
-        $fldValue = "{$field}_display";
-        return $iterationDAO->$fldValue;
-      }
-      else {
-        //normal fields with a touch of CRM-3157
-        switch ($field) {
-          case 'country':
-          case 'world_region':
-            return $i18n->crm_translate($fieldValue, array('context' => 'country'));
-
-          case 'state_province':
-            return $i18n->crm_translate($fieldValue, array('context' => 'province'));
-
-          case 'gender':
-          case 'preferred_communication_method':
-          case 'preferred_mail_format':
-          case 'communication_style':
-            return $i18n->crm_translate($fieldValue);
-
-          default:
-            if (isset($metadata[$field])) {
-              // No I don't know why we do it this way & whether we could
-              // make better use of pseudoConstants.
-              if (!empty($metadata[$field]['context'])) {
-                return $i18n->crm_translate($fieldValue, $metadata[$field]);
-              }
-              if (!empty($metadata[$field]['pseudoconstant'])) {
-                // This is not our normal syntax for pseudoconstants but I am a bit loath to
-                // call an external function until sure it is not increasing php processing given this
-                // may be iterated 100,000 times & we already have the $imProvider var loaded.
-                // That can be next refactor...
-                // Yes - definitely feeling hatred for this bit of code - I know you will beat me up over it's awfulness
-                // but I have to reach a stable point....
-                $varName = $metadata[$field]['pseudoconstant']['var'];
-                if ($varName === 'imProviders') {
-                  return CRM_Core_PseudoConstant::getLabel('CRM_Core_DAO_IM', 'provider_id', $fieldValue);
-                }
-                if ($varName === 'phoneTypes') {
-                  return CRM_Core_PseudoConstant::getLabel('CRM_Core_DAO_Phone', 'phone_type_id', $fieldValue);
-                }
-              }
-
-            }
-            return $fieldValue;
-        }
-      }
-    }
-    elseif ($processor->isExportSpecifiedPaymentFields() && array_key_exists($field, self::componentPaymentFields())) {
-      $paymentTableId = $processor->getPaymentTableID();
-      $paymentData = CRM_Utils_Array::value($iterationDAO->$paymentTableId, $paymentDetails);
-      $payFieldMapper = array(
-        'componentPaymentField_total_amount' => 'total_amount',
-        'componentPaymentField_contribution_status' => 'contribution_status',
-        'componentPaymentField_payment_instrument' => 'pay_instru',
-        'componentPaymentField_transaction_id' => 'trxn_id',
-        'componentPaymentField_received_date' => 'receive_date',
-      );
-      return CRM_Utils_Array::value($payFieldMapper[$field], $paymentData, '');
-    }
-    else {
-      // if field is empty or null
-      return '';
-    }
-  }
-
 }
index 0b77a52eda6e01de05a903991dc6b6c1812f4e50..72f99b32a74ecac33806c3efbdb2ed6edf50149a 100644 (file)
@@ -115,6 +115,11 @@ class CRM_Export_BAO_ExportProcessor {
    */
   protected $returnProperties = [];
 
+  /**
+   * @var array
+   */
+  protected $outputSpecification = [];
+
   /**
    * CRM_Export_BAO_ExportProcessor constructor.
    *
@@ -389,6 +394,33 @@ class CRM_Export_BAO_ExportProcessor {
     }
   }
 
+  /**
+   * Get the label for the header row based on the field to output.
+   *
+   * @param string $field
+   *
+   * @return string
+   */
+  public function getHeaderForRow($field) {
+    if (substr($field, -11) == 'campaign_id') {
+      // @todo - set this correctly in the xml rather than here.
+      // This will require a generalised handling cleanup
+      return ts('Campaign ID');
+    }
+    if ($this->isMergeSameHousehold() && $field === 'id') {
+      return ts('Household ID');
+    }
+    elseif (isset($this->getQueryFields()[$field]['title'])) {
+      return $this->getQueryFields()[$field]['title'];
+    }
+    elseif ($this->isExportPaymentFields() && array_key_exists($field, $this->getcomponentPaymentFields())) {
+      return CRM_Utils_Array::value($field, $this->getcomponentPaymentFields());
+    }
+    else {
+      return $field;
+    }
+  }
+
   /**
    * @param $params
    * @param $order
@@ -409,6 +441,263 @@ class CRM_Export_BAO_ExportProcessor {
     return array($query, $select, $from, $where, $having);
   }
 
+  /**
+   * Add a row to the specification for how to output data.
+   *
+   * @param string $key
+   * @param string $relationshipType
+   * @param string $locationType
+   * @param int $entityTypeID phone_type_id or provider_id for phone or im fields.
+   */
+  public function addOutputSpecification($key, $relationshipType = NULL, $locationType = NULL, $entityTypeID = NULL) {
+    $label = $this->getHeaderForRow($key);
+    $labelPrefix = $fieldPrefix = [];
+    if ($relationshipType) {
+      $labelPrefix[] = $this->getRelationshipTypes()[$relationshipType];
+      $fieldPrefix[] = $relationshipType;
+    }
+    if ($locationType) {
+      $labelPrefix[] = $fieldPrefix[] = $locationType;
+    }
+    if ($entityTypeID) {
+      if ($key === 'phone') {
+        $labelPrefix[] = $fieldPrefix[] = CRM_Core_PseudoConstant::getLabel('CRM_Core_BAO_Phone', 'phone_type_id', $entityTypeID);
+      }
+      if ($key === 'im') {
+        $labelPrefix[] = $fieldPrefix[] = CRM_Core_PseudoConstant::getLabel('CRM_Core_BAO_IM', 'provider_id', $entityTypeID);
+      }
+    }
+    $index = ($fieldPrefix ? (implode('-', $fieldPrefix)  . '-') : '') . $key;
+    $this->outputSpecification[$index]['header'] = ($labelPrefix ? (implode('-', $labelPrefix) . '-') : '') . $label;
+
+  }
+
+  /**
+   * Mark a column as only required for calculations.
+   *
+   * Do not include the row with headers.
+   *
+   * @param string $column
+   */
+  public function setColumnAsCalculationOnly($column) {
+    $this->outputSpecification[$column]['do_not_output_to_csv'] = TRUE;
+  }
+
+  /**
+   * @return array
+   */
+  public function getHeaderRows() {
+    $headerRows = [];
+    foreach ($this->outputSpecification as $key => $spec) {
+      if (empty($spec['do_not_output_to_csv'])) {
+        $headerRows[] = $spec['header'];
+      }
+    }
+    return $headerRows;
+  }
+
+  /**
+   * Build the row for output.
+   *
+   * @param \CRM_Contact_BAO_Query $query
+   * @param CRM_Core_DAO $iterationDAO
+   * @param array $outputColumns
+   * @param $metadata
+   * @param $paymentDetails
+   * @param $addPaymentHeader
+   * @param $paymentTableId
+   *
+   * @return array
+   */
+  public function buildRow($query, $iterationDAO, $outputColumns, $metadata, $paymentDetails, $addPaymentHeader, $paymentTableId) {
+    $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
+    $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id');
+
+    $row = [];
+    $query->convertToPseudoNames($iterationDAO);
+
+    //first loop through output columns so that we return what is required, and in same order.
+    foreach ($outputColumns as $field => $value) {
+
+      // add im_provider to $dao object
+      if ($field == 'im_provider' && property_exists($iterationDAO, 'provider_id')) {
+        $iterationDAO->im_provider = $iterationDAO->provider_id;
+      }
+
+      //build row values (data)
+      $fieldValue = NULL;
+      if (property_exists($iterationDAO, $field)) {
+        $fieldValue = $iterationDAO->$field;
+        // to get phone type from phone type id
+        if ($field == 'phone_type_id' && isset($phoneTypes[$fieldValue])) {
+          $fieldValue = $phoneTypes[$fieldValue];
+        }
+        elseif ($field == 'provider_id' || $field == 'im_provider') {
+          $fieldValue = CRM_Utils_Array::value($fieldValue, $imProviders);
+        }
+        elseif (strstr($field, 'master_id')) {
+          $masterAddressId = NULL;
+          if (isset($iterationDAO->$field)) {
+            $masterAddressId = $iterationDAO->$field;
+          }
+          // get display name of contact that address is shared.
+          $fieldValue = CRM_Contact_BAO_Contact::getMasterDisplayName($masterAddressId);
+        }
+      }
+
+      if ($this->isRelationshipTypeKey($field)) {
+        foreach (array_keys($value) as $property) {
+          if ($property === 'location') {
+            // @todo just undo all this nasty location wrangling!
+            foreach ($value['location'] as $locationKey => $locationFields) {
+              foreach (array_keys($locationFields) as $locationField) {
+                $fieldKey = str_replace(' ', '_', $locationKey . '-' . $locationField);
+                $row[$field . '_' . $fieldKey] = $this->getRelationshipValue($field, $iterationDAO->contact_id, $fieldKey);
+              }
+            }
+          }
+          else {
+            $row[$field . '_' . $property] = $this->getRelationshipValue($field, $iterationDAO->contact_id, $property);
+          }
+        }
+      }
+      else {
+        $row[$field] = $this->getTransformedFieldValue($field, $iterationDAO, $fieldValue, $metadata, $paymentDetails);
+      }
+    }
+
+    // If specific payment fields have been selected for export, payment
+    // data will already be in $row. Otherwise, add payment related
+    // information, if appropriate.
+    if ($addPaymentHeader) {
+      if (!$this->isExportSpecifiedPaymentFields()) {
+        $nullContributionDetails = array_fill_keys(array_keys($this->getPaymentHeaders()), NULL);
+        if ($this->isExportPaymentFields()) {
+          $paymentData = CRM_Utils_Array::value($row[$paymentTableId], $paymentDetails);
+          if (!is_array($paymentData) || empty($paymentData)) {
+            $paymentData = $nullContributionDetails;
+          }
+          $row = array_merge($row, $paymentData);
+        }
+        elseif (!empty($paymentDetails)) {
+          $row = array_merge($row, $nullContributionDetails);
+        }
+      }
+    }
+    //remove organization name for individuals if it is set for current employer
+    if (!empty($row['contact_type']) &&
+      $row['contact_type'] == 'Individual' && array_key_exists('organization_name', $row)
+    ) {
+      $row['organization_name'] = '';
+    }
+    return $row;
+  }
+
+  /**
+   * @param $field
+   * @param $iterationDAO
+   * @param $fieldValue
+   * @param $metadata
+   * @param $paymentDetails
+   *
+   * @return string
+   */
+  public function getTransformedFieldValue($field, $iterationDAO, $fieldValue, $metadata, $paymentDetails) {
+
+    $i18n = CRM_Core_I18n::singleton();
+    if ($field == 'id') {
+      return $iterationDAO->contact_id;
+      // special case for calculated field
+    }
+    elseif ($field == 'source_contact_id') {
+      return $iterationDAO->contact_id;
+    }
+    elseif ($field == 'pledge_balance_amount') {
+      return $iterationDAO->pledge_amount - $iterationDAO->pledge_total_paid;
+      // special case for calculated field
+    }
+    elseif ($field == 'pledge_next_pay_amount') {
+      return $iterationDAO->pledge_next_pay_amount + $iterationDAO->pledge_outstanding_amount;
+    }
+    elseif (isset($fieldValue) &&
+      $fieldValue != ''
+    ) {
+      //check for custom data
+      if ($cfID = CRM_Core_BAO_CustomField::getKeyID($field)) {
+        return CRM_Core_BAO_CustomField::displayValue($fieldValue, $cfID);
+      }
+
+      elseif (in_array($field, array(
+        'email_greeting',
+        'postal_greeting',
+        'addressee',
+      ))) {
+        //special case for greeting replacement
+        $fldValue = "{$field}_display";
+        return $iterationDAO->$fldValue;
+      }
+      else {
+        //normal fields with a touch of CRM-3157
+        switch ($field) {
+          case 'country':
+          case 'world_region':
+            return $i18n->crm_translate($fieldValue, array('context' => 'country'));
+
+          case 'state_province':
+            return $i18n->crm_translate($fieldValue, array('context' => 'province'));
+
+          case 'gender':
+          case 'preferred_communication_method':
+          case 'preferred_mail_format':
+          case 'communication_style':
+            return $i18n->crm_translate($fieldValue);
+
+          default:
+            if (isset($metadata[$field])) {
+              // No I don't know why we do it this way & whether we could
+              // make better use of pseudoConstants.
+              if (!empty($metadata[$field]['context'])) {
+                return $i18n->crm_translate($fieldValue, $metadata[$field]);
+              }
+              if (!empty($metadata[$field]['pseudoconstant'])) {
+                // This is not our normal syntax for pseudoconstants but I am a bit loath to
+                // call an external function until sure it is not increasing php processing given this
+                // may be iterated 100,000 times & we already have the $imProvider var loaded.
+                // That can be next refactor...
+                // Yes - definitely feeling hatred for this bit of code - I know you will beat me up over it's awfulness
+                // but I have to reach a stable point....
+                $varName = $metadata[$field]['pseudoconstant']['var'];
+                if ($varName === 'imProviders') {
+                  return CRM_Core_PseudoConstant::getLabel('CRM_Core_DAO_IM', 'provider_id', $fieldValue);
+                }
+                if ($varName === 'phoneTypes') {
+                  return CRM_Core_PseudoConstant::getLabel('CRM_Core_DAO_Phone', 'phone_type_id', $fieldValue);
+                }
+              }
+
+            }
+            return $fieldValue;
+        }
+      }
+    }
+    elseif ($this->isExportSpecifiedPaymentFields() && array_key_exists($field, $this->getcomponentPaymentFields())) {
+      $paymentTableId = $this->getPaymentTableID();
+      $paymentData = CRM_Utils_Array::value($iterationDAO->$paymentTableId, $paymentDetails);
+      $payFieldMapper = array(
+        'componentPaymentField_total_amount' => 'total_amount',
+        'componentPaymentField_contribution_status' => 'contribution_status',
+        'componentPaymentField_payment_instrument' => 'pay_instru',
+        'componentPaymentField_transaction_id' => 'trxn_id',
+        'componentPaymentField_received_date' => 'receive_date',
+      );
+      return CRM_Utils_Array::value($payFieldMapper[$field], $paymentData, '');
+    }
+    else {
+      // if field is empty or null
+      return '';
+    }
+  }
+
   /**
    * Get array of fields to return, over & above those defined in the main contact exportable fields.
    *
@@ -419,14 +708,6 @@ class CRM_Export_BAO_ExportProcessor {
    *   Array of fields to return in the format ['field_name' => 1,...]
    */
   public function getAdditionalReturnProperties() {
-
-    $missing = [
-      'location_type',
-      'im_provider',
-      'phone_type_id',
-      'provider_id',
-      'current_employer',
-    ];
     if ($this->getQueryMode() === CRM_Contact_BAO_Query::MODE_CONTACTS) {
       $componentSpecificFields = [];
     }
@@ -446,7 +727,7 @@ class CRM_Export_BAO_ExportProcessor {
       $componentSpecificFields = array_merge($componentSpecificFields, CRM_Contribute_BAO_Query::softCreditReturnProperties(TRUE));
       unset($componentSpecificFields['contribution_status_id']);
     }
-    return array_merge(array_fill_keys($missing, 1), $componentSpecificFields);
+    return $componentSpecificFields;
   }
 
   /**
@@ -519,9 +800,11 @@ class CRM_Export_BAO_ExportProcessor {
   /**
    * Get fields that indicate payment fields have been requested for a component.
    *
+   * Ideally this should be protected but making it temporarily public helps refactoring..
+   *
    * @return array
    */
-  protected function getComponentPaymentFields() {
+  public function getComponentPaymentFields() {
     return [
       'componentPaymentField_total_amount' => ts('Total Amount'),
       'componentPaymentField_contribution_status' => ts('Contribution Status'),
@@ -531,6 +814,19 @@ class CRM_Export_BAO_ExportProcessor {
     ];
   }
 
+  /**
+   * Get headers for payment fields.
+   *
+   * Returns an array of contribution fields when the entity supports payment fields and specific fields
+   * are not specified. This is a transitional function for refactoring legacy code.
+   */
+  public function getPaymentHeaders() {
+    if ($this->isExportPaymentFields() && !$this->isExportSpecifiedPaymentFields()) {
+      return $this->getcomponentPaymentFields();
+    }
+    return [];
+  }
+
   /**
    * Get the default properties when not specified.
    *
index af478d4adfad066729eaf4ff6f9e745426a60920..d26fdeaa6faa5dc551e3414860fb44a276873a87 100644 (file)
@@ -98,6 +98,7 @@ class CRM_Export_Form_Select extends CRM_Core_Form_Task {
     $components = array('Contact', 'Contribute', 'Member', 'Event', 'Pledge', 'Case', 'Grant', 'Activity');
 
     // FIXME: This should use a modified version of CRM_Contact_Form_Search::getModeValue but it doesn't have all the contexts
+    // FIXME: Or better still, use CRM_Core_DAO_AllCoreTables::getBriefName($daoName) to get the $entityShortName
     switch ($this->getQueryMode()) {
       case CRM_Contact_BAO_Query::MODE_CONTRIBUTE:
         $entityShortname = 'Contribute';
index a0a6e4bb033d5e017a96e4489ca26de6e563fa63..cc6d8e6a57c2d4a696e6345b0b3e23e31fe71646 100644 (file)
@@ -428,7 +428,7 @@ class CRM_Member_Page_DashBoard extends CRM_Core_Page {
 
     $this->assign('membershipSummary', $membershipSummary);
     $this->assign('totalCount', $totalCount);
-    $this->assign('month', CRM_Utils_Date::customFormat($monthStartTs, '%B'));
+    $this->assign('month', CRM_Utils_Date::customFormatTs($monthStartTs, '%B'));
     $this->assign('year', date('Y', $monthStartTs));
     $this->assign('premonth', CRM_Utils_Date::customFormat($preMonth, '%B'));
     $this->assign('currentMonth', date('F'));
index 922653857d8f5264384f966e71c77ad992266b09..670bb0fb8c16e2fceca22eb171ee0a5f99aac381 100644 (file)
@@ -737,7 +737,7 @@ WHERE  id = %1";
         case 'Radio':
           //special case if user select -none-
           if ($params["price_{$id}"] <= 0) {
-            continue;
+            break;
           }
           $params["price_{$id}"] = array($params["price_{$id}"] => 1);
           $optionValueId = CRM_Utils_Array::key(1, $params["price_{$id}"]);
index 83b15728809fa4c42d3fc3d3ee6a50d28a238d61..8286f49d9eed55facbb8a72d29d6e3677cea11d1 100644 (file)
@@ -1145,19 +1145,20 @@ class CRM_Report_Form extends CRM_Core_Form {
    *
    * This function creates a table AND adds the details to the developer tab & $this->>temporary tables.
    *
-   * @todo improve presentation on the developer tab since CREATE TEMPORARY is removed.
-   *
    * @param string $identifier
    * @param $sql
-   * @param bool $isTrueTemporary
-   *   Is this a mysql temporary table or temporary in a less technical sense.
    *
    * @return string
    */
-  public function createTemporaryTable($identifier, $sql, $isTrueTemporary = TRUE) {
+  public function createTemporaryTable($identifier, $sql) {
+    $tempTable = CRM_Utils_SQL_TempTable::build()->setUtf8(TRUE)->createWithQuery($sql);
+    $name = $tempTable->getName();
+    // Developers may force tables to be durable to assist in debugging so lets check.
+    $isNotTrueTemporary = $tempTable->isDurable();
+    // The TempTable build routine adds the next line - we output it to help developers see what has happened.
+    $sql = 'CREATE ' . ($isNotTrueTemporary ? '' : 'TEMPORARY ') . "TABLE $name DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci " . $sql;
     $this->addToDeveloperTab($sql);
-    $name = CRM_Utils_SQL_TempTable::build()->setUtf8(TRUE)->setDurable($isTrueTemporary)->createWithQuery($sql)->getName();
-    $this->temporaryTables[$identifier] = ['temporary' => $isTrueTemporary, 'name' => $name];
+    $this->temporaryTables[$identifier] = ['temporary' => !$isNotTrueTemporary, 'name' => $name];
     return $name;
   }
 
@@ -3692,11 +3693,7 @@ WHERE cg.extends IN ('" . implode("','", $this->_customGroupExtends) . "') AND
         WHERE smartgroup_contact.group_id IN ({$smartGroups}) ";
     }
 
-    $this->groupTempTable = CRM_Utils_SQL_TempTable::build()->setCategory('rptgrp')->setId(date('Ymd_') . uniqid())->getName();
-    $this->executeReportQuery("
-      CREATE TEMPORARY TABLE $this->groupTempTable $this->_databaseAttributes
-      $query
-    ");
+    $this->groupTempTable = $this->createTemporaryTable('rptgrp', $query);
     CRM_Core_DAO::executeQuery("ALTER TABLE $this->groupTempTable ADD INDEX i_id(id)");
   }
 
index 8e4c25d1e3ddc6af814d04007b890892837b074b..7f64c03e58d6787ae6e26e2a143142062595cbea 100644 (file)
@@ -211,7 +211,7 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form {
             'title' => ts('Financial Account Owner - Debit'),
             'operatorType' => CRM_Report_Form::OP_SELECT,
             'type' => CRM_Utils_Type::T_INT,
-            'options' => array('' => '- Select Organization -') + CRM_Financial_BAO_FinancialAccount::getOrganizationNames(),
+            'options' => array('' => '- Select Organization -') + CRM_Financial_BAO_FinancialAccount::getOrganizationNames(FALSE),
             'name' => 'contact_id',
             'alias' => 'financial_account_civireport_debit',
           ),
@@ -227,7 +227,7 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form {
             'title' => ts('Financial Account Owner - Credit'),
             'operatorType' => CRM_Report_Form::OP_SELECT,
             'type' => CRM_Utils_Type::T_INT,
-            'options' => array('' => '- Select Organization -') + CRM_Financial_BAO_FinancialAccount::getOrganizationNames(),
+            'options' => array('' => '- Select Organization -') + CRM_Financial_BAO_FinancialAccount::getOrganizationNames(FALSE),
             'name' => 'contact_id',
             'alias' => 'financial_account_civireport_credit',
           ),
index e5aeb0fcba2f95406d6660517d0b62887b23f10b..a7aeaee9033aa2da56cff9e6d0747dcec63a5c87 100644 (file)
@@ -567,13 +567,12 @@ class CRM_Report_Form_Contribute_Lybunt extends CRM_Report_Form {
     // @todo this acl has no test coverage and is very hard to test manually so could be fragile.
     $this->resetFormSqlAndWhereHavingClauses();
 
-    $this->contactTempTable = CRM_Utils_SQL_TempTable::build()->setCategory('rptlybunt')->setId(date('Ymd_') . uniqid())->getName();
+    $this->contactTempTable = $this->createTemporaryTable('rptlybunt', "
+      SELECT SQL_CALC_FOUND_ROWS {$this->_aliases['civicrm_contact']}.id as cid {$this->_from}
+      {$this->_where}
+      GROUP BY {$this->_aliases['civicrm_contact']}.id"
+    );
     $this->limit();
-    $getContacts = "
-      CREATE TEMPORARY TABLE $this->contactTempTable {$this->_databaseAttributes}
-      SELECT SQL_CALC_FOUND_ROWS {$this->_aliases['civicrm_contact']}.id as cid {$this->_from} {$this->_where}
-      GROUP BY {$this->_aliases['civicrm_contact']}.id";
-    $this->executeReportQuery($getContacts);
     if (empty($this->_params['charts'])) {
       $this->setPager();
     }
index 9fcb8341766d4ee2fdef0e26e4735edbc17d14ee..77cca1586dfa84db180c795ebd7905dead29b681 100644 (file)
  *
  * @package CRM
  * @copyright CiviCRM LLC (c) 2004-2018
- * $Id$
- *
  */
 
 /**
- *  This class is for UF Group
+ *  This class is for UF Group (Profile) configuration.
  */
 class CRM_UF_Form_Group extends CRM_Core_Form {
 
+  use CRM_Core_Form_EntityFormTrait;
+
+  /**
+   * Fields for the entity to be assigned to the template.
+   *
+   * Fields may have keys
+   *  - name (required to show in tpl from the array)
+   *  - description (optional, will appear below the field)
+   *  - not-auto-addable - this class will not attempt to add the field using addField.
+   *    (this will be automatically set if the field does not have html in it's metadata
+   *    or is not a core field on the form's entity).
+   *  - help (option) add help to the field - e.g ['id' => 'id-source', 'file' => 'CRM/Contact/Form/Contact']]
+   *  - template - use a field specific template to render this field
+   *  - required
+   *  - is_freeze (field should be frozen).
+   *
+   * @var array
+   */
+  protected $entityFields = [];
+
+  /**
+   * Set entity fields to be assigned to the form.
+   */
+  protected function setEntityFields() {
+    $this->entityFields = [
+      'title' => ['name' => 'title'],
+      'frontend_title' => ['name' => 'frontend_title'],
+      'description' => ['name' => 'description', 'help' => ['id' => 'id-description', 'file' => 'CRM/UF/Form/Group.hlp']],
+      'uf_group_type' => ['name' => 'uf_group_type', 'not-auto-addable' => TRUE, 'help' => ['id' => 'id-used_for', 'file' => 'CRM/UF/Form/Group.hlp'], 'post_html_text' => ' ' . $this->getOtherModuleString()]
+    ];
+  }
+
+  /**
+   * Explicitly declare the entity api name.
+   */
+  public function getDefaultEntity() {
+    return 'UFGroup';
+  }
+
   /**
    * The form id saved to the session for an update.
    *
@@ -57,8 +94,6 @@ class CRM_UF_Form_Group extends CRM_Core_Form {
 
   /**
    * Set variables up before form is built.
-   *
-   * @return void
    */
   public function preProcess() {
     // current form id
@@ -111,6 +146,7 @@ class CRM_UF_Form_Group extends CRM_Core_Form {
    * @return void
    */
   public function buildQuickForm() {
+    self::buildQuickEntityForm();
     if ($this->_action & (CRM_Core_Action::DISABLE | CRM_Core_Action::DELETE)) {
       if ($this->_action & (CRM_Core_Action::DISABLE)) {
         $display = 'Disable Profile';
@@ -132,12 +168,6 @@ class CRM_UF_Form_Group extends CRM_Core_Form {
       ));
       return;
     }
-    $this->applyFilter('__ALL__', 'trim');
-
-    // title
-    $this->add('text', 'title', ts('Profile Name'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_UFGroup', 'title'), TRUE);
-    $this->add('text', 'frontend_title', ts('Public Title'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_UFGroup', 'frontend_title'));
-    $this->add('textarea', 'description', ts('Description'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_UFGroup', 'description'));
 
     //add checkboxes
     $uf_group_type = array();
@@ -237,17 +267,6 @@ class CRM_UF_Form_Group extends CRM_Core_Form {
       }
       $defaults['uf_group_type'] = isset($checked) ? $checked : "";
 
-      //get the uf join records for current uf group other than default modules
-      $otherModules = array();
-      $otherModules = CRM_Core_BAO_UFGroup::getUFJoinRecord($this->_id, TRUE, TRUE);
-      if (!empty($otherModules)) {
-        $otherModuleString = NULL;
-        foreach ($otherModules as $key) {
-          $otherModuleString .= " [ x ] <label>" . $key . "</label>";
-        }
-        $this->assign('otherModuleString', $otherModuleString);
-      }
-
       $showAdvanced = 0;
       $advFields = array(
         'group',
@@ -335,7 +354,7 @@ class CRM_UF_Form_Group extends CRM_Core_Form {
     }
     else {
       // get the submitted form values.
-      $params = $ids = array();
+      $ids = array();
       $params = $this->controller->exportValues($this->_name);
 
       if (!array_key_exists('is_active', $params)) {
@@ -386,4 +405,27 @@ class CRM_UF_Form_Group extends CRM_Core_Form {
     CRM_Utils_System::updateCategories();
   }
 
+  /**
+   * Set the delete message.
+   *
+   * We do this from the constructor in order to do a translation.
+   */
+  public function setDeleteMessage() {}
+
+  /**
+   * Get the string to display next to the used for field indicating unchangeable uses.
+   *
+   * @return string
+   */
+  protected function getOtherModuleString() {
+    $otherModules = CRM_Core_BAO_UFGroup::getUFJoinRecord($this->_id, TRUE, TRUE);
+    if (!empty($otherModules)) {
+      $otherModuleString = NULL;
+      foreach ($otherModules as $key) {
+        $otherModuleString .= " [ x ] <label>" . $key . "</label>";
+      }
+    }
+    return $otherModuleString;
+  }
+
 }
diff --git a/CRM/Upgrade/Incremental/php/FiveNine.php b/CRM/Upgrade/Incremental/php/FiveNine.php
new file mode 100644 (file)
index 0000000..53a0069
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 5                                                  |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2017                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007.                                       |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License along with this program; if not, contact CiviCRM LLC       |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Upgrade logic for FiveNine */
+class CRM_Upgrade_Incremental_php_FiveNine extends CRM_Upgrade_Incremental_Base {
+
+  /**
+   * Compute any messages which should be displayed beforeupgrade.
+   *
+   * Note: This function is called iteratively for each upcoming
+   * revision to the database.
+   *
+   * @param string $preUpgradeMessage
+   * @param string $rev
+   *   a version number, e.g. '4.4.alpha1', '4.4.beta3', '4.4.0'.
+   * @param null $currentVer
+   */
+  public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) {
+    // Example: Generate a pre-upgrade message.
+    // if ($rev == '5.12.34') {
+    //   $preUpgradeMessage .= '<p>' . ts('A new permission, "%1", has been added. This permission is now used to control access to the Manage Tags screen.', array(1 => ts('manage tags'))) . '</p>';
+    // }
+  }
+
+  /**
+   * Compute any messages which should be displayed after upgrade.
+   *
+   * @param string $postUpgradeMessage
+   *   alterable.
+   * @param string $rev
+   *   an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs.
+   */
+  public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) {
+    if ($rev == '5.9.0') {
+      $args = array(
+        1 => ts('Enable multiple bulk email address for a contact'),
+        2 => ts('Email on Hold'),
+      );
+      $postUpgradeMessage .= '<p>' . ts('If the setting "%1" is enabled, you should update any smart groups based on the "%2" field.', $args) . '</p>';
+    }
+
+    // Example: Generate a post-upgrade message.
+    // if ($rev == '5.12.34') {
+    //   $postUpgradeMessage .= '<br /><br />' . ts("By default, CiviCRM now disables the ability to import directly from SQL. To use this feature, you must explicitly grant permission 'import SQL datasource'.");
+    // }
+  }
+
+  /*
+   * Important! All upgrade functions MUST add a 'runSql' task.
+   * Uncomment and use the following template for a new upgrade version
+   * (change the x in the function name):
+   */
+
+  //  /**
+  //   * Upgrade function.
+  //   *
+  //   * @param string $rev
+  //   */
+  //  public function upgrade_5_0_x($rev) {
+  //    $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
+  //    $this->addTask('Do the foo change', 'taskFoo', ...);
+  //    // Additional tasks here...
+  //    // Note: do not use ts() in the addTask description because it adds unnecessary strings to transifex.
+  //    // The above is an exception because 'Upgrade DB to %1: SQL' is generic & reusable.
+  //  }
+
+  // public static function taskFoo(CRM_Queue_TaskContext $ctx, ...) {
+  //   return TRUE;
+  // }
+
+}
diff --git a/CRM/Upgrade/Incremental/sql/5.7.0.mysql.tpl b/CRM/Upgrade/Incremental/sql/5.7.0.mysql.tpl
new file mode 100644 (file)
index 0000000..55f4e8c
--- /dev/null
@@ -0,0 +1 @@
+{* file to handle db changes in 5.7.0 during upgrade *}
diff --git a/CRM/Upgrade/Incremental/sql/5.9.0.mysql.tpl b/CRM/Upgrade/Incremental/sql/5.9.0.mysql.tpl
new file mode 100644 (file)
index 0000000..c290852
--- /dev/null
@@ -0,0 +1 @@
+{* file to handle db changes in 5.9.0 during upgrade *}
diff --git a/CRM/Upgrade/Incremental/sql/5.9.alpha1.mysql.tpl b/CRM/Upgrade/Incremental/sql/5.9.alpha1.mysql.tpl
new file mode 100644 (file)
index 0000000..fc92395
--- /dev/null
@@ -0,0 +1 @@
+{* file to handle db changes in 5.9.alpha1 during upgrade *}
index b2c843326e5ccccad58f8c67767e77af071d4b88..7d427b2b51019df894a637e090cdca08f5f3894a 100644 (file)
@@ -63,6 +63,11 @@ abstract class CRM_Utils_API_AbstractFieldCoder implements API_Wrapper {
     if ($skipFields === NULL) {
       return FALSE;
     }
+    // Strip extra numbers from custom fields e.g. custom_32_1 should be custom_32
+    if (strpos($fldName, 'custom_') === 0) {
+      list($fldName, $customId) = explode('_', $fldName);
+      $fldName .= '_' . $customId;
+    }
 
     // Field should be skipped
     if (in_array($fldName, $skipFields)) {
index 0c44427fe6c03eb1676d53164bd6e433fdf53c4b..e39193411b91d877f0ec6dd9042a1be9fbb44e48 100644 (file)
@@ -572,7 +572,7 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component {
         ts('Your extensions directory (%1) is read-only. If you would like to perform downloads or upgrades, then change the file permissions.',
           array(1 => $basedir)),
         ts('Read-Only Extensions'),
-        \Psr\Log\LogLevel::WARNING,
+        \Psr\Log\LogLevel::NOTICE,
         'fa-plug'
       );
     }
index b2316eca75d9fa49aaf00f324dea6df895545ac5..4eb750818934635381bd470fdaa39beb48b0e6bf 100644 (file)
@@ -438,6 +438,23 @@ class CRM_Utils_Date {
     }
   }
 
+  /**
+   * Wrapper for customFormat that takes a timestamp
+   *
+   * @param int $timestamp
+   *   Date and time in timestamp format.
+   * @param string $format
+   *   The output format.
+   * @param array $dateParts
+   *   An array with the desired date parts.
+   *
+   * @return string
+   *   the $format-formatted $date
+   */
+  public static function customFormatTs($timestamp, $format = NULL, $dateParts = NULL) {
+    return CRM_Utils_Date::customFormat(date("Y-m-d H:i:s", $timestamp), $format, $dateParts);
+  }
+
   /**
    * Converts the date/datetime from MySQL format to ISO format
    *
index cedc46c816221adf3824df21a3298825108df015..1a2e2faf365850bc85695a09da56356a52c66f97 100644 (file)
@@ -233,6 +233,7 @@ class CRM_Utils_SQL_TempTable {
 
   /**
    * @param string|NULL $category
+   *
    * @return CRM_Utils_SQL_TempTable
    */
   public function setCategory($category) {
@@ -244,7 +245,12 @@ class CRM_Utils_SQL_TempTable {
   }
 
   /**
-   * @parma bool $value
+   * Set whether the table should be durable.
+   *
+   * Durable tables are not TEMPORARY in the mysql sense.
+   *
+   * @param bool $durable
+   *
    * @return CRM_Utils_SQL_TempTable
    */
   public function setDurable($durable = TRUE) {
@@ -253,7 +259,10 @@ class CRM_Utils_SQL_TempTable {
   }
 
   /**
+   * Setter for id
+   *
    * @param mixed $id
+   *
    * @return CRM_Utils_SQL_TempTable
    */
   public function setId($id) {
@@ -264,6 +273,15 @@ class CRM_Utils_SQL_TempTable {
     return $this;
   }
 
+  /**
+   * Set table collation to UTF8.
+   *
+   * This would make sense as a default but cautiousness during phasing in has made it opt-in.
+   *
+   * @param bool $value
+   *
+   * @return $this
+   */
   public function setUtf8($value = TRUE) {
     $this->utf8 = $value;
     return $this;
index 9ca66a67c1c1112d18a481df474ef86cb0f1a758..92290febc70db9e441eafdf3c77a169ccb7b0aad 100644 (file)
@@ -230,6 +230,14 @@ class Container {
       []
     ));
 
+    $container->setDefinition('prevnext.driver.redis', new Definition(
+      'CRM_Core_PrevNextCache_Redis',
+      [new Reference('cache_config')]
+    ));
+
+    $container->setDefinition('cache_config', new Definition('ArrayObject'))
+      ->setFactory(array(new Reference(self::SELF), 'createCacheConfig'));
+
     $container->setDefinition('civi.mailing.triggers', new Definition(
       'Civi\Core\SqlTrigger\TimestampTriggers',
       array('civicrm_mailing', 'Mailing')
@@ -433,11 +441,26 @@ class Container {
    * @return \CRM_Core_PrevNextCache_Interface
    */
   public static function createPrevNextCache($container) {
-    $cacheDriver = \CRM_Utils_Cache::getCacheDriver();
-    $service = 'prevnext.driver.' . strtolower($cacheDriver);
-    return $container->has($service)
-      ? $container->get($service)
-      : $container->get('prevnext.driver.sql');
+    $setting = \Civi::settings()->get('prevNextBackend');
+    if ($setting === 'default') {
+      // For initial release (5.8.x), continue defaulting to SQL.
+      $isTransitional = version_compare(\CRM_Utils_System::version(), '5.9.alpha1', '<');
+      $cacheDriver = \CRM_Utils_Cache::getCacheDriver();
+      $service = 'prevnext.driver.' . strtolower($cacheDriver);
+      return $container->has($service) && !$isTransitional
+        ? $container->get($service)
+        : $container->get('prevnext.driver.sql');
+    }
+    else {
+      return $container->get('prevnext.driver.' . $setting);
+    }
+  }
+
+  public static function createCacheConfig() {
+    $driver = \CRM_Utils_Cache::getCacheDriver();
+    $settings = \CRM_Utils_Cache::getCacheSettings($driver);
+    $settings['driver'] = $driver;
+    return new \ArrayObject($settings);
   }
 
   /**
index d73a972bb037e781d529666b34f6857ec122d501..1e4306d32b165fcf225b7fd1ac53ed751c4bcb52 100644 (file)
@@ -7,5 +7,8 @@ return array(
   'ext' => 'civicrm',
   'js' => array('ang/crmUi.js'),
   'partials' => array('ang/crmUi'),
-  'requires' => array('crmResource'),
+  'requires' => array(
+    'crmResource',
+    'ui.utils',
+  ),
 );
index 70da3871c882862378f826be1ea53fd9dc4a95dc..216640ce90bb725636f70ea66b07fc7a80e97194 100644 (file)
         link: function (scope, element, attrs) {
           scope.ts = CRM.ts(null);
 
-          element.find('.crm-wizard-buttons button').click(function () {
+          element.find('.crm-wizard-buttons button[ng-click^=crmUiWizardCtrl]').click(function () {
             // These values are captured inside the click handler to ensure the
             // positions/sizes of the elements are captured at the time of the
             // click vs. at the time this directive is initialized.
index cc468c0a1bd8ae4a880c4759a52fce10d3075cda..81a433b27a641d4a97c29cc429c680f00aa53a93 100644 (file)
@@ -341,6 +341,10 @@ function civicrm_api3_activity_get($params) {
   }
 
   $activities = _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params, FALSE, 'Activity', $sql);
+  if ($options['is_count']) {
+    return civicrm_api3_create_success($activities, $params, 'Activity', 'get');
+  }
+
   if (!empty($params['check_permissions']) && !CRM_Core_Permission::check('view all activities')) {
     // @todo get this to work at the query level - see contact_id join above.
     foreach ($activities as $activity) {
@@ -349,9 +353,6 @@ function civicrm_api3_activity_get($params) {
       }
     }
   }
-  if ($options['is_count']) {
-    return civicrm_api3_create_success($activities, $params, 'Activity', 'get');
-  }
 
   $activities = _civicrm_api3_activity_get_formatResult($params, $activities, $options);
   //legacy custom data get - so previous formatted response is still returned too
index 0d641ad8641f8ececec66b2ea42e1626ff0b8a25..36ed63cfee7c0d061f71c8720b1c26acb7154317 100644 (file)
@@ -434,7 +434,8 @@ function civicrm_api3_contribution_transact($params) {
   $paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($params['payment_processor'], $params['payment_processor_mode']);
   $paymentProcessor['object']->doPayment($params);
 
-  $params['payment_instrument_id'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessorType', $paymentProcessor['payment_processor_type_id'], 'payment_type') == 1 ? 'Credit Card' : 'Debit Card';
+  $params['payment_instrument_id'] = $paymentProcessor['object']->getPaymentInstrumentID();
+
   return civicrm_api('Contribution', 'create', $params);
 }
 
index 22a26becab367ba7ca71c1beadc1fb584fad4aa5..be126ae5afbd3898956ca1d76b927765bfd13618 100644 (file)
@@ -188,6 +188,14 @@ function civicrm_api3_profile_submit($params) {
     }
   }
 
+  // Add custom greeting fields
+  $greetingFields = ['email_greeting', 'postal_greeting', 'addressee'];
+  foreach ($greetingFields as $greetingField) {
+    if (isset($profileFields[$greetingField]) && !isset($profileFields["{$greetingField}_custom"])) {
+      $profileFields["{$greetingField}_custom"] = ['name' => "{$greetingField}_custom"];
+    }
+  }
+
   foreach ($profileFields as $fieldName => $field) {
     if (!isset($params[$fieldName])) {
       continue;
index effedd84d2cb5234d3aad2427d07e8dd74e007e6..35f647cdd5102402d2659d34d568a05ef9aa14f6 100644 (file)
@@ -189,7 +189,7 @@ return array(
     'title' => ts('Enable multiple bulk email address for a contact.'),
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => ts('CiviMail will deliver a copy of the email to each bulk email listed for the contact.'),
+    'description' => ts('CiviMail will deliver a copy of the email to each bulk email listed for the contact. Enabling this setting will also change the options for the "Email on Hold" field in Advanced Search.'),
     'help_text' => NULL,
   ),
   'include_message_id' => array(
index ab784705b9fff11ee1a057c7e62674971c6a4c68..7750f2967999edaf967ba5ce8e089b32e391117f 100644 (file)
@@ -197,6 +197,27 @@ return array(
     'description' => 'If set, this will be the default profile used for contact search.',
     'help_text' => NULL,
   ),
+  'prevNextBackend' => array(
+    'group_name' => 'Search Preferences',
+    'group' => 'Search Preferences',
+    'name' => 'prevNextBackend',
+    'type' => 'String',
+    'quick_form_type' => 'Select',
+    'html_type' => 'Select',
+    'html_attributes' => array(
+      //'class' => 'crm-select2',
+    ),
+    'default' => 'default',
+    'add' => '5.9',
+    'title' => 'PrevNext Cache',
+    'is_domain' => 1,
+    'is_contact' => 0,
+    'pseudoconstant' => array(
+      'callback' => 'CRM_Core_BAO_PrevNextCache::getPrevNextBackends',
+    ),
+    'description' => 'When performing a search, how should the search-results be cached?',
+    'help_text' => '',
+  ),
   'searchPrimaryDetailsOnly' => array(
     'group_name' => 'Search Preferences',
     'group' => 'Search Preferences',
index c20b65b9e6df578cb7952e903065d1b67640454c..b0c0a2efa6a95363de8ce228c4e51aa63bb8b4cf 100644 (file)
@@ -399,7 +399,7 @@ UNLOCK TABLES;
 
 LOCK TABLES `civicrm_domain` WRITE;
 /*!40000 ALTER TABLE `civicrm_domain` DISABLE KEYS */;
-INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `config_backend`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,NULL,'5.8.beta1',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
+INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `config_backend`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,NULL,'5.9.alpha1',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
 /*!40000 ALTER TABLE `civicrm_domain` ENABLE KEYS */;
 UNLOCK TABLES;
 
index 617aea8c55089783643b23132908484623cc8212..37791e877ce32672a54d1ffe8c88775b574d46a1 100644 (file)
@@ -39,7 +39,7 @@
 {if $action eq 8}
    <div class="messages status no-popup">
        <div class="icon inform-icon"></div>
-       {ts}Do you want to delete this message template?{/ts}
+       {ts 1=$msg_title|escape}Do you want to delete the message template '%1'?{/ts}
    </div>
 {else}
         <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="top"}</div>
index fc67bd80e0b007bd3597d595d6c047615d695895..e0e2bf2cfbe18e525fa479d8463911b36f502acf 100644 (file)
           var params = {
             title: data.count == 1 ? {/literal}"{ts escape='js'}Similar Contact Found{/ts}" : "{ts escape='js'}Similar Contacts Found{/ts}"{literal},
             info: "{/literal}{ts escape='js'}If the contact you were trying to add is listed below, click their name to view or edit their record{/ts}{literal}:",
-            contacts: data.values
+            contacts: data.values,
+            cid: cid
           };
           if (data.count) {
             openDupeAlert(params);
           info: data.count ?
             "{/literal}{ts escape='js'}If the contact you were trying to add is listed below, click their name to view or edit their record{/ts}{literal}:" :
             "{/literal}{ts escape='js'}No matches found using the default Supervised deduping rule.{/ts}{literal}",
-          contacts: data.values
+          contacts: data.values,
+          cid: cid
         };
         updateDupeAlert(params, data.count ? 'alert' : 'success');
       });
           <%- contact.display_name %>
         </a>
         <%- contact.email %>
+        <% if (cid) { %>
+          <% var params = {reset: 1, action: 'update', oid: contact.id > cid ? contact.id : cid, cid: contact.id > cid ? cid : contact.id }; %>
+          (<a href="<%= CRM.url('civicrm/contact/merge', params) %>">{/literal}{ts}Merge{/ts}{literal}</a>)
+        <% } %>
       </li>
     <% }); %>
   </ul>
index 3e94b37b2d427ee633bdd6e285c87ad3cb40b10f..3edd73cb3b22e08607a4e7714ea3104d11bc525b 100644 (file)
@@ -3,7 +3,13 @@
 {$form.preferred_communication_method.html}
 <br/>
 
-{if $form.email_on_hold}
+{if $form.email_on_hold.type == 'select'}
+  <br/>
+  {$form.email_on_hold.label}
+  <br/>
+  {$form.email_on_hold.html}
+  <br/>
+{elseif $form.email_on_hold.type == 'checkbox'}
   <div class="spacer"></div>
   {$form.email_on_hold.html}
   {$form.email_on_hold.label}
index b1d9eb76b30269b013c0c1178c140d74e9d37e69..0406fec8753d6d162ed334f6308058020faa8a18 100644 (file)
@@ -48,8 +48,6 @@
             <div class="crm-content crm-custom-data crm-contact-reference">
               <a href="{crmURL p='civicrm/contact/view' q="reset=1&cid=`$element.contact_ref_id`"}" title="view contact">{$element.field_value}</a>
             </div>
-          {elseif $element.field_data_type EQ 'Memo'}
-            <div class="crm-content crm-custom-data">{$element.field_value|nl2br}</div>
           {elseif $element.field_data_type EQ 'Money'}
             <div class="crm-content crm-custom-data">{$element.field_value|crmMoney}</div>
           {else}
index 8d580a2ea59590974eacef4e5a2d4afe02826f73..8adea18fd02afd973052b5b2140d723ff75c5b94 100644 (file)
     </div>
     {/if}
 
-    {if $onbehalfProfile|@count}
+    {if $onbehalfProfile && $onbehalfProfile|@count}
       <div class="crm-group onBehalf_display-group label-left crm-profile-view">
          {include file="CRM/UF/Form/Block.tpl" fields=$onbehalfProfile prefix='onbehalf'}
       </div>
     {/if}
 
-    {if $honoreeProfileFields|@count}
+    {if $honoreeProfileFields && $honoreeProfileFields|@count}
         <div class="crm-group honor_block-group">
             <div class="header-dark">
                 {$soft_credit_type}
index 52b102ed8b537b6fb332d3aa0e7051fc5977bcb8..db82a07df4ee8111e0307dedad3bed29f72ca5d4 100644 (file)
       {include file="CRM/Contribute/Form/Contribution/PremiumBlock.tpl" context="makeContribution"}
     </div>
 
-    {if $honoreeProfileFields|@count}
+    {if $honoreeProfileFields && $honoreeProfileFields|@count}
       <fieldset class="crm-public-form-item crm-group honor_block-group">
         {crmRegion name="contribution-soft-credit-block"}
           <legend>{$honor_block_title}</legend>
index 4757a3131e3d9e513337ae09945d32fcb20963f8..983a6a8e77b6541d4870cf72c1faa713363bd34b 100644 (file)
@@ -41,7 +41,7 @@
 
 <div class="crm-public-form-item" id="on-behalf-block">
   {crmRegion name="onbehalf-block"}
-    {if $onBehalfOfFields|@count}
+    {if $onBehalfOfFields && $onBehalfOfFields|@count}
       <fieldset>
       <legend>{$fieldSetTitle}</legend>
       {if $form.org_option}
index 8520b458b5398ca894d23a676fcb8a7ea72c55ee..c696adad68b284f3bda8eaa329265d1f0ecef99a 100644 (file)
@@ -63,7 +63,7 @@
         </div>
       {/if}
     {elseif $isPendingOutcome}
-      <div>{ts 1=$paymentProcessor.name}Your contribution has been submitted to %1 for processing. Please print this page for your records.{/ts}</div>
+      <div>{ts 1=$paymentProcessor.name}Your contribution has been submitted to %1 for processing.{/ts}</div>
         {if $is_email_receipt}
       <div>
         {if $onBehalfEmail AND ($onBehalfEmail neq $email)}
@@ -74,7 +74,7 @@
       </div>
     {/if}
   {else}
-    <div>{ts}Your transaction has been processed successfully. Please print this page for your records.{/ts}</div>
+    <div>{ts}Your transaction has been processed successfully.{/ts}</div>
       {if $is_email_receipt}
         <div>
           {if $onBehalfEmail AND ($onBehalfEmail neq $email)}
     </div>
   {/if}
 
-  {if $onbehalfProfile|@count}
+  {if $onbehalfProfile && $onbehalfProfile|@count}
     <div class="crm-group onBehalf_display-group label-left crm-profile-view">
       {include file="CRM/UF/Form/Block.tpl" fields=$onbehalfProfile prefix='onbehalf'}
      </div>
   {/if}
 
-  {if $honoreeProfileFields|@count}
+  {if $honoreeProfileFields && $honoreeProfileFields|@count}
     <div class="crm-group honor_block-group">
       <div class="header-dark">
         {$soft_credit_type}
index bc281ac35e410fc18f8accf25590730ac9ee6cfc..6e966db510581942e5e7ff21ee52176a5fe776a6 100644 (file)
   {/if}
 </table>
 
-{if count($softContributions)} {* We show soft credit name with PCP section if contribution is linked to a PCP. *}
+{if $softContributions && count($softContributions)} {* We show soft credit name with PCP section if contribution is linked to a PCP. *}
   <div class="crm-accordion-wrapper crm-soft-credit-pane">
     <div class="crm-accordion-header">
       {ts}Soft Credit{/ts}
index 4412dac0a4cf33a8fa7b3df03770a8c517c2cbd9..add2f855631e8ee361bb43816de019a414085235 100644 (file)
     {include file="CRM/Contribute/Form/PCP.js.tpl"}
   </td>
   <td>
-    {$form.contribution_cancel_reason.label}<br />
-    {$form.contribution_cancel_reason.html}
+    {$form.cancel_reason.label}<br />
+    {$form.cancel_reason.html}
   </td>
 </tr>
 <tr>
index fb6a01eee9a066a0e938dfb1c04fb2f2f15085e7..5aa672db6c4456c991fde9a99c780129afa5f131 100644 (file)
@@ -33,7 +33,7 @@
     {/capture}{help id=$help.id file=$help.file}{/if}
     {if $action == 2 && $fieldSpec.is_add_translate_dialog}{include file='CRM/Core/I18n/Dialog.tpl' table=$entityTable field=$fieldName id=$entityID}{/if}
   </td>
-  <td>{if $form.$fieldName.html}{if $fieldSpec.formatter === 'crmMoney'}{$form.$fieldName.html|crmMoney}{else}{$form.$fieldName.html}{/if}{else}{$fieldSpec.place_holder}{/if}<br />
+  <td>{$fieldSpec.pre_html_text}{if $form.$fieldName.html}{if $fieldSpec.formatter === 'crmMoney'}{$form.$fieldName.html|crmMoney}{else}{$form.$fieldName.html}{/if}{else}{$fieldSpec.place_holder}{/if}{$fieldSpec.post_html_text}<br />
     {if $fieldSpec.description}<span class="description">{$fieldSpec.description}</span>{/if}
     {if $fieldSpec.documentation_link}{docURL page=$fieldSpec.documentation_link.page resource=$fieldSpec.documentation_link.resource}{/if}
   </td>
index dfefb47c58ee8f4aefd4ef033c84ee77f8111e42..b6f13b6072bfad76de051c0aca0ae67272762e3f 100644 (file)
@@ -23,7 +23,7 @@
  | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
  +--------------------------------------------------------------------+
 *}
-{if $config->languageLimit|@count >= 2 and $translatePermission }
+{if $config->languageLimit && $config->languageLimit|@count >= 2 and $translatePermission }
   <a href="{crmURL p='civicrm/i18n' q="reset=1&table=$table&field=$field&id=$id"}" data-field="{$field}" class="crm-hover-button crm-multilingual-edit-button" title="{ts}Languages{/ts}">
     <i class="crm-i fa-language fa-lg"></i>
   </a>
index e26cf0569470077626d64c0f666580c5580b4c21..3bf771bbf159d8cf38631b81f35ec284be97ae06 100644 (file)
@@ -77,7 +77,7 @@
 <p>{if $params.waitlist}
   {ts}You may allow users to join a waitlist when the event is full (by checking the box below).{/ts}
 {else}
-  {ts}You may allow users to join a waitlist when the event is full. To enable this feature you must first enable the Participant Statuses used by the waitlist work-flow (click the pencil icon, or navigate to Administer » CiviEvent » Participant Statuses). Then reload this form and check 'Offer a Waitlist?'.{/ts}
+  {ts}You may allow users to join a waitlist when the event is full. To enable this feature you must first enable the Participant Statuses used by the waitlist work-flow (click the wrench icon, or navigate to Administer » CiviEvent » Participant Statuses). Then reload this form and check 'Offer a Waitlist?'.{/ts}
 {/if}</p>
 <p>{ts}Otherwise, the registration link is hidden and the &quot;Event Full Message&quot' is displayed when the maximum number of registrations is reached. Only participants with status types marked as 'counted' are included when checking if the event is full.{/ts}</p>
 {/htxt}
index 1d530dca608c50bb3b085aa2700aabca8dd047b1..5a79dc57df233a3f00063a549dd063945f1f4ad7 100644 (file)
@@ -60,7 +60,7 @@
   {ts}Multiple Participants{/ts}
 {/htxt}
 {htxt id="id-allow_multiple"}
-<p>{ts}Check this box to allow users to register themselves AND additional participants for an event. When this feature is enabled, users have the option to specify the number of additional participants they are registering for. If this is a paid event, they can select a different event fees for each participant - and will be charged the total of those fees. If a profile is included - they will complete the profile information for each participant.{/ts}</p>
+<p>{ts}Check this box to allow users to register themselves AND additional participants for an event. When this feature is enabled, users have the option to specify the number of additional participants they are registering for. If this is a paid event, they can select a different event fee for each participant and will be charged the total of those fees. If a profile is included, they will complete the profile information for each participant.{/ts}</p>
 <p>{ts}You can use different profile for the person who is registering than for "Additional Participants". For example, you may want to require an email address from the person entering the registration while not requiring (or even requesting) emails for additional participants (i.e. their "guests").{/ts}</p>
 {/htxt}
 {htxt id="id-max_additional-title"}
index af3cb1955785d7097cff06abfe0fdb54fdc9b61c..811a6c92705b0f933ce79644d951479873b740fd 100644 (file)
             {/if}
         {* PayPal_Standard sets contribution_mode to 'notify'. We don't know if transaction is successful until we receive the IPN (payment notification) *}
         {elseif $contributeMode EQ 'notify' and $paidEvent}
-            <p>{ts 1=$paymentProcessor.name}Your registration payment has been submitted to %1 for processing. Please print this page for your records.{/ts}</p>
+            <p>{ts 1=$paymentProcessor.name}Your registration payment has been submitted to %1 for processing.{/ts}</p>
             {if $is_email_confirm}
                 <p>{ts 1=$email}A registration confirmation email will be sent to %1 once the transaction is processed successfully.{/ts}</p>
             {/if}
         {else}
-            <p>{ts}Your registration has been processed successfully. Please print this page for your records.{/ts}</p>
+            <p>{ts}Your registration has been processed successfully.{/ts}</p>
             {if $is_email_confirm}
                 <p>{ts 1=$email}A registration confirmation email has also been sent to %1{/ts}</p>
             {/if}
index d0a0fb6b6328c0e1643370819c1ab089b1118210..8ded5d96a529fdfe42b3621c0cc4ce3229b63e56 100644 (file)
@@ -40,7 +40,7 @@
                 </tr>
                 {counter start=0 skip=1 print=false}
                 {foreach from=$event_rows item=row}
-                    <tr id='rowid{$row.participant_id}' class=" crm-event-participant-id_{$row.participant_id} {cycle values="odd-row,even-row"}{if $row.status eq Cancelled} disabled{/if}">
+                    <tr id='rowid{$row.participant_id}' class=" crm-event-participant-id_{$row.participant_id} {cycle values="odd-row,even-row"}{if $row.status eq 'Cancelled'} disabled{/if}">
                        <td class="crm-participant-event-id_{$row.event_id}"><a href="{crmURL p='civicrm/event/info' q="reset=1&id=`$row.event_id`&context=dashboard"}">{$row.event_title}</a></td>
                        <td class="crm-participant-event_start_date">
                             {$row.event_start_date|crmDate}
@@ -73,4 +73,4 @@
     {/if}
 </div>
 {crmRegion name="crm-event-userdashboard-post"}
-{/crmRegion}
\ No newline at end of file
+{/crmRegion}
index 3b56964fe3091eaf98a021f0405d49fa16cc8d54..1dbf86d8530fa4cac1b4775a14dbcdf5d000030e 100644 (file)
@@ -30,7 +30,7 @@
     <tr class="columnheader-dark">
       <th scope="col" rowspan="2">{ts}Members by Type{/ts}</th>
         {if $preMonth}
-      <th scope="col" colspan="3">{$premonth} &ndash; {ts}(Last Month){/ts}</th>
+      <th scope="col" colspan="3">{$premonth} {ts}(Last Month){/ts}</th>
         {/if}
         <th scope="col" colspan="3">{$month}{if $isCurrent}{ts} (MTD){/ts}{/if}</th>
         <th scope="col" colspan="3">
index 5af929ddc6cdaf4c03bb163a98c68db263b221ce..73fbcab9c29aa714d4438ad30067046095c1461f 100644 (file)
     </div>
 {else}
     <table class="form-layout">
-        <tr class="crm-uf_group-form-block-title">
-            <td class="label">{$form.title.label} {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_uf_group' field='title' id=$gid}{/if}</td>
-            <td class="html-adjust">{$form.title.html}</td>
-        </tr>
-        <tr class="crm-uf_group-form-block-frontend_title">
-            <td class="label">{$form.frontend_title.label}</td>
-            <td class="html-adjust">{$form.frontend_title.html}</td>
-        </tr>
-        <tr class="crm-uf_group-form-block-description">
-            <td class="label">{$form.description.label} {help id='id-description' file="CRM/UF/Form/Group.hlp"}</td>
-            <td class="html-adjust">{$form.description.html}</td>
-        </tr>
-        <tr class="crm-uf_group-form-block-uf_group_type">
-            <td class="label">{$form.uf_group_type.label} {help id='id-used_for' file="CRM/UF/Form/Group.hlp"}</td>
-            <td class="html-adjust">{$form.uf_group_type.html}&nbsp;{$otherModuleString}</td>
+      {foreach from=$entityFields item=fieldSpec}
+        {assign var=fieldName value=$fieldSpec.name}
+        <tr class="crm-{$entityInClassFormat}-form-block-{$fieldName}">
+          {include file="CRM/Core/Form/Field.tpl"}
         </tr>
+      {/foreach}
         <tr class="crm-uf_group-form-block-weight" >
             <td class="label">{$form.weight.label}{if $config->userSystem->is_drupal EQ '1'} {help id='id-profile_weight' file="CRM/UF/Form/Group.hlp"}{/if}</td>
             <td class="html-adjust">{$form.weight.html}</td>
index f099889aa29b8f6e84827bdf1a96009a75e5b8c8..7a9631ac934810ca5162a14bae002d39ae42ac62 100644 (file)
@@ -51,9 +51,9 @@ class CRM_Batch_BAO_BatchTest extends CiviUnitTestCase {
     $this->contributionCreate([
       'contact_id' => $contactId,
       'total_amount' => 1,
-      'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
-      'financial_type_id' => 1,
-      'contribution_status_id' => 1,
+      'payment_instrument_id' => 'Check',
+      'financial_type_id' => 'Donation',
+      'contribution_status_id' => 'Completed',
       'receive_date' => '20080522000000',
       'receipt_date' => '20080522000000',
       'trxn_id' => '22ereerwww322323',
@@ -67,9 +67,9 @@ class CRM_Batch_BAO_BatchTest extends CiviUnitTestCase {
     $this->contributionCreate([
       'contact_id' => $contactId,
      'total_amount' => 1,
-      'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Credit Card'),
-      'financial_type_id' => 1,
-      'contribution_status_id' => 1,
+      'payment_instrument_id' => 'Credit Card',
+      'financial_type_id' => 'Member Dues',
+      'contribution_status_id' => 'Completed',
       'receive_date' => '20080523000000',
       'receipt_date' => '20080523000000',
       'trxn_id' => '22ereerwww323323',
@@ -96,7 +96,12 @@ class CRM_Batch_BAO_BatchTest extends CiviUnitTestCase {
     $result = CRM_Batch_BAO_Batch::getBatchFinancialItems($entityId, $returnvalues, $notPresent, $params, TRUE)->fetchAll();
     $this->assertEquals(count($result), 1, 'In line' . __LINE__);
     $this->assertEquals($result[0]['payment_method'], CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'), 'In line' . __LINE__);
-
+    $params['financial_type_id'] = implode(',', [
+      CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation'),
+      CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Member Dues'),
+    ]);
+    $result = CRM_Batch_BAO_Batch::getBatchFinancialItems($entityId, $returnvalues, $notPresent, $params, TRUE)->fetchAll();
+    $this->assertEquals(count($result), 1, 'In line' . __LINE__);
   }
 
 }
index 755233f9b6f79e1634e8af46660f832f3e4a6f24..64f1140a7f059eeb8fbc0b878db34276c7cbb485 100644 (file)
@@ -510,6 +510,12 @@ class CRM_Contact_BAO_QueryTest extends CiviUnitTestCase {
     $this->assertContains('INNER JOIN civicrm_rel_temp_', $sql, "Query appears to use temporary table of compiled relationships?", TRUE);
   }
 
+  public function testRelationshipPermissionClause() {
+    $params = [['relation_type_id', 'IN', ['1_b_a'], 0, 0], ['relation_permission', 'IN', [2], 0, 0]];
+    $sql = CRM_Contact_BAO_Query::getQuery($params);
+    $this->assertContains('(civicrm_relationship.is_permission_a_b IN (2))', $sql);
+  }
+
   /**
    * Test Relationship Clause
    */
diff --git a/tests/phpunit/CRM/Contact/Form/Search/CriteriaTest.php b/tests/phpunit/CRM/Contact/Form/Search/CriteriaTest.php
new file mode 100644 (file)
index 0000000..eba45ea
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/*
+  +--------------------------------------------------------------------+
+  | CiviCRM version 5                                                  |
+  +--------------------------------------------------------------------+
+  | Copyright CiviCRM LLC (c) 2004-2018                                |
+  +--------------------------------------------------------------------+
+  | This file is a part of CiviCRM.                                    |
+  |                                                                    |
+  | CiviCRM is free software; you can copy, modify, and distribute it  |
+  | under the terms of the GNU Affero General Public License           |
+  | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+  |                                                                    |
+  | CiviCRM is distributed in the hope that it will be useful, but     |
+  | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+  | See the GNU Affero General Public License for more details.        |
+  |                                                                    |
+  | You should have received a copy of the GNU Affero General Public   |
+  | License and the CiviCRM Licensing Exception along                  |
+  | with this program; if not, contact CiviCRM LLC                     |
+  | at info[AT]civicrm[DOT]org. If you have questions about the        |
+  | GNU Affero General Public License or the licensing of CiviCRM,     |
+  | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+  +--------------------------------------------------------------------+
+ */
+
+/**
+ * Test class for CRM_Contact_Form_Search_Criteria
+ *
+ * @package CiviCRM
+ * @group headless
+ */
+class CRM_Contact_Form_Search_CriteriaTest extends CiviUnitTestCase {
+
+  /**
+   * Test that the 'multiple bulk email' setting correctly affects the type of
+   * field used for the 'email on hold' criteria in Advanced Search.
+   */
+  public function testAdvancedHSearchObservesMultipleBulkEmailSetting() {
+
+    // If setting is enabled, criteria should be a select element.
+    Civi::settings()->set('civimail_multiple_bulk_emails', 1);
+    $form = new CRM_Contact_Form_Search_Advanced();
+    $form->controller = new CRM_Contact_Controller_Search();
+    $form->preProcess();
+    $form->buildQuickForm();
+    $onHoldElemenClass = (get_class($form->_elements[$form->_elementIndex['email_on_hold']]));
+    $this->assertEquals('HTML_QuickForm_select', $onHoldElemenClass, 'civimail_multiple_bulk_emails setting = 1, so email_on_hold should be a select element.');
+
+    // If setting is disabled, criteria should be a checkbox.
+    Civi::settings()->set('civimail_multiple_bulk_emails', 0);
+    $form = new CRM_Contact_Form_Search_Advanced();
+    $form->controller = new CRM_Contact_Controller_Search();
+    $form->preProcess();
+    $form->buildQuickForm();
+    $onHoldElemenClass = (get_class($form->_elements[$form->_elementIndex['email_on_hold']]));
+    $this->assertEquals('HTML_QuickForm_advcheckbox', $onHoldElemenClass, 'civimail_multiple_bulk_emails setting = 0, so email_on_hold should be a checkbox.');
+  }
+
+}
index 9da92008186c76c7313fc13f2e66621df4dd91b6..bb9bf2939a060bc515677c80a59094bfd6ab9a96 100644 (file)
@@ -36,8 +36,8 @@
  * @package CiviCRM
  * @group headless
  */
-class CRM_Contact_Imports_Parser_ContactTest extends CiviUnitTestCase {
-  protected $_tablesToTruncate = array();
+class CRM_Contact_Import_Parser_ContactTest extends CiviUnitTestCase {
+  protected $_tablesToTruncate = ['civicrm_address', 'civicrm_phone'];
 
   /**
    * Setup function.
@@ -246,6 +246,7 @@ class CRM_Contact_Imports_Parser_ContactTest extends CiviUnitTestCase {
     $this->assertEquals(1, $address['location_type_id']);
     $this->assertEquals(1, $address['is_primary']);
 
+    $this->markTestIncomplete('phone actually doesn\'t work');
     $phone = $this->callAPISuccessGetSingle('Phone', array('phone' => '12334'));
     $this->assertEquals(1, $phone['location_type_id']);
 
@@ -300,6 +301,7 @@ class CRM_Contact_Imports_Parser_ContactTest extends CiviUnitTestCase {
     $this->assertEquals(1, $address['values'][1]['is_primary']);
     $this->assertEquals('Big Mansion', $address['values'][1]['street_address']);
 
+    $this->markTestIncomplete('phone import primary actually IS broken');
     $phone = $this->callAPISuccess('Phone', 'get', array('contact_id' => $contact['id'], 'sequential' => 1));
     $this->assertEquals(1, $phone['values'][0]['location_type_id']);
     $this->assertEquals(1, $phone['values'][0]['is_primary']);
@@ -329,23 +331,24 @@ class CRM_Contact_Imports_Parser_ContactTest extends CiviUnitTestCase {
     $fields[] = 'phone';
     $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 3, 6 => 3, 7 => 'Primary', 8 => 'Primary'), $fields);
     $contact = $this->callAPISuccessGetSingle('Contact', array('external_identifier' => 'android'));
-    $address = $this->callAPISuccess('Address', 'get', array('contact_id' => $contact['id'], 'sequential' => 1));
+    $address = $this->callAPISuccess('Address', 'get', array('contact_id' => $contact['id'], 'sequential' => 1))['values'];
 
-    $this->assertEquals(1, $address['values'][0]['location_type_id']);
-    $this->assertEquals(1, $address['values'][0]['is_primary']);
-    $this->assertEquals('Teeny Mansion', $address['values'][0]['street_address']);
+    $this->assertEquals(1, $address[1]['location_type_id']);
+    $this->assertEquals(1, $address[1]['is_primary']);
+    $this->assertEquals('Teeny Mansion', $address[1]['street_address']);
 
-    $this->assertEquals(3, $address['values'][1]['location_type_id']);
-    $this->assertEquals(0, $address['values'][1]['is_primary']);
-    $this->assertEquals('Big Mansion', $address['values'][1]['street_address']);
+    $this->assertEquals(3, $address[0]['location_type_id']);
+    $this->assertEquals(0, $address[0]['is_primary']);
+    $this->assertEquals('Big Mansion', $address[0]['street_address']);
 
-    $phone = $this->callAPISuccess('Phone', 'get', array('contact_id' => $contact['id'], 'sequential' => 1));
-    $this->assertEquals(3, $phone['values'][0]['location_type_id']);
-    $this->assertEquals(0, $phone['values'][0]['is_primary']);
-    $this->assertEquals(12334, $phone['values'][0]['phone']);
-    $this->assertEquals(1, $phone['values'][1]['location_type_id']);
-    $this->assertEquals(1, $phone['values'][1]['is_primary']);
-    $this->assertEquals(4444, $phone['values'][1]['phone']);
+    $this->markTestIncomplete('phone import primary actually IS broken');
+    $phone = $this->callAPISuccess('Phone', 'get', array('contact_id' => $contact['id'], 'sequential' => 1))['values'];
+    $this->assertEquals(3, $phone[1]['location_type_id']);
+    $this->assertEquals(0, $phone[1]['is_primary']);
+    $this->assertEquals(12334, $phone[1]['phone']);
+    $this->assertEquals(1, $phone[0]['location_type_id']);
+    $this->assertEquals(1, $phone[0]['is_primary']);
+    $this->assertEquals(4444, $phone[0]['phone']);
 
     $this->callAPISuccess('Contact', 'delete', array('id' => $contact['id']));
   }
@@ -546,11 +549,12 @@ class CRM_Contact_Imports_Parser_ContactTest extends CiviUnitTestCase {
    * @param int $onDuplicateAction
    * @param int $expectedResult
    * @param array|null $mapperLocType
+   *   Array of location types that map to the input arrays.
    * @param array|null $fields
    *   Array of field names. Will be calculated from $originalValues if not passed in, but
    *   that method does not cope with duplicates.
    */
-  protected function runImport($originalValues, $onDuplicateAction, $expectedResult, $mapperLocType = NULL, $fields = NULL) {
+  protected function runImport($originalValues, $onDuplicateAction, $expectedResult, $mapperLocType = [], $fields = NULL) {
     if (!$fields) {
       $fields = array_keys($originalValues);
     }
index 395cec106df2a11a81e7d99f47fb1a911bbcd79e..facc2fc7c9e7f1167b618206973ff21a7563056f 100644 (file)
@@ -42,6 +42,13 @@ class CRM_Contact_Page_View_UserDashBoardTest extends CiviUnitTestCase {
    */
   protected $contactID;
 
+  /**
+   * Contributions created for the test.
+   *
+   * @var array
+   */
+  protected $contributions = [];
+
   /**
    * Prepare for test
    */
@@ -58,6 +65,9 @@ class CRM_Contact_Page_View_UserDashBoardTest extends CiviUnitTestCase {
     $this->quickCleanUpFinancialEntities();
     $this->quickCleanup(['civicrm_uf_match']);
     CRM_Utils_Hook::singleton()->reset();
+    CRM_Core_Session::singleton()->reset();
+    CRM_Core_Smarty::singleton()->clearTemplateVars();
+    $this->callAPISuccess('Contact', 'delete', ['id' => $this->contactID]);
   }
 
   /**
@@ -79,42 +89,85 @@ class CRM_Contact_Page_View_UserDashBoardTest extends CiviUnitTestCase {
   /**
    * Test the content of the dashboard.
    */
-  public function testDashboardContentContributions() {
-    $this->contributionCreate(['contact_id' => $this->contactID]);
+  public function testDashboardContentContributionsWithInvoicingEnabled() {
+    $this->contributions[] = $this->contributionCreate([
+      'contact_id' => $this->contactID,
+      'receive_date' => '2018-11-21',
+      'receipt_date' => '2018-11-22',
+    ]);
+    $this->contributions[] = $this->contributionCreate([
+      'contact_id' => $this->contactID,
+      'receive_date' => '2018-11-21',
+      'receipt_date' => '2018-11-22',
+      'trxn_id' => '',
+      'invoice_id' => '',
+    ]);
+    $recur = $this->callAPISuccess('ContributionRecur', 'create', [
+      'contact_id' => $this->contactID,
+      'frequency_interval' => 1,
+      'amount' => 5,
+    ]);
+    $this->contributions[] = $this->contributionCreate([
+      'contact_id' => $this->contactID,
+      'receive_date' => '2018-11-21',
+      'amount_level' => 'high',
+      'contribution_status_id' => 'Cancelled',
+      'invoice_id' => NULL,
+      'trxn_id' => NULL,
+      'contribution_recur_id' => $recur['id'],
+    ]);
+    $this->callAPISuccess('Setting', 'create', ['invoicing' => 1]);
     $this->runUserDashboard();
     $expectedStrings = [
       'Your Contribution(s)',
-       '<table class="selector"><tr class="columnheader"><th>Total Amount</th><th>Financial Type</th><th>Received date</th><th>Receipt Sent</th><th>Status</th>',
-      '</tr><tr id=\'rowid1\'class="odd-row"><td>$ 100.00 </td><td>Donation</td>',
-      '</td><td></td><td>Completed</td></tr></table>',
+      '<table class="selector"><tr class="columnheader"><th>Total Amount</th><th>Financial Type</th><th>Received date</th><th>Receipt Sent</th><th>Status</th><th></th>',
+      '<td>Completed</td><td><a class="button no-popup nowrap"href="/index.php?q=civicrm/contribute/invoice&amp;reset=1&amp;id=1&amp;cid=' . $this->contactID . '"><i class="crm-i fa-print"></i><span>Print Invoice</span></a></td></tr><tr id=\'rowid2\'',
     ];
+
     $this->assertPageContains($expectedStrings);
-    $this->assertSmartyVariables(['invoicing' => NULL]);
+    $this->assertSmartyVariableArrayIncludes('contribute_rows', 0, [
+      'contact_id' => $this->contactID,
+      'contribution_id' => '1',
+      'total_amount' => '100.00',
+      'financial_type' => 'Donation',
+      'contribution_source' => 'SSF',
+      'receive_date' => '2018-11-21 00:00:00',
+      'contribution_status' => 'Completed',
+      'currency' => 'USD',
+      //'receipt_date' => '2018-11-22 00:00:00',
+    ]);
+
   }
 
   /**
    * Test the content of the dashboard.
    */
-  public function testDashboardContentContributionsWithInvoicingEnabled() {
-    $this->markTestIncomplete('some issue on jenkins but not locally - disabling to investigage on master as this is an rc patch');
+  public function testDashboardContentContributions() {
     $this->contributionCreate(['contact_id' => $this->contactID]);
-    $this->callAPISuccess('Setting', 'create', ['invoicing' => 1]);
+    $this->contributions[] = civicrm_api3('Contribution', 'get', [
+      'contact_id' => $this->contactID,
+      'options' => ['limit' => 12, 'sort' => 'receive_date DESC'],
+      'sequential' => 1,
+    ])['values'];
     $this->runUserDashboard();
     $expectedStrings = [
       'Your Contribution(s)',
-      '<table class="selector"><tr class="columnheader"><th>Total Amount</th><th>Financial Type</th><th>Received date</th><th>Receipt Sent</th><th>Status</th><th></th>',
-      '<td>Completed</td><td><a class="button no-popup nowrap"href="/index.php?q=civicrm/contribute/invoice&amp;reset=1&amp;id=1&amp;cid=' . $this->contactID . '"><i class="crm-i fa-print"></i><span>Print Invoice</span></a></td></tr></table>',
+      '<table class="selector"><tr class="columnheader"><th>Total Amount</th><th>Financial Type</th><th>Received date</th><th>Receipt Sent</th><th>Status</th>',
+      '<td>$ 100.00 </td><td>Donation</td>',
+      '<td>Completed</td>',
     ];
     $this->assertPageContains($expectedStrings);
-    $this->assertSmartyVariables(['invoicing' => TRUE]);
   }
 
   /**
    * Run the user dashboard.
    */
   protected function runUserDashboard() {
+    $_REQUEST = ['reset' => 1, 'id' => $this->contactID];
     $dashboard = new CRM_Contact_Page_View_UserDashBoard();
+    $dashboard->_contactId = $this->contactID;
     $dashboard->run();
+    $_REQUEST = [];
   }
 
 }
index a409cf53b3996ecaf41135de3249c9d1767bdd75..311576a4ade5ed8c78edde899cc8f93215889a82 100644 (file)
@@ -542,14 +542,14 @@ class CRM_Contribute_Form_SearchTest extends CiviUnitTestCase {
         'form_value' => array('cancel_reason' => 'Invalid Credit Card Number'),
         'expected_count' => 1,
         'expected_contribution' => array($Contribution3['id']),
-        'expected_qill' => "Cancel Reason Like '%Invalid Credit Card Number%'",
+        'expected_qill' => "Cancellation / Refund Reason Like '%Invalid Credit Card Number%'",
       ),
       // Case 3: Search for Cancelled Date and Cancelled Reason
       array(
         'form_value' => array('cancel_date' => date('Y-m-d'), 'cancel_reason' => 'Insufficient funds'),
         'expected_count' => 1,
         'expected_contribution' => array($Contribution1['id']),
-        'expected_qill' => "Cancel Date Like '%" . date('Y-m-d') . "%'ANDCancel Reason Like '%Insufficient funds%'",
+        'expected_qill' => "Cancel Date Like '%" . date('Y-m-d') . "%'ANDCancellation / Refund Reason Like '%Insufficient funds%'",
       ),
     );
 
index 49dc922318e20f2fd40bb625a840d62c7e1a9e27..58b056cf05fa9325e83234fccf97030f4e6842da 100644 (file)
@@ -179,6 +179,26 @@ class CRM_Core_BAO_CustomFieldTest extends CiviUnitTestCase {
     $this->customGroupDelete($customGroup['id']);
   }
 
+  public function testGetDisplayedValuesContactRef() {
+    $customGroup = $this->customGroupCreate(['extends' => 'Individual']);
+    $params = [
+      'data_type' => 'ContactReference',
+      'html_type' => 'Autocomplete-Select',
+      'label' => 'test ref',
+      'custom_group_id' => $customGroup['id'],
+    ];
+    $createdField = $this->callAPISuccess('customField', 'create', $params);
+    $contact1 = $this->individualCreate();
+    $contact2 = $this->individualCreate(['custom_' . $createdField['id'] => $contact1['id']]);
+
+    $this->assertEquals($contact1['display_name'], CRM_Core_BAO_CustomField::displayValue($contact2['id'], $createdField['id']));
+    $this->assertEquals("Bob", CRM_Core_BAO_CustomField::displayValue("Bob", $createdField['id']));
+
+    $this->contactDelete($contact2['id']);
+    $this->contactDelete($contact1['id']);
+    $this->customGroupDelete($customGroup['id']);
+  }
+
   public function testDeleteCustomField() {
     $customGroup = $this->customGroupCreate(array('extends' => 'Individual'));
     $fields = array(
diff --git a/tests/phpunit/CRM/Core/I18n/LocaleTest.php b/tests/phpunit/CRM/Core/I18n/LocaleTest.php
new file mode 100644 (file)
index 0000000..cb1e88f
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 5                                                  |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2018                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+/**
+ * Class CRM_Core_I18n_LocaleTest
+ * @group headless
+ */
+class CRM_Core_I18n_LocaleTest extends CiviUnitTestCase {
+
+  public function setUp() {
+    parent::setUp();
+  }
+
+  public function tearDown() {
+    CRM_Core_I18n_Schema::makeSinglelingual('en_US');
+    parent::tearDown();
+  }
+
+  /**
+   *
+   */
+  public function testI18nLocaleChange() {
+    $this->enableMultilingual();
+    CRM_Core_I18n_Schema::addLocale('fr_CA', 'en_US');
+
+    CRM_Core_I18n::singleton()->setLocale('fr_CA');
+    $locale = CRM_Core_I18n::getLocale();
+
+    $this->assertEquals($locale, 'fr_CA');
+  }
+
+}
index 54af4b5a6e6cb5f146171f5c4e4772733af533b6..85d8043d748670c269ad0df96d62d15cdd9b6ac5 100644 (file)
@@ -61,45 +61,53 @@ class CRM_Event_BAO_AdditionalPaymentTest extends CiviUnitTestCase {
    *
    * @param int $feeTotal
    * @param int $actualPaidAmt
+   * @param array $participantParams
+   * @param array $contributionParams
    *
    * @return array
    * @throws Exception
    */
-  protected function addParticipantWithPayment($feeTotal, $actualPaidAmt) {
+  protected function addParticipantWithPayment($feeTotal, $actualPaidAmt, $participantParams = [], $contributionParams = []) {
     $priceSetId = $this->eventPriceSetCreate($feeTotal);
     CRM_Price_BAO_PriceSet::addTo('civicrm_event', $this->_eventId, $priceSetId);
 
     // create participant record
     $eventId = $this->_eventId;
-    $participantParams = array(
-      'send_receipt' => 1,
-      'is_test' => 0,
-      'is_pay_later' => 0,
-      'event_id' => $eventId,
-      'register_date' => date('Y-m-d') . " 00:00:00",
-      'role_id' => 1,
-      'status_id' => 14,
-      'source' => 'Event_' . $eventId,
-      'contact_id' => $this->_contactId,
-      'note' => 'Note added for Event_' . $eventId,
-      'fee_level' => '\ 1Price_Field - 55\ 1',
+    $participantParams = array_merge(
+      [
+        'send_receipt' => 1,
+        'is_test' => 0,
+        'is_pay_later' => 0,
+        'event_id' => $eventId,
+        'register_date' => date('Y-m-d') . " 00:00:00",
+        'role_id' => 1,
+        'status_id' => 14,
+        'source' => 'Event_' . $eventId,
+        'contact_id' => $this->_contactId,
+        'note' => 'Note added for Event_' . $eventId,
+        'fee_level' => '\ 1Price_Field - 55\ 1',
+      ],
+      $participantParams
     );
     $participant = $this->callAPISuccess('participant', 'create', $participantParams);
     $this->callAPISuccessGetSingle('participant', array('id' => $participant['id']));
     // create participant contribution with partial payment
-    $contributionParams = array(
-      'total_amount' => $actualPaidAmt,
-      'source' => 'Fall Fundraiser Dinner: Offline registration',
-      'currency' => 'USD',
-      'receipt_date' => date('Y-m-d') . " 00:00:00",
-      'contact_id' => $this->_contactId,
-      'financial_type_id' => 4,
-      'payment_instrument_id' => 4,
-      'contribution_status_id' => 1,
-      'receive_date' => date('Y-m-d') . " 00:00:00",
-      'skipLineItem' => 1,
-      'partial_payment_total' => $feeTotal,
-      'partial_amount_to_pay' => $actualPaidAmt,
+    $contributionParams = array_merge(
+      [
+        'total_amount' => $actualPaidAmt,
+        'source' => 'Fall Fundraiser Dinner: Offline registration',
+        'currency' => 'USD',
+        'receipt_date' => date('Y-m-d') . " 00:00:00",
+        'contact_id' => $this->_contactId,
+        'financial_type_id' => 4,
+        'payment_instrument_id' => 4,
+        'contribution_status_id' => 1,
+        'receive_date' => date('Y-m-d') . " 00:00:00",
+        'skipLineItem' => 1,
+        'partial_payment_total' => $feeTotal,
+        'partial_amount_to_pay' => $actualPaidAmt,
+      ],
+      $contributionParams
     );
 
     $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams);
@@ -136,6 +144,61 @@ class CRM_Event_BAO_AdditionalPaymentTest extends CiviUnitTestCase {
     );
   }
 
+  /**
+   * See https://lab.civicrm.org/dev/core/issues/153
+   */
+  public function testPaymentWithCustomPaymentInstrument() {
+    $feeAmt = 100;
+    $amtPaid = 0;
+
+    // Create undetermined Payment Instrument
+    $paymentInstrumentID = $this->createPaymentInstrument(['label' => 'Undetermined'], 'Accounts Receivable');
+
+    // record pending payment for an event
+    $result = $this->addParticipantWithPayment(
+      $feeAmt, $amtPaid,
+      ['is_pay_later' => 1],
+      [
+        'total_amount' => 100,
+        'payment_instrument_id' => $paymentInstrumentID,
+        'contribution_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'),
+      ]
+    );
+    $contributionID = $result['contribution']['id'];
+    extract($result);
+
+    // check payment info
+    $paymentInfo = CRM_Contribute_BAO_Contribution::getPaymentInfo($participant['id'], 'event');
+    $this->assertEquals(round($paymentInfo['total']), $feeAmt, 'Total amount recorded is not proper');
+    $this->assertEquals(round($paymentInfo['paid']), $amtPaid, 'Amount paid is not proper');
+    $this->assertEquals(round($paymentInfo['balance']), $feeAmt, 'Balance amount is not proper');
+    $this->assertEquals($paymentInfo['contribution_status'], 'Pending', 'Contribution status is not proper');
+
+    // make additional payment via 'Record Payment' form
+    $form = new CRM_Contribute_Form_AdditionalPayment();
+    $submitParams = array(
+      'contact_id' => $result['contribution']['contact_id'],
+      'contribution_id' => $contributionID,
+      'total_amount' => 100,
+      'currency' => 'USD',
+      'trxn_date' => '2017-04-11 13:05:11',
+      'payment_processor_id' => 0,
+      'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check'),
+      'check_number' => '#123',
+    );
+    $form->cid = $result['contribution']['contact_id'];
+    $form->testSubmit($submitParams);
+
+    // check payment info again and see if the payment is completed
+    $paymentInfo = CRM_Contribute_BAO_Contribution::getPaymentInfo($participant['id'], 'event');
+    $this->assertEquals(round($paymentInfo['total']), $feeAmt, 'Total amount recorded is not proper');
+    $this->assertEquals(round($paymentInfo['paid']), $feeAmt, 'Amount paid is not proper');
+    $this->assertEquals(round($paymentInfo['balance']), 0, 'Balance amount is not proper');
+    $this->assertEquals($paymentInfo['contribution_status'], 'Completed', 'Contribution status is not proper');
+
+    $this->callAPISuccess('OptionValue', 'delete', ['id' => $paymentInstrumentID]);
+  }
+
   /**
    * CRM-13964
    */
index ff368a31d79d12a4765ff5000dbd20bb51d68769..547dc90cf21dea73bb5db6bf33cb272e2f508715 100644 (file)
@@ -462,6 +462,13 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
     $dao->fetch();
     $this->assertEquals('Org 2', $dao->{$employerRelationshipTypeID . '_a_b_organization_name'});
     $this->assertEquals('well dodgey', $dao->{$employerRelationshipTypeID . '_a_b_legal_name'});
+
+    $this->assertEquals([
+      0 => 'First Name',
+      1 => 'Employee of-Organization Name',
+      2 => 'Employee of-Legal Name',
+      3 => 'Employee of-Contact Source',
+    ], $headerRows);
   }
 
   /**
@@ -1182,13 +1189,7 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
    * Get return properties manually added in.
    */
   public function getExtraReturnProperties() {
-    return [
-      'location_type' => 1,
-      'im_provider' => 1,
-      'phone_type_id' => 1,
-      'provider_id' => 1,
-      'current_employer' => 1,
-    ];
+    return [];
   }
 
   /**
@@ -1284,7 +1285,6 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
       'tags' => 1,
       'notes' => 1,
       'phone_type_id' => 1,
-      'provider_id' => 1,
     ];
     if (!$isContactMode) {
       unset($returnProperties['groups']);
@@ -1339,11 +1339,6 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
    */
   public function getMembershipReturnProperties() {
     return [
-      'location_type' => 1,
-      'im_provider' => 1,
-      'phone_type_id' => 1,
-      'provider_id' => 1,
-      'current_employer' => 1,
       'contact_type' => 1,
       'contact_sub_type' => 1,
       'sort_name' => 1,
@@ -1975,7 +1970,6 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
         80 => 'Group(s)',
         81 => 'Tag(s)',
         82 => 'Note(s)',
-        83 => 'IM Service Provider',
       ];
     if (!$isContactExport) {
       unset($headers[80]);
@@ -2065,7 +2059,7 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
       95 => 'Invoice Reference',
       96 => 'Invoice Number',
       97 => 'Currency',
-      98 => 'Cancel Reason',
+      98 => 'Cancellation / Refund Reason',
       99 => 'Receipt Date',
       100 => 'Product Name',
       101 => 'SKU',
@@ -2273,7 +2267,6 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
       'groups' => 'groups text',
       'tags' => 'tags text',
       'notes' => 'notes text',
-      'provider_id' => 'provider_id varchar(255)',
     ];
     if (!$isContactExport) {
       unset($columns['groups']);
@@ -2457,7 +2450,6 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
       'world_region' => 'world_region varchar(128)',
       'url' => 'url varchar(128)',
       'phone_type_id' => 'phone_type_id varchar(16)',
-      'provider_id' => 'provider_id varchar(255)',
       'financial_type' => 'financial_type varchar(64)',
       'contribution_source' => 'contribution_source varchar(255)',
       'receive_date' => 'receive_date varchar(32)',
index 25aa5add3dd912e899040aa9e65e20d1f053e5e6..87850752a153927c449249fd14834b555f47bbbf 100644 (file)
@@ -38,7 +38,7 @@ class CRM_Financial_Page_AjaxTest extends CiviUnitTestCase {
    */
   public function testGetFinancialTransactionsList() {
     $individualID = $this->individualCreate();
-    $this->contributionCreate(array('contact_id' => $individualID));
+    $this->contributionCreate(array('contact_id' => $individualID, 'trxn_id' => 12345));
     $batch = $this->callAPISuccess('Batch', 'create', array('title' => 'test', 'status_id' => 'Open'));
     CRM_Core_DAO::executeQuery("
      INSERT INTO civicrm_entity_batch (entity_table, entity_id, batch_id)
index 7641ab34f1efd64bc08e8f33d879190aa84cb832..0aea32679e39ba0d92ca0c378a6d48a93745b5d6 100644 (file)
@@ -135,6 +135,7 @@ class CRM_Member_Form_MembershipTest extends CiviUnitTestCase {
         'civicrm_membership_type',
         'civicrm_membership',
         'civicrm_uf_match',
+        'civicrm_email',
       )
     );
     foreach (array(17, 18, 23, 32) as $contactID) {
index d930c360c7a98c3d35b6ed7f4f402327c5eef957..570773bdc93498f5cb883ec6a2d29d02efdad430 100644 (file)
@@ -210,4 +210,48 @@ class CRM_Utils_DateTest extends CiviUnitTestCase {
     }
   }
 
+  /**
+   * Test customFormat() function
+   */
+  public function testCustomFormat() {
+    $dateTime = "2018-11-08 21:46:44";
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%b"), "Nov");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%B"), "November");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%d"), "08");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%e"), " 8");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%E"), "8");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%f"), "th");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%H"), "21");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%I"), "09");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%k"), "21");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%l"), " 9");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%m"), "11");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%M"), "46");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%p"), "pm");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%P"), "PM");
+    $this->assertEquals(CRM_Utils_Date::customFormat($dateTime, "%Y"), "2018");
+  }
+
+  /**
+   * Test customFormat() function
+   */
+  public function testCustomFormatTs() {
+    $ts = mktime(21, 46, 44, 11, 8, 2018);
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%b"), "Nov");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%B"), "November");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%d"), "08");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%e"), " 8");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%E"), "8");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%f"), "th");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%H"), "21");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%I"), "09");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%k"), "21");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%l"), " 9");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%m"), "11");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%M"), "46");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%p"), "pm");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%P"), "PM");
+    $this->assertEquals(CRM_Utils_Date::customFormatTs($ts, "%Y"), "2018");
+  }
+
 }
index a9b1bb36bf41523c721eba849aa2c214240e4f88..dab58cbfe5967a023472a40d3260181b34785b2c 100644 (file)
@@ -73,6 +73,8 @@ class CRM_Utils_TypeTest extends CiviUnitTestCase {
       array('field(table.civicrm_column_name,4,5,6)', 'MysqlOrderBy', 'field(table.civicrm_column_name,4,5,6)'),
       array('table.civicrm_column_name desc,other_column, another_column desc', 'MysqlOrderBy', 'table.civicrm_column_name desc,other_column, another_column desc'),
       array('table.`Home-street_address` asc, `table-alias`.`Home-street_address` desc,`table-alias`.column', 'MysqlOrderBy', 'table.`Home-street_address` asc, `table-alias`.`Home-street_address` desc,`table-alias`.column'),
+      // Lab issue dev/core#93 allow for 3 column orderby
+      array('contact_id.gender_id.label', 'MysqlOrderBy', 'contact_id.gender_id.label'),
       array('a string', 'String', 'a string'),
       array('{"contact":{"contact_id":205}}', 'Json', '{"contact":{"contact_id":205}}'),
       array('{"contact":{"contact_id":!n†rude®}}', 'Json', NULL),
index 150582e7c59084b239e82605ce07f19a97113b72..493369af68d160a7a70f8a5e4a690f4b5c39d725 100644 (file)
@@ -39,6 +39,16 @@ trait CRMTraits_Page_PageTestTrait {
    */
   protected $pageContent;
 
+  /**
+   * @var \CRM_Core_Page
+   */
+  protected $page;
+
+  /**
+   * @var string
+   */
+  protected $tplName;
+
   /**
    * Variables assigned to smarty.
    *
@@ -46,6 +56,8 @@ trait CRMTraits_Page_PageTestTrait {
    */
   protected $smartyVariables = [];
 
+  protected $context;
+
   /**
    * @param string $content
    * @param string $context
@@ -54,6 +66,9 @@ trait CRMTraits_Page_PageTestTrait {
    */
   public function checkPageContent(&$content, $context, $tplName, &$object) {
     $this->pageContent = $content;
+    $this->tplName = $tplName;
+    $this->page = $object;
+    $this->context = $context;
     // Ideally we would validate $content as valid html here.
     // Suppress console output.
     $content = '';
@@ -66,8 +81,10 @@ trait CRMTraits_Page_PageTestTrait {
    * @param $expectedStrings
    */
   protected function assertPageContains($expectedStrings) {
+    unset($this->smartyVariables['config']);
+    unset($this->smartyVariables['session']);
     foreach ($expectedStrings as $expectedString) {
-      $this->assertContains($expectedString, $this->pageContent);
+      $this->assertContains($expectedString, $this->pageContent, print_r($this->contributions, TRUE) . print_r($this->smartyVariables, TRUE));
     }
   }
 
@@ -82,6 +99,23 @@ trait CRMTraits_Page_PageTestTrait {
     }
   }
 
+  /**
+   * Check an array assigned to smarty for the inclusion of the expected variables.
+   *
+   * @param string $variableName
+   * @param $index
+   * @param $expected
+   */
+  protected function assertSmartyVariableArrayIncludes($variableName, $index, $expected) {
+    $smartyVariable = $this->smartyVariables[$variableName];
+    if ($index !== NULL) {
+      $smartyVariable = $smartyVariable[$index];
+    }
+    foreach ($expected as $key => $value) {
+      $this->assertEquals($value, $smartyVariable[$key], 'Checking ' . $key);
+    }
+  }
+
   /**
    * Set up environment to listen for page content.
    */
index 986a89169ffb8501be87882874202c92d7e24852..76a9d3f5c7553f5f337d64dd19c51a064228e9d8 100644 (file)
@@ -1302,8 +1302,6 @@ class CiviUnitTestCase extends PHPUnit_Extensions_Database_TestCase {
       'financial_type_id' => 1,
       'payment_instrument_id' => 1,
       'non_deductible_amount' => 10.00,
-      'trxn_id' => 12345,
-      'invoice_id' => 67890,
       'source' => 'SSF',
       'contribution_status_id' => 1,
     ), $params);
@@ -3384,6 +3382,37 @@ AND    ( TABLE_NAME LIKE 'civicrm_value_%' )
     return $this->callAPISuccess('FinancialType', 'create', $params);
   }
 
+  /**
+   * Create Payment Instrument.
+   *
+   * @param array $params
+   * @param string $financialAccountName
+   *
+   * @return int
+   */
+  protected function createPaymentInstrument($params = array(), $financialAccountName = 'Donation') {
+    $params = array_merge(array(
+      'label' => 'Payment Instrument -' . substr(sha1(rand()), 0, 7),
+      'option_group_id' => 'payment_instrument',
+      'is_active' => 1,
+      ),
+      $params
+    );
+    $newPaymentInstrument = $this->callAPISuccess('OptionValue', 'create', $params);
+
+    $relationTypeID = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Asset Account is' "));
+
+    $financialAccountParams = [
+      'entity_table' => 'civicrm_option_value',
+      'entity_id' => key($newPaymentInstrument),
+      'account_relationship' => $relationTypeID,
+      'financial_account_id' => $this->callAPISuccess('FinancialAccount', 'getValue', ['name' => $financialAccountName, 'return' => 'id']),
+    ];
+    CRM_Financial_BAO_FinancialTypeAccount::add($financialAccountParams);
+
+    return CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', $params['label']);
+  }
+
   /**
    * Enable Tax and Invoicing
    */
index 3ca3c48aa68b516ecc408f0b9ed7e314995977d3..d416e564b8141e49b34e9c21991cb0990b1f2f7d 100644 (file)
@@ -93,6 +93,19 @@ class PrevNextTest extends \CiviEndToEndTestCase {
     $this->assertSelections([]);
   }
 
+  public function testFetch() {
+    $this->testFillArray();
+
+    $cids = $this->prevNext->fetch($this->cacheKey, 0, 2);
+    $this->assertEquals([100, 400], $cids);
+
+    $cids = $this->prevNext->fetch($this->cacheKey, 0, 4);
+    $this->assertEquals([100, 400, 200, 300], $cids);
+
+    $cids = $this->prevNext->fetch($this->cacheKey, 2, 2);
+    $this->assertEquals([200, 300], $cids);
+  }
+
   public function getFillFunctions() {
     return [
       ['testFillSql'],
index 5e7f2479e80e543e6be845d2e1e3daa6b6e4da7a..0a9529957c2eb094bbd0bf44b56e0a8ef27e4b28 100644 (file)
@@ -1798,7 +1798,7 @@ class api_v3_ContributionPageTest extends CiviUnitTestCase {
     $this->callAPISuccess('contribution_page', 'submit', $submitParams);
     $contribution = $this->callAPISuccessGetSingle('contribution', array(
       'contribution_page_id' => $this->_ids['contribution_page'],
-      'contribution_status_id' => 2,
+      'contribution_status_id' => 'Pending',
     ));
     $this->assertEquals(80, $contribution['total_amount']);
     $lineItems = $this->callAPISuccess('LineItem', 'get', array(
index 3f7294e023ad56563c01077e5f4c2a290fc3d499..8cf1232c89a8c361117347d2340c2bdad2c106f0 100644 (file)
@@ -267,6 +267,8 @@ class api_v3_ContributionTest extends CiviUnitTestCase {
     $params = array_merge($params, array(
       'id' => $contributionID,
       'invoice_number' => CRM_Utils_Array::value('invoice_prefix', Civi::settings()->get('contribution_invoice_settings')) . "" . $contributionID,
+      'trxn_id' => 12345,
+      'invoice_id' => 6789,
     ));
     $contributionID = $this->contributionCreate($params);
 
@@ -302,6 +304,21 @@ class api_v3_ContributionTest extends CiviUnitTestCase {
     }
   }
 
+  /**
+   * Test cancel reason works as a filter.
+   */
+  public function testFilterCancelReason() {
+    $params = $this->_params;
+    $params['cancel_date'] = 'yesterday';
+    $params['cancel_reason'] = 'You lose sucker';
+    $this->callAPISuccess('Contribution', 'create', $params);
+    $params = $this->_params;
+    $params['cancel_date'] = 'yesterday';
+    $params['cancel_reason'] = 'You are a winner';
+    $this->callAPISuccess('Contribution', 'create', $params);
+    $this->callAPISuccessGetCount('Contribution', ['cancel_reason' => 'You are a winner'], 1);
+  }
+
   /**
    * We need to ensure previous tested behaviour still works as part of the api contract.
    */
index f16401859644f5a0f8505cd9028e372f13275ab4..b3902e4eb784e21ef0f3a4019cc6e0e971c93d76 100644 (file)
@@ -532,4 +532,104 @@ class api_v3_OrderTest extends CiviUnitTestCase {
     ));
   }
 
+  /**
+   * @expectedException CiviCRM_API3_Exception
+   * @expectedExceptionMessage Line item total doesn't match with total amount.
+   */
+  public function testCreateOrderIfTotalAmountDoesNotMatchLineItemsAmountsIfNoTaxSupplied() {
+    $params = [
+      'contact_id' => $this->_individualId,
+      'receive_date' => '2018-01-01',
+      'total_amount' => 50,
+      'financial_type_id' => $this->_financialTypeId,
+      'contribution_status_id' => 1,
+      'line_items' => [
+        0 => [
+          'line_item' => [
+            '0' => [
+              'price_field_id' => 1,
+              'price_field_value_id' => 1,
+              'label' => 'Test 1',
+              'field_title' => 'Test 1',
+              'qty' => 1,
+              'unit_price' => 40,
+              'line_total' => 40,
+              'financial_type_id' => 1,
+              'entity_table' => 'civicrm_contribution',
+            ],
+          ]
+        ],
+      ],
+    ];
+
+    civicrm_api3('Order', 'create', $params);
+  }
+
+  /**
+   * @expectedException CiviCRM_API3_Exception
+   * @expectedExceptionMessage Line item total doesn't match with total amount.
+   */
+  public function testCreateOrderIfTotalAmountDoesNotMatchLineItemsAmountsIfTaxSupplied() {
+    $params = [
+      'contact_id' => $this->_individualId,
+      'receive_date' => '2018-01-01',
+      'total_amount' => 50,
+      'financial_type_id' => $this->_financialTypeId,
+      'contribution_status_id' => 1,
+      'tax_amount' => 15,
+      'line_items' => [
+        0 => [
+          'line_item' => [
+            '0' => [
+              'price_field_id' => 1,
+              'price_field_value_id' => 1,
+              'label' => 'Test 1',
+              'field_title' => 'Test 1',
+              'qty' => 1,
+              'unit_price' => 30,
+              'line_total' => 30,
+              'financial_type_id' => 1,
+              'entity_table' => 'civicrm_contribution',
+              'tax_amount' => 15,
+            ],
+          ]
+        ],
+      ],
+    ];
+
+    civicrm_api3('Order', 'create', $params);
+  }
+
+  public function testCreateOrderIfTotalAmountDoesMatchLineItemsAmountsAndTaxSupplied() {
+    $params = [
+      'contact_id' => $this->_individualId,
+      'receive_date' => '2018-01-01',
+      'total_amount' => 50,
+      'financial_type_id' => $this->_financialTypeId,
+      'contribution_status_id' => 1,
+      'tax_amount' => 15,
+      'line_items' => [
+        0 => [
+          'line_item' => [
+            '0' => [
+              'price_field_id' => 1,
+              'price_field_value_id' => 1,
+              'label' => 'Test 1',
+              'field_title' => 'Test 1',
+              'qty' => 1,
+              'unit_price' => 35,
+              'line_total' => 35,
+              'financial_type_id' => 1,
+              'entity_table' => 'civicrm_contribution',
+              'tax_amount' => 15,
+            ],
+          ]
+        ],
+      ],
+    ];
+
+    $order = civicrm_api3('Order', 'create', $params);
+    $this->assertEquals(1, $order['count']);
+  }
+
 }
index e049425fc4ccb6eef7835681cf608ae68faea11e..64daaca635e3f15e9f8a343670a6bfbfe933ff5c 100644 (file)
@@ -769,6 +769,39 @@ class api_v3_ProfileTest extends CiviUnitTestCase {
     $this->assertEquals("Hello 123", $note['note']);
   }
 
+  /**
+   * Check handling a custom greeting.
+   */
+  public function testSubmitGreetingFields() {
+    $profileFieldValues = $this->_createIndividualContact();
+    $params = reset($profileFieldValues);
+    $contactId = key($profileFieldValues);
+    $params['profile_id'] = $this->_profileID;
+    $params['contact_id'] = $contactId;
+
+    $this->callAPISuccess('ufField', 'create', array(
+      'uf_group_id' => $this->_profileID,
+      'field_name' => 'email_greeting',
+      'visibility' => 'Public Pages and Listings',
+      'field_type' => 'Contact',
+      'label' => 'Email Greeting',
+    ));
+
+    $emailGreetings = array_column(civicrm_api3('OptionValue', 'get', ['option_group_id' => "email_greeting"])['values'], NULL, 'name');
+
+    $params['email_greeting'] = $emailGreetings['Customized']['value'];
+    // Custom greeting should be required
+    $this->callAPIFailure('profile', 'submit', $params);
+
+    $params['email_greeting_custom'] = 'Hello fool!';
+    $this->callAPISuccess('profile', 'submit', $params);
+
+    // Api3 will not return custom greeting field so resorting to this
+    $greeting = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $contactId, 'email_greeting_custom');
+
+    $this->assertEquals("Hello fool!", $greeting);
+  }
+
   /**
    * Helper function to create an Individual with address/email/phone info. Import UF Group and UF Fields
    * @param array $params
index beeaaa98741380951611ebb65a49f13b193f20f6..7487fc7b6c782b71246c212d0aa582c42168d46c 100644 (file)
     <comment>Date on which given case starts.</comment>
     <html>
       <type>Select Date</type>
+      <formatType>activityDateTime</formatType>
     </html>
     <add>1.8</add>
   </field>
     <comment>Date on which given case ends.</comment>
     <html>
       <type>Select Date</type>
+      <formatType>activityDateTime</formatType>
     </html>
     <add>1.8</add>
   </field>
index edcb3befa4114113cf03630ef1f12b4830c9bf6c..5ea0ccc0fd8a2e0216253007e3440b9e2fd42253 100644 (file)
   <field>
     <name>cancel_reason</name>
     <type>text</type>
+    <title>Cancellation / Refund Reason</title>
     <import>true</import>
+    <export>true</export>
     <headerPattern>/(cancel.?)?reason/i</headerPattern>
     <html>
       <type>Text</type>
+      <size>40</size>
     </html>
     <add>1.3</add>
   </field>
index b4a0f2387bbb7464f0ecc0f131913ded772b51f9..e4b06ffd251ffa733c542ad22ee51874588ef856 100644 (file)
     <add>1.5</add>
   </foreignKey>
   <index>
-    <name>index_entity_file_id</name>
+    <name>UI_entity_table_entity_id_file_id</name>
     <fieldName>entity_table</fieldName>
     <fieldName>entity_id</fieldName>
     <fieldName>file_id</fieldName>
+    <unique>true</unique>
     <add>1.1</add>
   </index>
 </table>
index 6339d9c6cfd3c88dbfae6899ed3772de8c9594cb..4057e3bd9fdb812327fa5de77394d62e7e4f314e 100644 (file)
   </field>
   <field>
     <name>title</name>
+    <title>Profile Name</title>
     <type>varchar</type>
     <length>64</length>
     <localizable>true</localizable>
     <required>true</required>
+    <html>
+      <type>Text</type>
+    </html>
     <comment>Form title.</comment>
     <add>1.1</add>
   </field>
   <field>
     <name>frontend_title</name>
+    <title>Public Title</title>
     <type>varchar</type>
     <length>64</length>
     <localizable>true</localizable>
     <comment>Profile Form Public title</comment>
+    <html>
+      <type>Text</type>
+    </html>
     <add>4.7</add>
   </field>
   <field>
     <type>int</type>
     <required>true</required>
     <default>1</default>
+    <html>
+      <type>Text</type>
+    </html>
     <comment>Controls display order when multiple user framework groups are setup for concurrent display.</comment>
     <add>1.2</add>
     <drop>1.3</drop>
index 960d6990e58412c62ca3d5915b69b7f78c256db5..bbcd2296e9cde23c6937ecd8fb7b57b156454b20 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="iso-8859-1" ?>
 <version>
-  <version_no>5.8.beta1</version_no>
+  <version_no>5.9.alpha1</version_no>
 </version>