Merge pull request #13019 from seamuslee001/lab_core_481
authorEileen McNaughton <eileen@mcnaughty.com>
Mon, 29 Oct 2018 19:48:30 +0000 (08:48 +1300)
committerGitHub <noreply@github.com>
Mon, 29 Oct 2018 19:48:30 +0000 (08:48 +1300)
dev/core#481 Ensure that Bulk Mailings is a checkbox rather than radi…

23 files changed:
CRM/Admin/Form/Preferences/Contribute.php
CRM/Admin/Form/Preferences/Display.php
CRM/Admin/Form/Preferences/Mailing.php
CRM/Admin/Form/Preferences/Multisite.php
CRM/Admin/Form/SettingTrait.php
CRM/Admin/Page/APIExplorer.php
CRM/Contact/BAO/Query.php
CRM/Export/BAO/Export.php
CRM/Export/BAO/ExportProcessor.php
CRM/Mailing/BAO/Query.php
CRM/Member/ActionMapping.php
CRM/Report/Form/Contribute/Summary.php
Civi/ActionSchedule/RecipientBuilder.php
Civi/CCase/Events.php
settings/Contribute.setting.php
settings/Core.setting.php
settings/Mailing.setting.php
settings/Multisite.setting.php
templates/CRM/Form/basicForm.tpl
tests/phpunit/CRM/Contact/BAO/QueryTest.php
tests/phpunit/CRM/Core/BAO/ActionScheduleTest.php
tests/phpunit/CRM/Export/BAO/ExportTest.php
tests/phpunit/api/v3/ContactTest.php

index f650ec51de4b96a855e580c1b2d4c114872b9f9c..8e51270b0df3688d606b1fcd7548e93af4f25db7 100644 (file)
@@ -126,16 +126,17 @@ class CRM_Admin_Form_Preferences_Contribute extends CRM_Admin_Form_Preferences {
   public function buildQuickForm() {
     $htmlFields = array();
     foreach ($this->_settings as $setting => $group) {
+      // @todo - remove this whole loop! The parent form does this - it's just because of the werid handling
+      // of $htmlFields for this form that needs to be unwound that we have it atm.
+      // The 'basicform' has been contaminated with processing $htlFields
+      // to cater to this form - probably due to the way invoicing settings were handled as
+      // an array not a bunch of keys.
       $settingMetaData = civicrm_api3('setting', 'getfields', array('name' => $setting));
       $props = $settingMetaData['values'][$setting];
       if (isset($props['quick_form_type'])) {
         $add = 'add' . $props['quick_form_type'];
         if ($add == 'addElement') {
           if (in_array($props['html_type'], array('checkbox', 'textarea'))) {
-            $this->add($props['html_type'],
-              $setting,
-              $props['title']
-            );
           }
           else {
             if ($props['html_type'] == 'select') {
@@ -144,24 +145,8 @@ class CRM_Admin_Form_Preferences_Contribute extends CRM_Admin_Form_Preferences {
                 $props['option_values'] = array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::$functionName();
               }
             }
-            $this->$add(
-              $props['html_type'],
-              $setting,
-              ts($props['title']),
-              CRM_Utils_Array::value($props['html_type'] == 'select' ? 'option_values' : 'html_attributes', $props, array()),
-              $props['html_type'] == 'select' ? CRM_Utils_Array::value('html_attributes', $props) : NULL
-            );
           }
         }
-        elseif ($add == 'addMonthDay') {
-          $this->add('date', $setting, ts($props['title']), CRM_Core_SelectValues::date(NULL, 'M d'));
-        }
-        elseif ($add == 'addDate') {
-          $this->addDate($setting, ts($props['title']), FALSE, array('formatType' => $props['type']));
-        }
-        else {
-          $this->$add($setting, ts($props['title']));
-        }
       }
       $htmlFields[$setting] = ts($props['description']);
     }
@@ -197,9 +182,6 @@ class CRM_Admin_Form_Preferences_Contribute extends CRM_Admin_Form_Preferences {
     unset($params['qfKey']);
     unset($params['entryURL']);
     Civi::settings()->set('contribution_invoice_settings', $params);
-    Civi::settings()->set('update_contribution_on_membership_type_change',
-      CRM_Utils_Array::value('update_contribution_on_membership_type_change', $params)
-    );
 
     // to set default value for 'Invoices / Credit Notes' checkbox on display preferences
     $values = CRM_Core_BAO_Setting::getItem("CiviCRM Preferences");
index 60c03f623d72af0e7d0c6d7250f8fba7d770cbd6..b5fc8bc460d82ef0e40165c893ab7dddab3a2aba 100644 (file)
@@ -41,7 +41,10 @@ class CRM_Admin_Form_Preferences_Display extends CRM_Admin_Form_Preferences {
     'contact_smart_group_display' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
     'advanced_search_options' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
     'user_dashboard_options' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
+    'activity_assignee_notification' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
+    'activity_assignee_notification_ics' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
     'preserve_activity_tab_filter' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
+    'ajaxPopupsEnabled' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
     'display_name_format' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
     'sort_name_format' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
   );
@@ -57,31 +60,15 @@ class CRM_Admin_Form_Preferences_Display extends CRM_Admin_Form_Preferences {
           'title' => ts('Editing Contacts'),
           'weight' => 3,
         ),
-        'activity_assignee_notification' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Notify Activity Assignees'),
-          'weight' => 5,
-        ),
-        'activity_assignee_notification_ics' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Include ICal Invite to Activity Assignees'),
-          'weight' => 6,
-        ),
         'contact_ajax_check_similar' => array(
           'title' => ts('Check for Similar Contacts'),
           'weight' => 8,
           'html_type' => NULL,
         ),
-
         'editor_id' => array(
           'html_type' => NULL,
           'weight' => 12,
         ),
-        'ajaxPopupsEnabled' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Enable Popup Forms'),
-          'weight' => 13,
-        ),
         'do_not_notify_assignees_for' => array(
           'html_type' => 'select',
           'option_values' => $optionValues,
index e26a56a57fe160f9dbe8d1b576fbbf706ab017ae..697c1fbaafe215d9d652210912243f41e7ba8da5 100644 (file)
  *
  * @package CRM
  * @copyright CiviCRM LLC (c) 2004-2018
- * $Id: Display.php 36505 2011-10-03 14:19:56Z lobo $
- *
  */
 
 /**
- * This class generates form components for the component preferences
- *
+ * This class generates form components for the maling component preferences.
  */
 class CRM_Admin_Form_Preferences_Mailing extends CRM_Admin_Form_Preferences {
-  public function preProcess() {
-    CRM_Utils_System::setTitle(ts('CiviMail Component Settings'));
-    $this->_varNames = array(
-      CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME => array(
-        'profile_double_optin' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Enable Double Opt-in for Profile Group(s) field'),
-          'weight' => 1,
-          'description' => ts('When CiviMail is enabled, users who "subscribe" to a group from a profile Group(s) checkbox will receive a confirmation email. They must respond (opt-in) before they are added to the group.'),
-        ),
-        'profile_add_to_group_double_optin' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Enable Double Opt-in for Profiles which use the "Add to Group" setting'),
-          'weight' => 2,
-          'description' => ts('When CiviMail is enabled and a profile uses the "Add to Group" setting, users who complete the profile form will receive a confirmation email. They must respond (opt-in) before they are added to the group.'),
-        ),
-        'track_civimail_replies' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Track replies using VERP in Reply-To header'),
-          'weight' => 3,
-          'description' => ts('If checked, mailings will default to tracking replies using VERP-ed Reply-To.'),
-        ),
-        'civimail_workflow' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Enable workflow support for CiviMail'),
-          'weight' => 4,
-          'description' => ts('Drupal-only. Rules module must be enabled (beta feature - use with caution).'),
-        ),
-        'civimail_multiple_bulk_emails' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Enable multiple bulk email address for a contact.'),
-          'weight' => 5,
-          'description' => ts('CiviMail will deliver a copy of the email to each bulk email listed for the contact.'),
-        ),
-        'civimail_server_wide_lock' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Enable global server wide lock for CiviMail'),
-          'weight' => 6,
-          'description' => NULL,
-        ),
-        'include_message_id' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Enable CiviMail to generate Message-ID header'),
-          'weight' => 7,
-          'description' => NULL,
-        ),
-        'write_activity_record' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Enable CiviMail to create activities on delivery'),
-          'weight' => 8,
-          'description' => NULL,
-        ),
-        'disable_mandatory_tokens_check' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Disable check for mandatory tokens'),
-          'weight' => 9,
-          'description' => ts('Don\'t check for presence of mandatory tokens (domain address; unsubscribe/opt-out) before sending mailings. WARNING: Mandatory tokens are a safe-guard which facilitate compliance with the US CAN-SPAM Act. They should only be disabled if your organization adopts other mechanisms for compliance or if your organization is not subject to CAN-SPAM.'),
-        ),
-        'dedupe_email_default' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('CiviMail dedupes e-mail addresses by default'),
-          'weight' => 10,
-          'description' => NULL,
-        ),
-        'hash_mailing_url' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Hashed Mailing URL\'s'),
-          'weight' => 11,
-          'description' => ts('If enabled, a randomized hash key will be used to reference the mailing URL in the mailing.viewUrl token, instead of the mailing ID'),
-        ),
-        'auto_recipient_rebuild' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Enable automatic CiviMail recipient count display'),
-          'weight' => 12,
-          'description' => ts('Enable this setting to rebuild recipient list automatically during composing mail. Disable will allow you to rebuild recipient manually.'),
-        ),
-      ),
-    );
 
-    parent::preProcess();
-  }
+  protected $_settings = [
+    'profile_double_optin' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'profile_add_to_group_double_optin' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'track_civimail_replies' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'civimail_workflow' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'civimail_multiple_bulk_emails' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'civimail_server_wide_lock' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'include_message_id' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'write_activity_record' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'disable_mandatory_tokens_check' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'dedupe_email_default' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'hash_mailing_url' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'auto_recipient_rebuild' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+  ];
 
   public function postProcess() {
-    // check if mailing tab is enabled, if not prompt user to enable the tab if "write_activity_record" is disabled
     $params = $this->controller->exportValues($this->_name);
 
     if (empty($params['write_activity_record'])) {
+      // @todo use the setting onToggle & add an action rather than have specific form handling.
+      // see logging setting for eg.
       $existingViewOptions = Civi::settings()->get('contact_view_options');
 
       $displayValue = CRM_Core_OptionGroup::getValue('contact_view_options', 'CiviMail', 'name');
index 1a8460357e0b9abacfe70d8716fe44f8c03ec9be..db9930e1cb16558e3e48d60a6a232710be8f5a8b 100644 (file)
  *
  * @package CRM
  * @copyright CiviCRM LLC (c) 2004-2018
- * $Id: Display.php 36505 2011-10-03 14:19:56Z lobo $
- *
  */
 
 /**
- * This class generates form components for multi site preferences
- *
+ * This class generates form components for multi site preferences.
  */
 class CRM_Admin_Form_Preferences_Multisite extends CRM_Admin_Form_Preferences {
-  public function preProcess() {
-    $msDoc = CRM_Utils_System::docURL2('Multi Site Installation', NULL, NULL, NULL, NULL, "wiki");
-    CRM_Utils_System::setTitle(ts('Multi Site Settings'));
-    $this->_varNames = array(
-      CRM_Core_BAO_Setting::MULTISITE_PREFERENCES_NAME => array(
-        'is_enabled' => array(
-          'html_type' => 'checkbox',
-          'title' => ts('Enable Multi Site Configuration'),
-          'weight' => 1,
-          'description' => ts('Make CiviCRM aware of multiple domains. You should configure a domain group if enabled') . ' ' . $msDoc,
-        ),
-        /** Remove this checkbox until some one knows what this setting does
-         * 'uniq_email_per_site' => array(
-         * 'html_type' => 'checkbox',
-         * 'title' => ts('Ensure multi sites have a unique email per site'),
-         * 'weight' => 2,
-         * 'description' => NULL,
-         * ),
-         */
-        'domain_group_id' => array(
-          'html_type' => 'entity_reference',
-          'title' => ts('Domain Group'),
-          'weight' => 3,
-          'options' => array('entity' => 'group', 'select' => array('minimumInputLength' => 0)),
-          'description' => ts('Contacts created on this site are added to this group'),
-        ),
-        /** Remove this checkbox until some one knows what this setting does
-         * 'event_price_set_domain_id' => array(
-         * 'html_type' => 'text',
-         * 'title' => ts('Domain for event price sets'),
-         * 'weight' => 4,
-         * 'description' => NULL,
-         * ),
-         */
-      ),
-    );
 
-    parent::preProcess();
-  }
+  protected $_settings = [
+    'is_enabled' => CRM_Core_BAO_Setting::MULTISITE_PREFERENCES_NAME,
+    'domain_group_id' => CRM_Core_BAO_Setting::MULTISITE_PREFERENCES_NAME,
+  ];
 
 }
index 827b44c56c3ca179b7a2a1c5e55229354c907ad9..219291fa578f02b940a8552829b36b32c6fc7288 100644 (file)
@@ -74,7 +74,16 @@ trait CRM_Admin_Form_SettingTrait {
    * @return array
    */
   protected function getSettingsToSetByMetadata($params) {
-    return array_intersect_key($params, $this->_settings);
+    $setValues = array_intersect_key($params, $this->_settings);
+    // Checkboxes will be unset rather than empty so we need to add them back in.
+    // Handle quickform hateability just once, right here right now.
+    $unsetValues = array_diff_key($this->_settings, $params);
+    foreach ($unsetValues as $key => $unsetValue) {
+      if ($this->getQuickFormType($this->getSettingMetadata($key)) === 'CheckBox') {
+        $setValues[$key] = [$key => 0];
+      }
+    }
+    return $setValues;
   }
 
   /**
@@ -134,6 +143,11 @@ trait CRM_Admin_Form_SettingTrait {
           $this->includesReadOnlyFields = TRUE;
         }
 
+        if (isset($props['help_link'])) {
+          // Set both the value in this loop & the outer value as we assign both to the template while we deprecate the $descriptions assignment.
+          $settingMetaData[$setting]['description'] = $props['description'] .= ' ' . CRM_Utils_System::docURL2($props['help_link']['page'], NULL, NULL, NULL, NULL, $props['help_link']['resource']);
+
+        }
         $add = 'add' . $quickFormType;
         if ($add == 'addElement') {
           $this->$add(
@@ -171,6 +185,9 @@ trait CRM_Admin_Form_SettingTrait {
         elseif ($add == 'addMonthDay') {
           $this->add('date', $setting, ts($props['title']), CRM_Core_SelectValues::date(NULL, 'M d'));
         }
+        elseif ($add === 'addEntityRef') {
+          $this->$add($setting, ts($props['title']), $props['entity_reference_options']);
+        }
         else {
           $this->$add($setting, ts($props['title']), $options);
         }
@@ -202,7 +219,8 @@ trait CRM_Admin_Form_SettingTrait {
    * @return string
    */
   protected function getQuickFormType($spec) {
-    if (isset($spec['quick_form_type'])) {
+    if (isset($spec['quick_form_type']) &&
+    !($spec['quick_form_type'] === 'Element' && !empty($spec['html_type']))) {
       return $spec['quick_form_type'];
     }
     $mapping = [
@@ -211,6 +229,7 @@ trait CRM_Admin_Form_SettingTrait {
       'radio' => 'Radio',
       'select' => 'Select',
       'textarea' => 'Element',
+      'entity_reference' => 'EntityRef',
     ];
     return $mapping[$spec['html_type']];
   }
@@ -230,6 +249,9 @@ trait CRM_Admin_Form_SettingTrait {
       if ($this->getQuickFormType($spec) === 'CheckBoxes') {
         $this->_defaults[$setting] = array_fill_keys($this->_defaults[$setting], 1);
       }
+      if ($this->getQuickFormType($spec) === 'CheckBox') {
+        $this->_defaults[$setting] = [$setting => $this->_defaults[$setting]];
+      }
     }
   }
 
@@ -243,6 +265,10 @@ trait CRM_Admin_Form_SettingTrait {
       if ($this->getQuickFormType($this->getSettingMetadata($setting)) === 'CheckBoxes') {
         $settings[$setting] = array_keys($settingValue);
       }
+      if ($this->getQuickFormType($this->getSettingMetadata($setting)) === 'CheckBox') {
+        // This will be an array with one value.
+        $settings[$setting] = (int) reset($settings[$setting]);
+      }
     }
     civicrm_api3('setting', 'create', $settings);
   }
index 678cef4a8f4803c0e9ddf41d56667cfa5206d405..9d5cd46c9a9cbf20777551f72f8965d436168903 100644 (file)
  */
 class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
 
+  /**
+   * Return unique paths for checking for examples.
+   * @return array
+   */
+  private static function uniquePaths() {
+    // Ensure that paths with trailing slashes are properly dealt with
+    $paths = explode(PATH_SEPARATOR, get_include_path());
+    foreach ($paths as $id => $rawPath) {
+      $pathParts = explode(DIRECTORY_SEPARATOR, $rawPath);
+      foreach ($pathParts as $partId => $part) {
+        if (empty($part)) {
+          unset($pathParts[$partId]);
+        }
+      }
+      $newRawPath = implode(DIRECTORY_SEPARATOR, $pathParts);
+      if ($newRawPath != $rawPath) {
+        $paths[$id] = DIRECTORY_SEPARATOR . $newRawPath;
+      }
+    }
+    $paths = array_unique($paths);
+    return $paths;
+  }
+
+
   /**
    * Run page.
    *
@@ -51,12 +75,20 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
     $this->assign('operators', CRM_Core_DAO::acceptedSQLOperators());
 
     // List example directories
+    // use get_include_path to ensure that extensions are captured.
     $examples = array();
-    foreach (scandir(\Civi::paths()->getPath('[civicrm.root]/api/v3/examples')) as $item) {
-      if ($item && strpos($item, '.') === FALSE) {
-        $examples[] = $item;
+    $paths = self::uniquePaths();
+    foreach ($paths as $path) {
+      $dir = \CRM_Utils_File::addTrailingSlash($path) . 'api' . DIRECTORY_SEPARATOR . 'v3' . DIRECTORY_SEPARATOR . 'examples';
+      if (is_dir($dir)) {
+        foreach (scandir($dir) as $item) {
+          if ($item && strpos($item, '.') === FALSE && array_search($item, $examples) === FALSE) {
+            $examples[] = $item;
+          }
+        }
       }
     }
+    sort($examples);
     $this->assign('examples', $examples);
 
     return parent::run();
@@ -78,20 +110,31 @@ class CRM_Admin_Page_APIExplorer extends CRM_Core_Page {
   public static function getExampleFile() {
     if (!empty($_GET['entity']) && strpos($_GET['entity'], '.') === FALSE) {
       $examples = array();
-      foreach (scandir(\Civi::paths()->getPath("[civicrm.root]/api/v3/examples/{$_GET['entity']}")) as $item) {
-        $item = str_replace('.php', '', $item);
-        if ($item && strpos($item, '.') === FALSE) {
-          $examples[] = array('key' => $item, 'value' => $item);
+      $paths = self::uniquePaths();
+      foreach ($paths as $path) {
+        $dir = \CRM_Utils_File::addTrailingSlash($path) . 'api' . DIRECTORY_SEPARATOR . 'v3' . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $_GET['entity'];
+        if (is_dir($dir)) {
+          foreach (scandir($dir) as $item) {
+            $item = str_replace('.php', '', $item);
+            if ($item && strpos($item, '.') === FALSE) {
+              $examples[] = array('key' => $item, 'value' => $item);
+            }
+          }
         }
       }
       CRM_Utils_JSON::output($examples);
     }
     if (!empty($_GET['file']) && strpos($_GET['file'], '.') === FALSE) {
-      $fileName = \Civi::paths()->getPath("[civicrm.root]/api/v3/examples/{$_GET['file']}.php");
-      if (file_exists($fileName)) {
-        echo file_get_contents($fileName);
+      $paths = self::uniquePaths();
+      $fileFound = FALSE;
+      foreach ($paths as $path) {
+        $fileName = \CRM_Utils_File::addTrailingSlash($path) . 'api' . DIRECTORY_SEPARATOR . 'v3' . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $_GET['file'] . '.php';
+        if (!$fileFound && file_exists($fileName)) {
+          $fileFound = TRUE;
+          echo file_get_contents($fileName);
+        }
       }
-      else {
+      if (!$fileFound) {
         echo "Not found.";
       }
       CRM_Utils_System::civiExit();
index 027d8165dfdf87ad70f24a26c7797ca118b14dc5..083b643c5b75dc6fcfdca8c51913009738f929cd 100644 (file)
@@ -2294,8 +2294,7 @@ class CRM_Contact_BAO_Query {
 
         //get the location name
         list($tName, $fldName) = self::getLocationTableName($field['where'], $locType);
-        // LOWER roughly translates to 'hurt my database without deriving any benefit' See CRM-19811.
-        $fieldName = "LOWER(`$tName`.$fldName)";
+        $fieldName = "`$tName`.$fldName";
 
         // we set both _tables & whereTables because whereTables doesn't seem to do what the name implies it should
         $this->_tables[$tName] = $this->_whereTables[$tName] = 1;
@@ -2307,8 +2306,7 @@ class CRM_Contact_BAO_Query {
         }
         else {
           if ($op != 'IN' && !is_numeric($value) && !is_array($value)) {
-            // LOWER roughly translates to 'hurt my database without deriving any benefit' See CRM-19811.
-            $fieldName = "LOWER({$field['where']})";
+            $fieldName = "{$field['where']}";
           }
           else {
             $fieldName = "{$field['where']}";
@@ -3402,8 +3400,6 @@ WHERE  $smartGroupClause
     //By default, $sub elements should be joined together with OR statements (don't change this variable).
     $subGlue = ' OR ';
 
-    $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
-
     $firstChar = substr($value, 0, 1);
     $lastChar = substr($value, -1, 1);
     $quotes = array("'", '"');
@@ -3416,22 +3412,19 @@ WHERE  $smartGroupClause
     elseif ($op == 'LIKE' && strpos($value, ',') === FALSE) {
       $value = str_replace(' ', '%', $value);
     }
-    $value = $strtolower(CRM_Core_DAO::escapeString(trim($value)));
+    $value = CRM_Core_DAO::escapeString(trim($value));
     if (strlen($value)) {
       $fieldsub = array();
       $value = "'" . self::getWildCardedValue($wildcard, $op, $value) . "'";
       if ($fieldName == 'sort_name') {
-        // LOWER roughly translates to 'hurt my database without deriving any benefit' See CRM-19811.
-        $wc = self::caseImportant($op) ? "LOWER(contact_a.sort_name)" : "contact_a.sort_name";
+        $wc = "contact_a.sort_name";
       }
       else {
-        // LOWER roughly translates to 'hurt my database without deriving any benefit' See CRM-19811.
-        $wc = self::caseImportant($op) ? "LOWER(contact_a.display_name)" : "contact_a.display_name";
+        $wc = "contact_a.display_name";
       }
       $fieldsub[] = " ( $wc $op $value )";
       if ($config->includeNickNameInName) {
-        // LOWER roughly translates to 'hurt my database without deriving any benefit' See CRM-19811.
-        $wc = self::caseImportant($op) ? "LOWER(contact_a.nick_name)" : "contact_a.nick_name";
+        $wc = "contact_a.nick_name";
         $fieldsub[] = " ( $wc $op $value )";
       }
       if ($config->includeEmailInName) {
@@ -3490,7 +3483,7 @@ WHERE  $smartGroupClause
       return;
     }
 
-    $n = strtolower(trim($value));
+    $n = trim($value);
     if ($n) {
       if (substr($n, 0, 1) == '"' &&
         substr($n, -1, 1) == '"'
@@ -3566,7 +3559,6 @@ WHERE  $smartGroupClause
         $value = "%{$value}%";
       }
       $op = 'LIKE';
-      // LOWER roughly translates to 'hurt my database without deriving any benefit' See CRM-19811.
       $this->_where[$grouping][] = self::buildClause('civicrm_address.street_address', $op, $value, 'String');
       $this->_qill[$grouping][] = ts('Street') . " $op '$n'";
     }
index fec624e51fd6431b07cb02ebd0bcd75956c9f766..00214262afd44bb750bd7dd05597f0c9d00f125e 100644 (file)
@@ -357,11 +357,13 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
               $returnProperties[$householdRelationshipType][$key] = $value;
             }
           }
+          // @todo - don't use returnProperties above.
+          $processor->setHouseholdMergeReturnProperties($returnProperties[$householdRelationshipType]);
         }
       }
     }
 
-    list($relationQuery, $allRelContactArray) = self::buildRelatedContactArray($selectAll, $ids, $processor, $componentTable, $returnProperties);
+    self::buildRelatedContactArray($selectAll, $ids, $processor, $componentTable);
 
     // make sure the groups stuff is included only if specifically specified
     // by the fields param (CRM-1969), else we limit the contacts outputted to only
@@ -510,9 +512,20 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c
           }
 
           if ($processor->isRelationshipTypeKey($field)) {
-            $relDAO = CRM_Utils_Array::value($iterationDAO->contact_id, $allRelContactArray[$field]);
-            $relationQuery[$field]->convertToPseudoNames($relDAO);
-            self::fetchRelationshipDetails($relDAO, $value, $field, $row);
+            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);
@@ -1110,16 +1123,11 @@ WHERE  id IN ( $deleteIDString )
    */
   public static function mergeSameHousehold($exportTempTable, &$sqlColumns, $prefix) {
     $prefixColumn = $prefix . '_';
-    $allKeys = array_keys($sqlColumns);
     $replaced = array();
 
     // name map of the non standard fields in header rows & sql columns
     $mappingFields = array(
       'civicrm_primary_id' => 'id',
-      'contact_source' => 'source',
-      'current_employer_id' => 'employer_id',
-      'contact_is_deleted' => 'is_deleted',
-      'name' => 'address_name',
       'provider_id' => 'im_service_provider',
       'phone_type_id' => 'phone_type',
     );
@@ -1400,7 +1408,7 @@ WHERE  {$whereClause}";
                 $headerName = $field . '-' . 'current_employer';
               }
               else {
-                $headerName = $field . '-' . $queryFields[$relationField]['name'];
+                $headerName = $field . '-' . $relationField;
               }
             }
 
@@ -1502,6 +1510,8 @@ WHERE  {$whereClause}";
     $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
     $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id');
     $i18n = CRM_Core_I18n::singleton();
+    $field = $field . '_';
+
     foreach ($value as $relationField => $relationValue) {
       if (is_object($relDAO) && property_exists($relDAO, $relationField)) {
         $fieldValue = $relDAO->$relationField;
@@ -1532,7 +1542,6 @@ WHERE  {$whereClause}";
       else {
         $fieldValue = '';
       }
-      $field = $field . '_';
       $relPrefix = $field . $relationField;
 
       if (is_object($relDAO) && $relationField == 'id') {
@@ -1649,64 +1658,74 @@ WHERE  {$whereClause}";
    * @param $ids
    * @param \CRM_Export_BAO_ExportProcessor $processor
    * @param $componentTable
-   * @param $returnProperties
-   *
-   * @return array
    */
-  protected static function buildRelatedContactArray($selectAll, $ids, $processor, $componentTable, $returnProperties) {
+  protected static function buildRelatedContactArray($selectAll, $ids, $processor, $componentTable) {
     $allRelContactArray = $relationQuery = array();
     $queryMode = $processor->getQueryMode();
     $exportMode = $processor->getExportMode();
-    foreach (self::$relationshipTypes as $rel => $dnt) {
-      if ($relationReturnProperties = CRM_Utils_Array::value($rel, $returnProperties)) {
-        $allRelContactArray[$rel] = array();
-        // build Query for each relationship
-        $relationQuery[$rel] = new CRM_Contact_BAO_Query(NULL, $relationReturnProperties,
-          NULL, FALSE, FALSE, $queryMode
-        );
-        list($relationSelect, $relationFrom, $relationWhere, $relationHaving) = $relationQuery[$rel]->query();
-
-        list($id, $direction) = explode('_', $rel, 2);
-        // identify the relationship direction
-        $contactA = 'contact_id_a';
-        $contactB = 'contact_id_b';
-        if ($direction == 'b_a') {
-          $contactA = 'contact_id_b';
-          $contactB = 'contact_id_a';
-        }
-        $relIDs = self::getIDsForRelatedContact($ids, $exportMode);
 
-        $relationshipJoin = $relationshipClause = '';
-        if (!$selectAll && $componentTable) {
-          $relationshipJoin = " INNER JOIN {$componentTable} ctTable ON ctTable.contact_id = {$contactA}";
-        }
-        elseif (!empty($relIDs)) {
-          $relID = implode(',', $relIDs);
-          $relationshipClause = " AND crel.{$contactA} IN ( {$relID} )";
-        }
+    foreach ($processor->getRelationshipReturnProperties() as $relationshipKey => $relationReturnProperties) {
+      $allRelContactArray[$relationshipKey] = array();
+      // build Query for each relationship
+      $relationQuery = new CRM_Contact_BAO_Query(NULL, $relationReturnProperties,
+        NULL, FALSE, FALSE, $queryMode
+      );
+      list($relationSelect, $relationFrom, $relationWhere, $relationHaving) = $relationQuery->query();
+
+      list($id, $direction) = explode('_', $relationshipKey, 2);
+      // identify the relationship direction
+      $contactA = 'contact_id_a';
+      $contactB = 'contact_id_b';
+      if ($direction == 'b_a') {
+        $contactA = 'contact_id_b';
+        $contactB = 'contact_id_a';
+      }
+      $relIDs = self::getIDsForRelatedContact($ids, $exportMode);
+
+      $relationshipJoin = $relationshipClause = '';
+      if (!$selectAll && $componentTable) {
+        $relationshipJoin = " INNER JOIN {$componentTable} ctTable ON ctTable.contact_id = {$contactA}";
+      }
+      elseif (!empty($relIDs)) {
+        $relID = implode(',', $relIDs);
+        $relationshipClause = " AND crel.{$contactA} IN ( {$relID} )";
+      }
 
-        $relationFrom = " {$relationFrom}
-                INNER JOIN civicrm_relationship crel ON crel.{$contactB} = contact_a.id AND crel.relationship_type_id = {$id}
-                {$relationshipJoin} ";
-
-        //check for active relationship status only
-        $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[$rel]->_select, "crel.{$contactA}");
-        $relationSelect = "{$relationSelect}, {$contactA} as refContact ";
-        $relationQueryString = "$relationSelect $relationFrom $relationWhere $relationHaving $relationGroupBy";
-
-        $allRelContactDAO = CRM_Core_DAO::executeQuery($relationQueryString);
-        while ($allRelContactDAO->fetch()) {
-          //FIX Me: Migrate this to table rather than array
-          // build the array of all related contacts
-          $allRelContactArray[$rel][$allRelContactDAO->refContact] = clone($allRelContactDAO);
+      $relationFrom = " {$relationFrom}
+              INNER JOIN civicrm_relationship crel ON crel.{$contactB} = contact_a.id AND crel.relationship_type_id = {$id}
+              {$relationshipJoin} ";
+
+      //check for active relationship status only
+      $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}");
+      $relationSelect = "{$relationSelect}, {$contactA} as refContact ";
+      $relationQueryString = "$relationSelect $relationFrom $relationWhere $relationHaving $relationGroupBy";
+
+      $allRelContactDAO = CRM_Core_DAO::executeQuery($relationQueryString);
+      while ($allRelContactDAO->fetch()) {
+        $relationQuery->convertToPseudoNames($allRelContactDAO);
+        $row = [];
+        // @todo pass processor to fetchRelationshipDetails and set fields directly within it.
+        self::fetchRelationshipDetails($allRelContactDAO, $relationReturnProperties, $relationshipKey, $row);
+        foreach (array_keys($relationReturnProperties) as $property) {
+          if ($property === 'location') {
+            // @todo - simplify location in self::fetchRelationshipDetails - remove handling here. Or just call
+            // $processor->setRelationshipValue from fetchRelationshipDetails
+            foreach ($relationReturnProperties['location'] as $locationName => $locationValues) {
+              foreach (array_keys($locationValues) as $locationValue) {
+                $key = str_replace(' ', '_', $locationName) . '-' . $locationValue;
+                $processor->setRelationshipValue($relationshipKey, $allRelContactDAO->refContact, $key, $row[$relationshipKey . '__' . $key]);
+              }
+            }
+          }
+          else {
+            $processor->setRelationshipValue($relationshipKey, $allRelContactDAO->refContact, $property, $row[$relationshipKey . '_' . $property]);
+          }
         }
-        $allRelContactDAO->free();
       }
     }
-    return array($relationQuery, $allRelContactArray);
   }
 
   /**
index 026befc13373076bbf96260a0f553fca4bf75eec..0b77a52eda6e01de05a903991dc6b6c1812f4e50 100644 (file)
@@ -95,6 +95,21 @@ class CRM_Export_BAO_ExportProcessor {
    */
   protected $relationshipReturnProperties = [];
 
+  /**
+   * Get return properties by relationship.
+   * @return array
+   */
+  public function getRelationshipReturnProperties() {
+    return $this->relationshipReturnProperties;
+  }
+
+  /**
+   * Export values for related contacts.
+   *
+   * @var array
+   */
+  protected $relatedContactValues = [];
+
   /**
    * @var array
    */
@@ -131,7 +146,6 @@ class CRM_Export_BAO_ExportProcessor {
     $this->requestedFields = $requestedFields;
   }
 
-
   /**
    * @return array
    */
@@ -167,6 +181,41 @@ class CRM_Export_BAO_ExportProcessor {
     );
   }
 
+  /**
+   * Set the value for a relationship type field.
+   *
+   * In this case we are building up an array of properties for a related contact.
+   *
+   * These may be used for direct exporting or for merge to household depending on the
+   * options selected.
+   *
+   * @param string $relationshipType
+   * @param int $contactID
+   * @param string $field
+   * @param string $value
+   */
+  public function setRelationshipValue($relationshipType, $contactID, $field, $value) {
+    $this->relatedContactValues[$relationshipType][$contactID][$field] = $value;
+  }
+
+  /**
+   * Get the value for a relationship type field.
+   *
+   * In this case we are building up an array of properties for a related contact.
+   *
+   * These may be used for direct exporting or for merge to household depending on the
+   * options selected.
+   *
+   * @param string $relationshipType
+   * @param int $contactID
+   * @param string $field
+   *
+   * @return string
+   */
+  public function getRelationshipValue($relationshipType, $contactID, $field) {
+    return isset($this->relatedContactValues[$relationshipType][$contactID][$field]) ? $this->relatedContactValues[$relationshipType][$contactID][$field] : '';
+  }
+
   /**
    * @return bool
    */
@@ -567,6 +616,20 @@ class CRM_Export_BAO_ExportProcessor {
     return $this->relationshipReturnProperties[$relationshipKey];
   }
 
+  /**
+   * Add the main return properties to the household merge properties if needed for merging.
+   *
+   * If we are using household merge we need to add these to the relationship properties to
+   * be retrieved.
+   *
+   * @param $returnProperties
+   */
+  public function setHouseholdMergeReturnProperties($returnProperties) {
+    foreach ($this->getHouseholdRelationshipTypes() as $householdRelationshipType) {
+      $this->relationshipReturnProperties[$householdRelationshipType] = $returnProperties;
+    }
+  }
+
   /**
    * Get the default location fields to request.
    *
index d556dd9fd18dd1104c49143aa0647c21069be24c..5adc1376e9b19cd705888b5576c74b3cf6b22562 100644 (file)
@@ -237,9 +237,6 @@ class CRM_Mailing_BAO_Query {
   public static function whereClauseSingle(&$values, &$query) {
     list($name, $op, $value, $grouping, $wildcard) = $values;
 
-    $fields = array();
-    $fields = self::getFields();
-
     switch ($name) {
       case 'mailing_id':
         $selectedMailings = array_flip($value);
@@ -259,14 +256,13 @@ class CRM_Mailing_BAO_Query {
         return;
 
       case 'mailing_name':
-        $value = strtolower(addslashes($value));
+        $value = addslashes($value);
         if ($wildcard) {
           $value = "%$value%";
           $op = 'LIKE';
         }
 
-        // LOWER in query below roughly translates to 'hurt my database without deriving any benefit' See CRM-19811.
-        $query->_where[$grouping][] = "LOWER(civicrm_mailing.name) $op '$value'";
+        $query->_where[$grouping][] = "civicrm_mailing.name $op '$value'";
         $query->_qill[$grouping][] = "Mailing Namename $op \"$value\"";
         $query->_tables['civicrm_mailing'] = $query->_whereTables['civicrm_mailing'] = 1;
         $query->_tables['civicrm_mailing_recipients'] = $query->_whereTables['civicrm_mailing_recipients'] = 1;
index 069577e9dab9f3bb2cfcaff1bfe6c5e7490b0be9..25e64bf5022f98184548623990ec26b374118f32 100644 (file)
@@ -97,8 +97,16 @@ class CRM_Member_ActionMapping extends \Civi\ActionSchedule\Mapping {
     $query['casContactIdField'] = 'e.contact_id';
     $query['casEntityIdField'] = 'e.id';
     $query['casContactTableAlias'] = NULL;
+
+    // Leaving this in case of legacy databases
     $query['casDateField'] = str_replace('membership_', 'e.', $schedule->start_action_date);
 
+    // Options currently are just 'join_date', 'start_date', and 'end_date':
+    // they need an alias
+    if (strpos($query['casDateField'], 'e.') !== 0) {
+      $query['casDateField'] = 'e.' . $query['casDateField'];
+    }
+
     // FIXME: Numbers should be constants.
     if (in_array(2, $selectedStatuses)) {
       //auto-renew memberships
@@ -113,11 +121,28 @@ class CRM_Member_ActionMapping extends \Civi\ActionSchedule\Mapping {
         ->param('memberTypeValues', $selectedValues);
     }
     else {
+      // FIXME: The membership type is never null, so nobody will ever get a
+      // reminder if no membership types are selected.  Either this should be a
+      // validation on the reminder form or all types should get a reminder if
+      // no types are selected.
       $query->where("e.membership_type_id IS NULL");
     }
 
+    // FIXME: This makes a lot of sense for renewal reminders, but a user
+    // scheduling another kind of reminder might not expect members to be
+    // excluded if they have status overrides.  Ideally there would be some kind
+    // of setting per reminder.
     $query->where("( e.is_override IS NULL OR e.is_override = 0 )");
+
+    // FIXME: Similarly to overrides, excluding contacts who can't edit the
+    // primary member makes sense in the context of renewals (see CRM-11342) but
+    // would be a surprise for other use cases.
     $query->merge($this->prepareMembershipPermissionsFilter());
+
+    // FIXME: A lot of undocumented stuff happens with regard to
+    // `is_current_member`, and this is no exception.  Ideally there would be an
+    // opportunity to pick statuses when setting up the scheduled reminder
+    // rather than making the assumptions here.
     $query->where("e.status_id IN (#memberStatus)")
       ->param('memberStatus', \CRM_Member_PseudoConstant::membershipStatus(NULL, "(is_current_member = 1 OR name = 'Expired')", 'id'));
 
@@ -130,39 +155,22 @@ class CRM_Member_ActionMapping extends \Civi\ActionSchedule\Mapping {
   }
 
   /**
-   * @return array
+   * Filter out the memberships that are inherited from a contact that the
+   * recipient cannot edit.
+   *
+   * @return CRM_Utils_SQL_Select
    */
   protected function prepareMembershipPermissionsFilter() {
-    $query = '
-SELECT    cm.id AS owner_id, cm.contact_id AS owner_contact, m.id AS slave_id, m.contact_id AS slave_contact, cmt.relationship_type_id AS relation_type, rel.contact_id_a, rel.contact_id_b, rel.is_permission_a_b, rel.is_permission_b_a
-FROM      civicrm_membership m
-LEFT JOIN civicrm_membership cm ON cm.id = m.owner_membership_id
-LEFT JOIN civicrm_membership_type cmt ON cmt.id = m.membership_type_id
-LEFT JOIN civicrm_relationship rel ON ( ( rel.contact_id_a = m.contact_id AND rel.contact_id_b = cm.contact_id AND rel.relationship_type_id = cmt.relationship_type_id )
-                                        OR ( rel.contact_id_a = cm.contact_id AND rel.contact_id_b = m.contact_id AND rel.relationship_type_id = cmt.relationship_type_id ) )
-WHERE     m.owner_membership_id IS NOT NULL AND
-          ( rel.is_permission_a_b = 0 OR rel.is_permission_b_a = 0)
-
-';
-    $excludeIds = array();
-    $dao = \CRM_Core_DAO::executeQuery($query, array());
-    while ($dao->fetch()) {
-      if ($dao->slave_contact == $dao->contact_id_a && $dao->is_permission_a_b == 0) {
-        $excludeIds[] = $dao->slave_contact;
-      }
-      elseif ($dao->slave_contact == $dao->contact_id_b && $dao->is_permission_b_a == 0) {
-        $excludeIds[] = $dao->slave_contact;
-      }
-    }
-
-    if (!empty($excludeIds)) {
-      return \CRM_Utils_SQL_Select::fragment()
-        ->where("!casContactIdField NOT IN (#excludeMemberIds)")
-        ->param(array(
-          '#excludeMemberIds' => $excludeIds,
-        ));
-    }
-    return NULL;
+    $joins = [
+      'cm' => 'LEFT JOIN civicrm_membership cm ON cm.id = e.owner_membership_id',
+      'rela' => 'LEFT JOIN civicrm_relationship rela ON rela.contact_id_a = e.contact_id AND rela.contact_id_b = cm.contact_id AND rela.is_permission_a_b = #editPerm',
+      'relb' => 'LEFT JOIN civicrm_relationship relb ON relb.contact_id_a = cm.contact_id AND relb.contact_id_b = e.contact_id AND relb.is_permission_b_a = #editPerm',
+    ];
+
+    return \CRM_Utils_SQL_Select::fragment()
+      ->join(NULL, $joins)
+      ->param('#editPerm', CRM_Contact_BAO_Relationship::EDIT)
+      ->where('!( e.owner_membership_id IS NOT NULL AND rela.id IS NULL and relb.id IS NULL )');
   }
 
 }
index 2fb301915cff78d5e53cfb506477a4e7c0b38bdc..6ebe56ab8072b8cff26b4cc8ed2ac02ffa4288f5 100644 (file)
@@ -525,15 +525,7 @@ class CRM_Report_Form_Contribute_Summary extends CRM_Report_Form {
           AND {$this->_aliases['civicrm_batch']}.entity_table = 'civicrm_financial_trxn')";
     }
 
-    // for credit card type
-    if ($this->isTableSelected('civicrm_financial_trxn')) {
-      $this->_from .= "
-        LEFT JOIN civicrm_entity_financial_trxn eftcc
-          ON ({$this->_aliases['civicrm_contribution']}.id = eftcc.entity_id AND
-            eftcc.entity_table = 'civicrm_contribution')
-        LEFT JOIN civicrm_financial_trxn {$this->_aliases['civicrm_financial_trxn']}
-          ON {$this->_aliases['civicrm_financial_trxn']}.id = eftcc.financial_trxn_id";
-    }
+    $this->addFinancialTrxnFromClause();
   }
 
   /**
index ded78c660f197039d9e9969121c891f7960a2126..86c383ee64bc9fb2773844e71dc296c720489d36 100644 (file)
@@ -314,7 +314,7 @@ class RecipientBuilder {
 
     $addlCheck = \CRM_Utils_SQL_Select::from($query['casAddlCheckFrom'])
       ->select('*')
-      ->merge($query, array('params', 'wheres'))// why only where? why not the joins?
+      ->merge($query, array('params', 'wheres', 'joins'))
       ->merge($this->prepareRepetitionEndFilter($query['casDateField']))
       ->limit(1)
       ->strict()
index 9cecdf72e522fba92a38c185ed566758cc3672e3..a7d047dbfd2a25c556884dcea2ecdd744632d1fa 100644 (file)
@@ -46,11 +46,12 @@ class Events {
    * @throws \CRM_Core_Exception
    */
   public static function fireCaseChange(\Civi\Core\Event\PostEvent $event) {
-    $caseId = NULL;
+    // Activities can be linked to multiple cases, so $caseIds might be an array or an int
+    $caseIds = NULL;
     switch ($event->entity) {
       case 'Activity':
         if (!empty($event->object->case_id)) {
-          $caseId = $event->object->case_id;
+          $caseIds = $event->object->case_id;
         }
         break;
 
@@ -58,7 +59,7 @@ class Events {
         // by the time we get the post-delete event, the record is gone, so
         // there's nothing to analyze
         if ($event->action != 'delete') {
-          $caseId = $event->id;
+          $caseIds = $event->id;
         }
         break;
 
@@ -66,15 +67,17 @@ class Events {
         throw new \CRM_Core_Exception("CRM_Case_Listener does not support entity {$event->entity}");
     }
 
-    if ($caseId) {
-      if (!isset(self::$isActive[$caseId])) {
-        $tx = new \CRM_Core_Transaction();
-        \CRM_Core_Transaction::addCallback(
-          \CRM_Core_Transaction::PHASE_POST_COMMIT,
-          array(__CLASS__, 'fireCaseChangeForRealz'),
-          array($caseId),
-          "Civi_CCase_Events::fire::{$caseId}"
-        );
+    if ($caseIds) {
+      foreach ((array) $caseIds as $caseId) {
+        if (!isset(self::$isActive[$caseId])) {
+          $tx = new \CRM_Core_Transaction();
+          \CRM_Core_Transaction::addCallback(
+            \CRM_Core_Transaction::PHASE_POST_COMMIT,
+            array(__CLASS__, 'fireCaseChangeForRealz'),
+            array($caseId),
+            "Civi_CCase_Events::fire::{$caseId}"
+          );
+        }
       }
     }
   }
index 5bceb7424e9d151d213d1b0245501fe27702c158..41b9cc8e12f109f95cd5be5c9eb12c9965cb1a2f 100644 (file)
@@ -73,7 +73,7 @@ return array(
     'group_name' => 'Contribute Preferences',
     'group' => 'contribute',
     'name' => 'invoicing',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'quick_form_type' => 'Element',
     'default' => 0,
@@ -88,7 +88,7 @@ return array(
     'group_name' => 'Contribute Preferences',
     'group' => 'contribute',
     'name' => 'acl_financial_type',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'quick_form_type' => 'Element',
     'default' => 0,
@@ -103,7 +103,7 @@ return array(
     'group_name' => 'Contribute Preferences',
     'group' => 'contribute',
     'name' => 'deferred_revenue_enabled',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'quick_form_type' => 'Element',
     'default' => 0,
@@ -119,7 +119,7 @@ return array(
     'group' => 'contribute',
     'name' => 'default_invoice_page',
     'type' => 'Integer',
-    'quick_form_type' => 'Element',
+    'quick_form_type' => 'Select',
     'default' => NULL,
     'pseudoconstant' => array(
       // @todo - handle table style pseudoconstants for settings & avoid deprecated function.
@@ -137,7 +137,7 @@ return array(
     'group_name' => 'Contribute Preferences',
     'group' => 'contribute',
     'name' => 'always_post_to_accounts_receivable',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'quick_form_type' => 'Element',
     'default' => 0,
@@ -152,7 +152,7 @@ return array(
     'group_name' => 'Contribute Preferences',
     'group' => 'contribute',
     'name' => 'update_contribution_on_membership_type_change',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'quick_form_type' => 'Element',
     'default' => 0,
index e21fb003a9ed8bd7eef1af322d0f98421331222c..996b005c3aa861d6eaa6704af25bc1faef5744cf 100644 (file)
@@ -29,8 +29,6 @@
  *
  * @package CRM
  * @copyright CiviCRM LLC (c) 2004-2017
- * $Id$
- *
  */
 
 /**
@@ -243,7 +241,7 @@ return array(
     'group' => 'core',
     'name' => 'ajaxPopupsEnabled',
     'type' => 'Boolean',
-    'quick_form_type' => 'YesNo',
+    'html_type' => 'checkbox',
     'default' => 1,
     'add' => '4.5',
     'title' => ts('Enable Popup Forms'),
@@ -256,8 +254,8 @@ return array(
     'group_name' => 'CiviCRM Preferences',
     'group' => 'core',
     'name' => 'activity_assignee_notification',
-    'type' => 'String',
-    'html_type' => 'Text',
+    'type' => 'Boolean',
+    'html_type' => 'checkbox',
     'default' => '1',
     'add' => '4.1',
     'title' => ts('Notify Activity Assignees'),
@@ -270,8 +268,8 @@ return array(
     'group_name' => 'CiviCRM Preferences',
     'group' => 'core',
     'name' => 'activity_assignee_notification_ics',
-    'type' => 'String',
-    'html_type' => 'Text',
+    'type' => 'Boolean',
+    'html_type' => 'checkbox',
     'default' => '0',
     'add' => '4.3',
     'title' => ts('Include ICal Invite to Activity Assignees'),
index 691da48ee93e93fe6b8414b38515e4a5cb9f5238..effedd84d2cb5234d3aad2427d07e8dd74e007e6 100644 (file)
@@ -41,28 +41,28 @@ return array(
     'group_name' => 'Mailing Preferences',
     'group' => 'mailing',
     'name' => 'profile_double_optin',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'default' => '1',
     'add' => '4.1',
-    'title' => 'Enable Double Opt-in for Profile Group(s) field',
+    'title' => ts('Enable Double Opt-in for Profile Group(s) field'),
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => 'When CiviMail is enabled, users who "subscribe" to a group from a profile Group(s) checkbox will receive a confirmation email. They must respond (opt-in) before they are added to the group.',
+    'description' => ts('When CiviMail is enabled, users who "subscribe" to a group from a profile Group(s) checkbox will receive a confirmation email. They must respond (opt-in) before they are added to the group.'),
     'help_text' => NULL,
   ),
   'track_civimail_replies' => array(
     'group_name' => 'Mailing Preferences',
     'group' => 'mailing',
     'name' => 'track_civimail_replies',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'default' => '0',
     'add' => '4.1',
-    'title' => 'Track replies using VERP in Reply-To header',
+    'title' => ts('Track replies using VERP in Reply-To header'),
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => 'If checked, mailings will default to tracking replies using VERP-ed Reply-To. ',
+    'description' => ts('If checked, mailings will default to tracking replies using VERP-ed Reply-To. '),
     'help_text' => NULL,
     'validate_callback' => 'CRM_Core_BAO_Setting::validateBoolSetting',
   ),
@@ -70,25 +70,25 @@ return array(
     'group_name' => 'Mailing Preferences',
     'group' => 'mailing',
     'name' => 'civimail_workflow',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'default' => '0',
     'add' => '4.1',
-    'title' => 'Use CiviMail Workflow',
+    'title' => ts('Enable workflow support for CiviMail'),
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => 'When CiviMail is enabled, users who "subscribe" to a group from a profile Group(s) checkbox will receive a confirmation email. They must respond (opt-in) before they are added to the group.',
+    'description' => ts('Drupal-only. Rules module must be enabled (beta feature - use with caution).'),
     'help_text' => NULL,
   ),
   'civimail_server_wide_lock' => array(
     'group_name' => 'Mailing Preferences',
     'group' => 'mailing',
     'name' => 'civimail_server_wide_lock',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'default' => '0',
     'add' => '4.1',
-    'title' => 'Lock Mails Server-Wide for Mail Sending',
+    'title' => ts('Enable global server wide lock for CiviMail'),
     'is_domain' => 1,
     'is_contact' => 0,
     'description' => NULL,
@@ -126,81 +126,81 @@ return array(
     'group_name' => 'Mailing Preferences',
     'group' => 'mailing',
     'name' => 'profile_add_to_group_double_optin',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'default' => '0',
     'add' => '4.1',
-    'title' => 'Enable Double Opt-in for Profile Group(s) field',
+    'title' => ts('Enable Double Opt-in for Profile Group(s) field'),
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => 'When CiviMail is enabled, users who "subscribe" to a group from a profile Group(s) checkbox will receive a confirmation email. They must respond (opt-in) before they are added to the group.',
+    'description' => ts('When CiviMail is enabled, users who "subscribe" to a group from a profile Group(s) checkbox will receive a confirmation email. They must respond (opt-in) before they are added to the group.'),
     'help_text' => NULL,
   ),
   'disable_mandatory_tokens_check' => array(
     'group_name' => 'Mailing Preferences',
     'group' => 'mailing',
     'name' => 'disable_mandatory_tokens_check',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'default' => 0,
     'add' => '4.4',
-    'title' => 'Disable check for mandatory tokens',
+    'title' => ts('Disable check for mandatory tokens'),
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => 'Don\'t check for presence of mandatory tokens (domain address; unsubscribe/opt-out) before sending mailings. WARNING: Mandatory tokens are a safe-guard which facilitate compliance with the US CAN-SPAM Act. They should only be disabled if your organization adopts other mechanisms for compliance or if your organization is not subject to CAN-SPAM.',
+    'description' => ts('Don\'t check for presence of mandatory tokens (domain address; unsubscribe/opt-out) before sending mailings. WARNING: Mandatory tokens are a safe-guard which facilitate compliance with the US CAN-SPAM Act. They should only be disabled if your organization adopts other mechanisms for compliance or if your organization is not subject to CAN-SPAM.'),
     'help_text' => NULL,
   ),
   'dedupe_email_default' => array(
     'group_name' => 'Mailing Preferences',
     'group' => 'mailing',
     'name' => 'dedupe_email_default',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'default' => 1,
     'add' => '4.5',
-    'title' => 'CiviMail dedupes e-mail addresses by default',
+    'title' => ts('CiviMail dedupes e-mail addresses by default'),
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => 'Set the "dedupe e-mail" option when sending a new mailing to "true" by default.',
+    'description' => ts('Set the "dedupe e-mail" option when sending a new mailing to "true" by default.'),
     'help_text' => NULL,
   ),
   'hash_mailing_url' => array(
     'group_name' => 'Mailing Preferences',
     'group' => 'mailing',
     'name' => 'hash_mailing_url',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'default' => 0,
     'add' => '4.5',
-    'title' => 'Hashed Mailing URL\'s',
+    'title' => ts('Hashed Mailing URL\'s'),
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => 'If enabled, a randomized hash key will be used to reference the mailing URL in the mailing.viewUrl token, instead of the mailing ID',
+    'description' => ts('If enabled, a randomized hash key will be used to reference the mailing URL in the mailing.viewUrl token, instead of the mailing ID'),
     'help_text' => NULL,
   ),
   'civimail_multiple_bulk_emails' => array(
     'group_name' => 'Mailing Preferences',
     'group' => 'mailing',
     'name' => 'civimail_multiple_bulk_emails',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'default' => 0,
     'add' => '4.5',
-    'title' => ' Multiple Bulk Emails',
+    'title' => ts('Enable multiple bulk email address for a contact.'),
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => 'If enabled, 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.'),
     'help_text' => NULL,
   ),
   'include_message_id' => array(
     'group_name' => 'Mailing Preferences',
     'group' => 'mailing',
     'name' => 'include_message_id',
-    'type' => 'Integer',
+    'type' => 'Boolean',
     'html_type' => 'checkbox',
     'default' => FALSE,
     'add' => '4.5',
-    'title' => 'Enable CiviMail to generate Message-ID header',
+    'title' => ts('Enable CiviMail to generate Message-ID header'),
     'is_domain' => 1,
     'is_contact' => 0,
     'description' => '',
@@ -306,10 +306,11 @@ return array(
     'group' => 'mailing',
     'name' => 'write_activity_record',
     'type' => 'Boolean',
-    'quick_form_type' => 'YesNo',
+    'html_type' => 'checkbox',
+    'quick_form_type' => 'CheckBox',
     'default' => '1',
     'add' => '4.7',
-    'title' => 'Enable CiviMail to create activities on delivery',
+    'title' => ts('Enable CiviMail to create activities on delivery'),
     'is_domain' => 1,
     'is_contact' => 0,
     'description' => NULL,
@@ -338,13 +339,14 @@ return array(
     'group' => 'mailing',
     'name' => 'auto_recipient_rebuild',
     'type' => 'Boolean',
-    'quick_form_type' => 'YesNo',
+    'html_type' => 'checkbox',
+    'quick_form_type' => 'CheckBox',
     'default' => '1',
-    'title' => 'Enable automatic CiviMail recipient count display',
+    'title' => ts('Enable automatic CiviMail recipient count display'),
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => 'Enable this setting to rebuild recipient list automatically during composing mail. Disable will allow you to rebuild recipient manually.',
-    'help_text' => 'CiviMail automatically fetches recipient list and count whenever mailing groups are included or excluded while composing bulk mail. This phenomena may degrade performance for large sites, so disable this setting to build and fetch recipients for selected groups, manually.',
+    'description' => ts('Enable this setting to rebuild recipient list automatically during composing mail. Disable will allow you to rebuild recipient manually.'),
+    'help_text' => ts('CiviMail automatically fetches recipient list and count whenever mailing groups are included or excluded while composing bulk mail. This phenomena may degrade performance for large sites, so disable this setting to build and fetch recipients for selected groups, manually.'),
   ),
   'allow_mail_from_logged_in_contact' => array(
     'group_name' => 'Mailing Preferences',
index 4a1a3b85a0006bd5f2aa95ce232bc5067b18532b..c7c6bfb04db65d78fcda38d28c8fe3afe887a681 100644 (file)
  *
  * @package CRM
  * @copyright CiviCRM LLC (c) 2004-2017
- * $Id$
- *
  */
-/*
+
+/**
  * Settings metadata file
  */
 
@@ -41,26 +40,30 @@ return array(
     'group_name' => 'Multi Site Preferences',
     'group' => 'multisite',
     'name' => 'is_enabled',
-    'title' => 'Multisite Is enabled',
-    'type' => 'Integer',
+    'title' => ts('Enable Multi Site Configuration'),
+    'html_type' => 'checkbox',
+    'type' => 'Boolean',
     'default' => '0',
     'add' => '4.1',
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => 'Multisite is enabled',
+    'description' => ts('Make CiviCRM aware of multiple domains. You should configure a domain group if enabled'),
+    'help_link' => ['page' => 'Multi Site Installation', 'resource' => 'wiki'],
     'help_text' => NULL,
   ),
   'domain_group_id' => array(
     'group_name' => 'Multi Site Preferences',
     'group' => 'multisite',
     'name' => 'domain_group_id',
-    'title' => 'Multisite Domain Group',
+    'title' => ts('Multisite Domain Group'),
     'type' => 'Integer',
+    'html_type' => 'entity_reference',
+    'entity_reference_options' => ['entity' => 'group', 'select' => array('minimumInputLength' => 0)],
     'default' => '0',
     'add' => '4.1',
     'is_domain' => 1,
     'is_contact' => 0,
-    'description' => NULL,
+    'description' => ts('Contacts created on this site are added to this group'),
     'help_text' => NULL,
   ),
   'event_price_set_domain_id' => array(
index ff22a93292b28fbd0d157ed2721102f66414a470..7b25c00a91f90550a8cb3a91b42938112882b11c 100644 (file)
@@ -61,7 +61,7 @@
                     {if $field.html_type EQ 'checkbox'|| $field.html_type EQ 'checkboxes'}
                         <td class="label"></td>
                         <td>
-                            {$form.$n.html} {$form.$n.label}
+                            {$form.$n.html}
                             {if $field.description}
                                 <br /><span class="description">{$field.description}</span>
                             {/if}
index 96c380d3dc69078619c42664590e49afe2b49678..393d6f0cf993fbe986d19b39293b07aa56357a2f 100644 (file)
@@ -175,7 +175,7 @@ class CRM_Contact_BAO_QueryTest extends CiviUnitTestCase {
         0 => array(
           0 => 'email',
           1 => 'LIKE',
-          2 => 'secondary@example.com',
+          2 => 'sEcondary@example.com',
           3 => 0,
           4 => 1,
         ),
@@ -223,8 +223,8 @@ class CRM_Contact_BAO_QueryTest extends CiviUnitTestCase {
     CRM_Core_Config::singleton()->defaultSearchProfileID = 1;
     $this->callAPISuccess('address', 'create', array(
         'contact_id' => $contactID,
-        'city' => 'Cool City',
-        'street_address' => 'Long Street',
+        'city' => 'Cool CITY',
+        'street_address' => 'Long STREET',
         'location_type_id' => 1,
       ));
     $returnProperties = array(
@@ -257,7 +257,7 @@ class CRM_Contact_BAO_QueryTest extends CiviUnitTestCase {
   public function getSearchProfileData() {
     return [
       [
-        [['city', '=', 'Cool City', 1, 0]], "civicrm_address.city as `city`", "LOWER(civicrm_address.city) = 'cool city'",
+        [['city', '=', 'Cool City', 1, 0]], "civicrm_address.city as `city`", "civicrm_address.city = 'cool city'",
       ],
       [
         // Note that in the query 'long street' is lower cased. We eventually want to change that & not mess with the vars - it turns out
index 23be104e54437197757f323dd5cbafac1cab9d75..b653f8f07c93d1d5b984f78d60c7e89f3b5141a7 100644 (file)
@@ -223,6 +223,35 @@ class CRM_Core_BAO_ActionScheduleTest extends CiviUnitTestCase {
       'start_action_unit' => 'week',
       'subject' => 'subject sched_membership_join_2week (joined {membership.join_date})',
     );
+    $this->fixtures['sched_membership_start_1week'] = array(
+      'name' => 'sched_membership_start_1week',
+      'title' => 'sched_membership_start_1week',
+      'absolute_date' => '',
+      'body_html' => '<p>body sched_membership_start_1week</p>',
+      'body_text' => 'body sched_membership_start_1week',
+      'end_action' => '',
+      'end_date' => '',
+      'end_frequency_interval' => '',
+      'end_frequency_unit' => '',
+      'entity_status' => '',
+      'entity_value' => '',
+      'group_id' => '',
+      'is_active' => 1,
+      'is_repeat' => '0',
+      'mapping_id' => 4,
+      'msg_template_id' => '',
+      'recipient' => '',
+      'recipient_listing' => '',
+      'recipient_manual' => '',
+      'record_activity' => 1,
+      'repetition_frequency_interval' => '',
+      'repetition_frequency_unit' => '',
+      'start_action_condition' => 'after',
+      'start_action_date' => 'membership_start_date',
+      'start_action_offset' => '1',
+      'start_action_unit' => 'week',
+      'subject' => 'subject sched_membership_start_1week (joined {membership.start_date})',
+    );
     $this->fixtures['sched_membership_end_2week'] = array(
       'name' => 'sched_membership_end_2week',
       'title' => 'sched_membership_end_2week',
@@ -969,39 +998,57 @@ class CRM_Core_BAO_ActionScheduleTest extends CiviUnitTestCase {
    * an email should be sent.
    */
   public function testMembershipDateMatch() {
-    foreach (['membership_join_date', 'membership_start_date'] as $date) {
-      $membership = $this->createTestObject('CRM_Member_DAO_Membership', array_merge($this->fixtures['rolling_membership'], array('status_id' => 1)));
-      $this->assertTrue(is_numeric($membership->id));
-      $result = $this->callAPISuccess('Email', 'create', array(
-        'contact_id' => $membership->contact_id,
-        'email' => 'test-member@example.com',
-        'location_type_id' => 1,
-      ));
-      $this->assertAPISuccess($result);
+    $membership = $this->createTestObject('CRM_Member_DAO_Membership', array_merge($this->fixtures['rolling_membership'], array('status_id' => 1)));
+    $this->assertTrue(is_numeric($membership->id));
+    $result = $this->callAPISuccess('Email', 'create', array(
+      'contact_id' => $membership->contact_id,
+      'email' => 'test-member@example.com',
+      'location_type_id' => 1,
+      'is_primary' => 1,
+    ));
 
-      $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], array('contact_id' => $membership->contact_id)));
-      $actionSchedule = $this->fixtures['sched_membership_join_2week'];
-      $actionSchedule['start_action_date'] = $date;
-      $actionSchedule['entity_value'] = $membership->membership_type_id;
-      $actionScheduleDao = CRM_Core_BAO_ActionSchedule::add($actionSchedule);
-      $this->assertTrue(is_numeric($actionScheduleDao->id));
+    $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], array('contact_id' => $membership->contact_id)));
+    $actionSchedule = $this->fixtures['sched_membership_join_2week'];
+    $actionSchedule['entity_value'] = $membership->membership_type_id;
+    $actionScheduleDao = CRM_Core_BAO_ActionSchedule::add($actionSchedule);
+    $this->assertTrue(is_numeric($actionScheduleDao->id));
 
-      // start_date=2012-03-15 ; schedule is 2 weeks after start_date
-      $this->assertCronRuns(array(
-        array(
-          // Before the 2-week mark, no email.
-          'time' => '2012-03-28 01:00:00',
-          'recipients' => array(),
-          'subjects' => array(),
-        ),
-        array(
-          // After the 2-week mark, send an email.
-          'time' => '2012-03-29 01:00:00',
-          'recipients' => array(array('test-member@example.com')),
-          'subjects' => array('subject sched_membership_join_2week (joined March 15th, 2012)'),
-        ),
-      ));
-    }
+    // start_date=2012-03-15 ; schedule is 2 weeks after join_date
+    $this->assertCronRuns(array(
+      array(
+        // Before the 2-week mark, no email.
+        'time' => '2012-03-28 01:00:00',
+        'recipients' => array(),
+        'subjects' => array(),
+      ),
+      array(
+        // After the 2-week mark, send an email.
+        'time' => '2012-03-29 01:00:00',
+        'recipients' => array(array('test-member@example.com')),
+        'subjects' => array('subject sched_membership_join_2week (joined March 15th, 2012)'),
+      ),
+    ));
+
+    $actionSchedule = $this->fixtures['sched_membership_start_1week'];
+    $actionSchedule['entity_value'] = $membership->membership_type_id;
+    $actionScheduleDao = CRM_Core_BAO_ActionSchedule::add($actionSchedule);
+    $this->assertTrue(is_numeric($actionScheduleDao->id));
+
+    // start_date=2012-03-15 ; schedule is 1 weeks after start_date
+    $this->assertCronRuns(array(
+      array(
+        // Before the 2-week mark, no email.
+        'time' => '2012-03-21 01:00:00',
+        'recipients' => array(),
+        'subjects' => array(),
+      ),
+      array(
+        // After the 2-week mark, send an email.
+        'time' => '2012-03-22 01:00:00',
+        'recipients' => array(array('test-member@example.com')),
+        'subjects' => array('subject sched_membership_start_1week (joined March 15th, 2012)'),
+      ),
+    ));
   }
 
 
@@ -2005,6 +2052,123 @@ class CRM_Core_BAO_ActionScheduleTest extends CiviUnitTestCase {
     }
   }
 
+  /**
+   * Inherited members without permission to edit the main member contact should
+   * not get reminders.
+   *
+   * However, just because a contact inherits one membership doesn't mean
+   * reminders for other memberships should be suppressed.
+   *
+   * See CRM-14098
+   */
+  public function testInheritedMembershipPermissions() {
+    // Set up common parameters for memberships.
+    $membershipParams = $this->fixtures['rolling_membership'];
+    $membershipParams['status_id'] = 1;
+
+    $membershipParams['membership_type_id']['relationship_type_id'] = 1;
+    $membershipParams['membership_type_id']['relationship_direction'] = 'b_a';
+    $membershipType1 = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipParams['membership_type_id']);
+
+    // We'll create a new membership type that can be held at the same time as
+    // the first one.
+    $membershipParams['membership_type_id']['relationship_type_id'] = 'NULL';
+    $membershipParams['membership_type_id']['relationship_direction'] = 'NULL';
+    $membershipType2 = $this->createTestObject('CRM_Member_DAO_MembershipType', $membershipParams['membership_type_id']);
+
+    // Create the parent membership and contact
+    $membershipParams['membership_type_id'] = $membershipType1->id;
+    $mainMembership = $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
+
+    $contactParams = [
+      'contact_type' => 'Individual',
+      'first_name' => 'Mom',
+      'last_name' => 'Rel',
+      'is_deceased' => 0,
+    ];
+    $this->createTestObject('CRM_Contact_DAO_Contact', array_merge($contactParams, ['id' => $mainMembership->contact_id]));
+
+    $emailParams = [
+      'contact_id' => $mainMembership->contact_id,
+      'email' => 'test-member@example.com',
+      'location_type_id' => 1,
+      'is_primary' => 1,
+    ];
+    $email = $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
+
+    // Set up contacts and emails for the two children
+    $contactParams['first_name'] = 'Favorite';
+    $permChild = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
+    $emailParams['email'] = 'favorite@example.com';
+    $emailParams['contact_id'] = $permChild->id;
+    $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
+
+    $contactParams['first_name'] = 'Black Sheep';
+    $nonPermChild = $this->createTestObject('CRM_Contact_DAO_Contact', $contactParams);
+    $emailParams['email'] = 'black.sheep@example.com';
+    $emailParams['contact_id'] = $nonPermChild->id;
+    $this->createTestObject('CRM_Core_DAO_Email', $emailParams);
+
+    // Each child gets a relationship, one with permission to edit the parent.  This
+    // will trigger inherited memberships for the first membership type
+    $relParams = [
+      'relationship_type_id' => 1,
+      'contact_id_a' => $nonPermChild->id,
+      'contact_id_b' => $mainMembership->contact_id,
+      'is_active' => 1,
+    ];
+    $this->callAPISuccess('relationship', 'create', $relParams);
+
+    $relParams['contact_id_a'] = $permChild->id;
+    $relParams['is_permission_a_b'] = CRM_Contact_BAO_Relationship::EDIT;
+    $this->callAPISuccess('relationship', 'create', $relParams);
+
+    // Mom and Black Sheep get their own memberships of the second type.
+    $membershipParams['membership_type_id'] = $membershipType2->id;
+    $membershipParams['owner_membership_id'] = 'NULL';
+    $membershipParams['contact_id'] = $mainMembership->contact_id;
+    $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
+
+    $membershipParams['contact_id'] = $nonPermChild->id;
+    $this->createTestObject('CRM_Member_DAO_Membership', $membershipParams);
+
+    // Test a reminder for the first membership type - that should exclude Black
+    // Sheep.
+    $actionSchedule = $this->fixtures['sched_membership_join_2week'];
+    $actionSchedule['entity_value'] = $membershipType1->id;
+    $actionScheduleDao = CRM_Core_BAO_ActionSchedule::add($actionSchedule);
+    $this->assertTrue(is_numeric($actionScheduleDao->id));
+
+    $this->assertCronRuns([
+      [
+        'time' => '2012-03-29 01:00:00',
+        'recipients' => [['test-member@example.com'], ['favorite@example.com']],
+        'subjects' => [
+          'subject sched_membership_join_2week (joined March 15th, 2012)',
+          'subject sched_membership_join_2week (joined March 15th, 2012)',
+        ],
+      ],
+    ]);
+
+    // Test a reminder for the second membership type - that should include
+    // Black Sheep.
+    $actionSchedule = $this->fixtures['sched_membership_start_1week'];
+    $actionSchedule['entity_value'] = $membershipType2->id;
+    $actionScheduleDao = CRM_Core_BAO_ActionSchedule::add($actionSchedule);
+    $this->assertTrue(is_numeric($actionScheduleDao->id));
+
+    $this->assertCronRuns([
+      [
+        'time' => '2012-03-22 01:00:00',
+        'recipients' => [['test-member@example.com'], ['black.sheep@example.com']],
+        'subjects' => [
+          'subject sched_membership_start_1week (joined March 15th, 2012)',
+          'subject sched_membership_start_1week (joined March 15th, 2012)',
+        ],
+      ],
+    ]);
+  }
+
   public function createModifiedDateTime($origDateTime, $modifyRule) {
     $newDateTime = clone($origDateTime);
     $newDateTime->modify($modifyRule);
index cea24c8dcac36291d3c8d63c22beb59bdf42e629..ff368a31d79d12a4765ff5000dbd20bb51d68769 100644 (file)
@@ -419,6 +419,51 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
     $this->assertEquals('Big campaign,', CRM_Core_DAO::singleValueQuery("SELECT GROUP_CONCAT(contribution_campaign_title) FROM {$tableName}"));
   }
 
+  /**
+   * Test exporting relationships.
+   */
+  public function testExportRelationships() {
+    $organization1 = $this->organizationCreate(['organization_name' => 'Org 1', 'legal_name' => 'pretty legal', 'contact_source' => 'friend who took a law paper once']);
+    $organization2 = $this->organizationCreate(['organization_name' => 'Org 2', 'legal_name' => 'well dodgey']);
+    $contact1 = $this->individualCreate(['employer_id' => $organization1, 'first_name' => 'one']);
+    $contact2 = $this->individualCreate(['employer_id' => $organization2, 'first_name' => 'one']);
+    $employerRelationshipTypeID = $this->callAPISuccessGetValue('RelationshipType', ['return' => 'id', 'label_a_b' => 'Employee of']);
+    $selectedFields = [
+      ['Individual', 'first_name', ''],
+      ['Individual', $employerRelationshipTypeID . '_a_b', 'organization_name', ''],
+      ['Individual', $employerRelationshipTypeID . '_a_b', 'legal_name', ''],
+      ['Individual', $employerRelationshipTypeID . '_a_b', 'contact_source', ''],
+    ];
+    list($tableName, $sqlColumns, $headerRows) = CRM_Export_BAO_Export::exportComponents(
+      FALSE,
+      [$contact1, $contact2],
+      [],
+      NULL,
+      $selectedFields,
+      NULL,
+      CRM_Export_Form_Select::CONTACT_EXPORT,
+      "contact_a.id IN ( $contact1, $contact2 )",
+      NULL,
+      FALSE,
+      FALSE,
+      [
+        'exportOption' => CRM_Export_Form_Select::CONTACT_EXPORT,
+        'suppress_csv_for_testing' => TRUE,
+      ]
+    );
+
+    $dao = CRM_Core_DAO::executeQuery("SELECT * FROM {$tableName}");
+    $dao->fetch();
+    $this->assertEquals('one', $dao->first_name);
+    $this->assertEquals('Org 1', $dao->{$employerRelationshipTypeID . '_a_b_organization_name'});
+    $this->assertEquals('pretty legal', $dao->{$employerRelationshipTypeID . '_a_b_legal_name'});
+    $this->assertEquals('friend who took a law paper once', $dao->{$employerRelationshipTypeID . '_a_b_contact_source'});
+
+    $dao->fetch();
+    $this->assertEquals('Org 2', $dao->{$employerRelationshipTypeID . '_a_b_organization_name'});
+    $this->assertEquals('well dodgey', $dao->{$employerRelationshipTypeID . '_a_b_legal_name'});
+  }
+
   /**
    * Test exporting relationships.
    *
@@ -432,6 +477,7 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
       ['Individual', $houseHoldTypeID . '_a_b', 'city', ''],
       ['Individual', 'city', ''],
       ['Individual', 'state_province', ''],
+      ['Individual', 'contact_source', ''],
     ];
     list($tableName, $sqlColumns, $headerRows) = CRM_Export_BAO_Export::exportComponents(
       FALSE,
@@ -456,18 +502,21 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
       $this->assertEquals('ME', $dao->state_province);
       $this->assertEquals($householdID, $dao->civicrm_primary_id);
       $this->assertEquals($householdID, $dao->civicrm_primary_id);
+      $this->assertEquals('household sauce', $dao->contact_source);
     }
 
     $this->assertEquals([
       0 => 'City',
       1 => 'State',
-      2 => 'Household ID',
+      2 => 'Contact Source',
+      3 => 'Household ID',
     ], $headerRows);
     $this->assertEquals(
       [
         'city' => 'city varchar(64)',
         'state_province' => 'state_province varchar(64)',
         'civicrm_primary_id' => 'civicrm_primary_id varchar(16)',
+        'contact_source' => 'contact_source varchar(255)',
       ], $sqlColumns);
   }
 
@@ -956,6 +1005,7 @@ class CRM_Export_BAO_ExportTest extends CiviUnitTestCase {
   protected function setUpHousehold() {
     $this->setUpContactExportData();
     $householdID = $this->householdCreate([
+      'source' => 'household sauce',
       'api.Address.create' => [
         'city' => 'Portland',
         'state_province_id' => 'Maine',
index 3e2e9b0c2622d6a785a499df23d16071d68fcafa..ebef36284e3c8d7ce55dba470d6e4eb9acb52db8 100644 (file)
@@ -396,6 +396,22 @@ class api_v3_ContactTest extends CiviUnitTestCase {
     $this->getAndCheck($params, $contact['id'], 'contact');
   }
 
+  /**
+   * Test that name searches are case insensitive.
+   */
+  public function testGetNameVariantsCaseInsensitive() {
+    $this->callAPISuccess('contact', 'create', [
+      'display_name' => 'Abc1',
+      'contact_type' => 'Individual',
+    ]);
+    $this->callAPISuccessGetSingle('Contact', ['display_name' => 'aBc1']);
+    $this->callAPISuccessGetSingle('Contact', ['sort_name' => 'aBc1']);
+    Civi::settings()->set('includeNickNameInName', TRUE);
+    $this->callAPISuccessGetSingle('Contact', ['display_name' => 'aBc1']);
+    $this->callAPISuccessGetSingle('Contact', ['sort_name' => 'aBc1']);
+    Civi::settings()->set('includeNickNameInName', FALSE);
+  }
+
   /**
    * Test old keys still work.
    *
@@ -3183,7 +3199,7 @@ class api_v3_ContactTest extends CiviUnitTestCase {
    * CRM-15443 - ensure getlist api does not return deleted contacts.
    */
   public function testGetlistExcludeConditions() {
-    $name = md5(time());
+    $name = 'Scarabée';
     $contact = $this->individualCreate(array('last_name' => $name));
     $this->individualCreate(array('last_name' => $name, 'is_deceased' => 1));
     $this->individualCreate(array('last_name' => $name, 'is_deleted' => 1));