Merge pull request #14254 from yashodha/add_dev_tab
authorEileen McNaughton <eileen@mcnaughty.com>
Mon, 20 May 2019 22:03:59 +0000 (10:03 +1200)
committerGitHub <noreply@github.com>
Mon, 20 May 2019 22:03:59 +0000 (10:03 +1200)
add developer tab for summary report

35 files changed:
CRM/Admin/Form/SettingTrait.php
CRM/Contact/BAO/Contact.php
CRM/Contribute/BAO/Contribution.php
CRM/Contribute/BAO/Widget.php
CRM/Core/BAO/Address.php
CRM/Core/BAO/CustomField.php
CRM/Core/BAO/CustomQuery.php
CRM/Core/BAO/PrevNextCache.php
CRM/Core/Page/File.php
CRM/Core/Payment/Elavon.php
CRM/Dedupe/Merger.php
CRM/Event/Page/ManageEvent.php
CRM/Financial/BAO/Payment.php
CRM/Utils/AutoClean.php
CRM/Utils/File.php
CRM/Utils/Rule.php
Civi/Core/SettingsMetadata.php
LICENSE [moved from agpl-3.0.txt with 100% similarity]
api/v3/Generic.php
api/v3/Setting.php
bower.json
composer.json
composer.lock
install/index.php
release-notes.md
release-notes/5.13.3.md
release-notes/5.13.4.md [new file with mode: 0644]
settings/Core.setting.php
tests/phpunit/CRM/Dedupe/MergerTest.php
tests/phpunit/CRM/Event/BAO/AdditionalPaymentTest.php
tests/phpunit/CRM/Event/BAO/ChangeFeeSelectionTest.php
tests/phpunit/CRM/Utils/FileTest.php
tests/phpunit/CiviTest/CiviUnitTestCase.php
tests/phpunit/api/v3/AddressTest.php
tests/phpunit/api/v3/SettingTest.php

index f22470803fe66521fcd3ad141c6d8a02bfce1c92..308d409e7ea5b0b69351811740f0dc4a8f7e5a38 100644 (file)
@@ -66,8 +66,7 @@ trait CRM_Admin_Form_SettingTrait {
    */
   protected function getSettingsMetaData() {
     if (empty($this->settingsMetadata)) {
-      $allSettingMetaData = civicrm_api3('setting', 'getfields', []);
-      $this->settingsMetadata = array_intersect_key($allSettingMetaData['values'], $this->_settings);
+      $this->settingsMetadata = \Civi\Core\SettingsMetadata::getMetadata(['name' => array_keys($this->_settings)], NULL, TRUE);
       // This array_merge re-orders to the key order of $this->_settings.
       $this->settingsMetadata = array_merge($this->_settings, $this->settingsMetadata);
     }
@@ -174,10 +173,7 @@ trait CRM_Admin_Form_SettingTrait {
       $quickFormType = $this->getQuickFormType($props);
       if (isset($quickFormType)) {
         $options = CRM_Utils_Array::value('options', $props);
-        if (isset($props['pseudoconstant'])) {
-          $options = civicrm_api3('Setting', 'getoptions', [
-            'field' => $setting,
-          ])['values'];
+        if ($options) {
           if ($props['html_type'] === 'Select' && isset($props['is_required']) && $props['is_required'] === FALSE && !isset($options[''])) {
             // If the spec specifies the field is not required add a null option.
             // Why not if empty($props['is_required']) - basically this has been added to the spec & might not be set to TRUE
index 7280f6d5c75bd9aace4f860d8189a9743130c690..5a22a86d7850614dd166ec3e224723876f565e74 100644 (file)
@@ -1736,7 +1736,7 @@ WHERE     civicrm_contact.id = " . CRM_Utils_Type::escape($id, 'Integer');
    * @return array
    *   Contact details
    */
-  public static function getHierContactDetails($contactId, &$fields) {
+  public static function getHierContactDetails($contactId, $fields) {
     $params = array(array('contact_id', '=', $contactId, 0, 0));
     $options = array();
 
@@ -2073,7 +2073,7 @@ ORDER BY civicrm_email.is_primary DESC";
    */
   public static function formatProfileContactParams(
     &$params,
-    &$fields,
+    $fields,
     $contactID = NULL,
     $ufGroupId = NULL,
     $ctype = NULL,
index d5f7a0b76e0c8a0b379ba2a67e308960bafeaa34..8f1fd8db46e51045e8cf1b3fbaaefcc41c151237 100644 (file)
@@ -3591,8 +3591,12 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
     ) {
       return;
     }
-    if ((($previousContributionStatus == 'Partially paid'
-      && $currentContributionStatus == 'Completed')
+    // The 'right' way to add payments or refunds is through the Payment.create api. That api
+    // then updates the contribution but this process shoud not also record another financial trxn.
+    if ((($previousContributionStatus == 'Partially paid' && $currentContributionStatus == 'Completed')
+      || ($previousContributionStatus == 'Pending refund' && $currentContributionStatus == 'Completed')
+      // This concept of pay_later as different to any other sort of pending is deprecated & it's unclear
+      // why it is here or where it is handled instead.
       || ($previousContributionStatus == 'Pending' && $params['prevContribution']->is_pay_later == TRUE
       && $currentContributionStatus == 'Partially paid'))
       && $context == 'changedStatus'
index 45ecc4116cf8a89024050c2906b22a52a95ffb55..e4bff0b4ffec2548eb01f5e4f8ef87666d900e8b 100644 (file)
@@ -127,6 +127,7 @@ class CRM_Contribute_BAO_Widget extends CRM_Contribute_DAO_Widget {
         $now = time();
         if ($dao->start_date) {
           $startDate = CRM_Utils_Date::unixTime($dao->start_date);
+          $data['start_date'] = $dao->start_date;
           if ($startDate && $startDate >= $now) {
             $data['is_active'] = FALSE;
             $data['campaign_start'] = ts('Campaign starts on %1', [
@@ -137,6 +138,7 @@ class CRM_Contribute_BAO_Widget extends CRM_Contribute_DAO_Widget {
 
         if ($dao->end_date) {
           $endDate = CRM_Utils_Date::unixTime($dao->end_date);
+          $data['end_date'] = $dao->end_date;
           if ($endDate &&
             $endDate < $now
           ) {
index 3ce2b0cdbb2bd3fb783a44241d4e601cb4b78627..cbe0066deca007b27622f88ec14cfc62b5394c10 100644 (file)
@@ -1318,6 +1318,9 @@ SELECT is_primary,
           }
         }
         if (!empty($props['country_id'])) {
+          if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', (array) $props['country_id']))) {
+            throw new CRM_Core_Exception(ts('Province limit or default country setting is incorrect'));
+          }
           $params['condition'] = 'country_id IN (' . implode(',', (array) $props['country_id']) . ')';
         }
         break;
@@ -1330,6 +1333,9 @@ SELECT is_primary,
         if ($context != 'get' && $context != 'validate') {
           $config = CRM_Core_Config::singleton();
           if (!empty($config->countryLimit) && is_array($config->countryLimit)) {
+            if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', $config->countryLimit))) {
+              throw new CRM_Core_Exception(ts('Available Country setting is incorrect'));
+            }
             $params['condition'] = 'id IN (' . implode(',', $config->countryLimit) . ')';
           }
         }
@@ -1338,6 +1344,9 @@ SELECT is_primary,
       // Filter county list based on chosen state
       case 'county_id':
         if (!empty($props['state_province_id'])) {
+          if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', (array) $props['state_province_id']))) {
+            throw new CRM_Core_Exception(ts('Can only accept Integers for state_province_id filtering'));
+          }
           $params['condition'] = 'state_province_id IN (' . implode(',', (array) $props['state_province_id']) . ')';
         }
         break;
index 19f1ce3bf50789d406807e0ee5ee85bfce13f2de..5c49f22862c377d692cca10a9a71df7715fa96f8 100644 (file)
@@ -599,7 +599,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
         if (!empty($customDataSubType)) {
           $subtypeClause = array();
           foreach ($customDataSubType as $subtype) {
-            $subtype = CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR;
+            $subtype = CRM_Core_DAO::VALUE_SEPARATOR . CRM_Utils_Type::escape($subtype, 'String') . CRM_Core_DAO::VALUE_SEPARATOR;
             $subtypeClause[] = "$cgTable.extends_entity_column_value LIKE '%{$subtype}%'";
           }
           if (!$onlySubType) {
index 790487e43891b62c2bad6ab03afddb2d63310603..24b829e5f5b427660de3f00e12a02a73f3b8fd72 100644 (file)
@@ -351,6 +351,12 @@ SELECT f.id, f.label, f.data_type,
                 foreach ($value as $key => $val) {
                   $value[$key] = str_replace(['[', ']', ','], ['\[', '\]', '[:comma:]'], $val);
                   $value[$key] = str_replace('|', '[:separator:]', $value[$key]);
+                  if ($field['data_type'] == 'String') {
+                    $value[$key] = CRM_Utils_Type::escape($value[$key], 'String');
+                  }
+                  elseif ($value) {
+                    $value[$key] = CRM_Utils_Type::escape($value[$key], 'Integer');
+                  }
                 }
                 $value = implode(',', $value);
               }
index bca6a1824bb2d9c9a21ca48d266446fc9113578d..bdc8e44ba131789db47434a891c315add1098a43 100644 (file)
@@ -138,19 +138,18 @@ WHERE  cacheKey     = %3 AND
   }
 
   /**
-   * Delete from the previous next cache table for a pair of ids.
+   * Delete pair from the previous next cache table to remove it from further merge consideration.
+   *
+   * The pair may have been flipped, so make sure we delete using both orders
    *
    * @param int $id1
    * @param int $id2
    * @param string $cacheKey
-   * @param bool $isViceVersa
-   * @param string $entityTable
    */
-  public static function deletePair($id1, $id2, $cacheKey = NULL, $isViceVersa = FALSE, $entityTable = 'civicrm_contact') {
-    $sql = "DELETE FROM civicrm_prevnext_cache WHERE  entity_table = %1";
-    $params = [1 => [$entityTable, 'String']];
+  public static function deletePair($id1, $id2, $cacheKey = NULL) {
+    $sql = "DELETE FROM civicrm_prevnext_cache WHERE  entity_table = 'civicrm_contact'";
 
-    $pair = !$isViceVersa ? "entity_id1 = %2 AND entity_id2 = %3" : "(entity_id1 = %2 AND entity_id2 = %3) OR (entity_id1 = %3 AND entity_id2 = %2)";
+    $pair = "(entity_id1 = %2 AND entity_id2 = %3) OR (entity_id1 = %3 AND entity_id2 = %2)";
     $sql .= " AND ( {$pair} )";
     $params[2] = [$id1, 'Integer'];
     $params[3] = [$id2, 'Integer'];
index 362e3d1616869af1ea9b9562eb66e4e3ee99fc1a..c1f6cd10bf080df476b3f6ec9ad2d91a63318213 100644 (file)
@@ -68,12 +68,25 @@ class CRM_Core_Page_File extends CRM_Core_Page {
       $mimeType = '';
       $path = CRM_Core_Config::singleton()->customFileUploadDir . $fileName;
     }
-    $mimeType = CRM_Utils_Request::retrieveValue('mime-type', 'String', $mimeType, FALSE);
 
     if (!$path) {
       CRM_Core_Error::statusBounce('Could not retrieve the file');
     }
 
+    if (empty($mimeType)) {
+      $passedInMimeType = self::convertBadMimeAliasTypes(CRM_Utils_Request::retrieveValue('mime-type', 'String', $mimeType, FALSE));
+      if (!in_array($passedInMimeType, explode(',', Civi::settings()->get('requestableMimeTypes')))) {
+        throw new CRM_Core_Exception("Supplied mime-type is not accepted");
+      }
+      $extension = CRM_Utils_File::getExtensionFromPath($path);
+      $candidateExtensions = CRM_Utils_File::getAcceptableExtensionsForMimeType($passedInMimeType);
+      if (!in_array($extension, $candidateExtensions)) {
+        throw new CRM_Core_Exception("Supplied mime-type does not match file extension");
+      }
+      // Now that we have validated mime-type supplied as much as possible lets now set the MimeType variable/
+      $mimeType = $passedInMimeType;
+    }
+
     $buffer = file_get_contents($path);
     if (!$buffer) {
       CRM_Core_Error::statusBounce('The file is either empty or you do not have permission to retrieve the file');
@@ -101,4 +114,33 @@ class CRM_Core_Page_File extends CRM_Core_Page {
     }
   }
 
+  /**
+   * Translate one mime type to another.
+   *
+   * Certain non-standard/weird MIME types have been common. Unfortunately, because
+   * of the way this controller is used, the weird types may baked-into URLs.
+   * We clean these up for compatibility.
+   *
+   * @param string $type
+   *   Ex: 'image/jpg'
+   * @return string
+   *   Ex: 'image/jpeg'.
+   */
+  protected static function convertBadMimeAliasTypes($type) {
+    $badTypes = [
+      // Before PNG format was ubiquitous, it was image/x-png?
+      'image/x-png' => 'image/png',
+
+      // People see "image/gif" and "image/png" and wrongly guess "image/jpg"?
+      'image/jpg' => 'image/jpeg',
+      'image/tif' => 'image/tiff',
+      'image/svg' => 'image/svg+xml',
+
+      // StackExchange attributes "pjpeg" to some quirk in an old version of IE?
+      'image/pjpeg' => 'image/jpeg',
+
+    ];
+    return isset($badTypes[$type]) ? $badTypes[$type] : $type;
+  }
+
 }
index 7b91ac425198f3760bca857d89dc59e636106ce1..8d9df671c4dae8634edd2a8aeafe0bdeef0c6f61 100644 (file)
@@ -343,7 +343,13 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
 
     $xml = '<txn>';
     foreach ($requestFields as $key => $value) {
-      $xml .= '<' . $key . '>' . self::tidyStringforXML($value, $xmlFieldLength[$key]) . '</' . $key . '>';
+      //dev/core/966 Don't send email through the urlencode.
+      if ($key == 'ssl_email') {
+        $xml .= '<' . $key . '>' . substr($value, 0, $xmlFieldLength[$key]) . '</' . $key . '>';
+      }
+      else {
+        $xml .= '<' . $key . '>' . self::tidyStringforXML($value, $xmlFieldLength[$key]) . '</' . $key . '>';
+      }
     }
     $xml .= '</txn>';
     return $xml;
index 6703e57a7cda0e1c7a73a94f5cce7343c2a497ac..7e713da3a6e7d5d7f6ab68f0b7777aad2bdf1908 100644 (file)
@@ -1080,8 +1080,6 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
 
     $main = self::getMergeContactDetails($mainId);
     $other = self::getMergeContactDetails($otherId);
-    $specialValues['main'] = self::getSpecialValues($main);
-    $specialValues['other'] = self::getSpecialValues($other);
 
     $compareFields = self::retrieveFields($main, $other);
 
@@ -1092,14 +1090,22 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
         // CRM-15681 don't display sub-types in UI
         continue;
       }
-      foreach (['main', 'other'] as $moniker) {
-        $contact = &$$moniker;
-        $value = CRM_Utils_Array::value($field, $contact);
-        if (isset($specialValues[$moniker][$field]) && is_string($specialValues[$moniker][$field])) {
-          $value = CRM_Core_DAO::VALUE_SEPARATOR . trim($specialValues[$moniker][$field], CRM_Core_DAO::VALUE_SEPARATOR) . CRM_Core_DAO::VALUE_SEPARATOR;
+      foreach (['main' => $main, 'other' => $other] as $moniker => $contact) {
+        $value = $label = CRM_Utils_Array::value($field, $contact);
+        $fieldSpec = $fields[$field];
+        if (!empty($fieldSpec['serialize']) && is_array($value)) {
+          // In practice this only applies to preferred_communication_method as the sub types are skipped above
+          // and no others are serialized.
+          $labels = [];
+          foreach ($value as $individualValue) {
+            $labels[] = CRM_Core_PseudoConstant::getLabel('CRM_Contact_BAO_Contact', $field, $individualValue);
+          }
+          $label = implode(', ', $labels);
+          // We serialize this due to historic handling but it's likely that if we just left it as an
+          // array all would be well & we would have less code.
+          $value = CRM_Core_DAO::serializeField($value, $fieldSpec['serialize']);
         }
-        $label = isset($specialValues[$moniker]["{$field}_display"]) ? $specialValues[$moniker]["{$field}_display"] : $value;
-        if (!empty($fields[$field]['type']) && $fields[$field]['type'] == CRM_Utils_Type::T_DATE) {
+        elseif (!empty($fieldSpec['type']) && $fieldSpec['type'] == CRM_Utils_Type::T_DATE) {
           if ($value) {
             $value = str_replace('-', '', $value);
             $label = CRM_Utils_Date::customFormat($label);
@@ -1116,15 +1122,8 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
             $label = ts('[x]');
           }
         }
-        elseif ($field == 'prefix_id') {
-          $label = CRM_Utils_Array::value('individual_prefix', $contact);
-        }
-        elseif ($field == 'suffix_id') {
-          $label = CRM_Utils_Array::value('individual_suffix', $contact);
-        }
-        elseif ($field == 'gender_id' && !empty($value)) {
-          $genderOptions = civicrm_api3('contact', 'getoptions', ['field' => 'gender_id']);
-          $label = $genderOptions['values'][$value];
+        elseif (!empty($fieldSpec['pseudoconstant'])) {
+          $label = CRM_Core_PseudoConstant::getLabel('CRM_Contact_BAO_Contact', $field, $value);
         }
         elseif ($field == 'current_employer_id' && !empty($value)) {
           $label = "$value (" . CRM_Contact_BAO_Contact::displayName($value) . ")";
@@ -1882,38 +1881,6 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
     return $cacheKeyString;
   }
 
-  /**
-   * @param array $contact
-   * @return array
-   *   $specialValues
-   */
-  public static function getSpecialValues($contact) {
-    $preferred_communication_method = CRM_Utils_Array::value('preferred_communication_method', $contact);
-    $value = empty($preferred_communication_method) ? [] : $preferred_communication_method;
-    $specialValues = [
-      'preferred_communication_method' => $value,
-      'communication_style_id' => $value,
-    ];
-
-    if (!empty($contact['preferred_communication_method'])) {
-      // api 3 returns pref_comm_method as an array, which breaks the lookup; so we reconstruct
-      $prefCommList = is_array($specialValues['preferred_communication_method']) ? implode(CRM_Core_DAO::VALUE_SEPARATOR, $specialValues['preferred_communication_method']) : $specialValues['preferred_communication_method'];
-      $specialValues['preferred_communication_method'] = CRM_Core_DAO::VALUE_SEPARATOR . $prefCommList . CRM_Core_DAO::VALUE_SEPARATOR;
-    }
-    $names = [
-      'preferred_communication_method' => [
-        'newName' => 'preferred_communication_method_display',
-        'groupName' => 'preferred_communication_method',
-      ],
-    ];
-    CRM_Core_OptionGroup::lookupValues($specialValues, $names);
-
-    if (!empty($contact['communication_style'])) {
-      $specialValues['communication_style_id_display'] = $contact['communication_style'];
-    }
-    return $specialValues;
-  }
-
   /**
    * Get the metadata for the merge fields.
    *
@@ -2155,9 +2122,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
       CRM_Core_BAO_PrevNextCache::markConflict($mainId, $otherId, $cacheKeyString, $conflicts);
     }
     else {
-      // delete entry from PrevNextCache table so we don't consider the pair next time
-      // pair may have been flipped, so make sure we delete using both orders
-      CRM_Core_BAO_PrevNextCache::deletePair($mainId, $otherId, $cacheKeyString, TRUE);
+      CRM_Core_BAO_PrevNextCache::deletePair($mainId, $otherId, $cacheKeyString);
     }
   }
 
index 1a316ef657d658c3e80d48808d25305de115cce2..c2b0a3a55025969a6986d758409adeeb29fb4c4c 100644 (file)
@@ -520,7 +520,8 @@ ORDER BY start_date desc
       if (is_array($value)) {
         $type = implode(',', $value);
       }
-      $clauses[] = "event_type_id IN ({$type})";
+      $clauses[] = "event_type_id IN (%2)";
+      $params[2] = [$type, 'String'];
     }
 
     $eventsByDates = $this->get('eventsByDates');
index e743d2d796851b6e7983565a65cb1345fdc4b371..664c193e0cc4f8d7d8533f540730384015f15dad 100644 (file)
@@ -51,6 +51,7 @@ class CRM_Financial_BAO_Payment {
    *
    * @throws \API_Exception
    * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
    */
   public static function create($params) {
     $contribution = civicrm_api3('Contribution', 'getsingle', ['id' => $params['contribution_id']]);
@@ -104,11 +105,24 @@ class CRM_Financial_BAO_Payment {
     }
 
     if ($isPaymentCompletesContribution) {
-      civicrm_api3('Contribution', 'completetransaction', ['id' => $contribution['id']]);
-      // Get the trxn
-      $trxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC');
-      $ftParams = ['id' => $trxnId['financialTrxnId']];
-      $trxn = CRM_Core_BAO_FinancialTrxn::retrieve($ftParams, CRM_Core_DAO::$_nullArray);
+      if ($contributionStatus == 'Pending refund') {
+        // Ideally we could still call completetransaction as non-payment related actions should
+        // be outside this class. However, for now we just update the contribution here.
+        // Unit test cover in CRM_Event_BAO_AdditionalPaymentTest::testTransactionInfo.
+        civicrm_api3('Contribution', 'create',
+          [
+            'id' => $contribution['id'],
+            'contribution_status_id' => 'Completed',
+          ]
+        );
+      }
+      else {
+        civicrm_api3('Contribution', 'completetransaction', ['id' => $contribution['id']]);
+        // Get the trxn
+        $trxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC');
+        $ftParams = ['id' => $trxnId['financialTrxnId']];
+        $trxn = CRM_Core_BAO_FinancialTrxn::retrieve($ftParams, CRM_Core_DAO::$_nullArray);
+      }
     }
     elseif ($contributionStatus === 'Pending') {
       civicrm_api3('Contribution', 'create',
index 558ca34adb7ff157ca04ad43b869c070335432e0..c2c21dc1849a2222fced03049b4d99de5f7a1afa 100644 (file)
@@ -102,4 +102,24 @@ class CRM_Utils_AutoClean {
     \Civi\Core\Resolver::singleton()->call($this->callback, $this->args);
   }
 
+  /**
+   * Prohibit (de)serialization of CRM_Utils_AutoClean.
+   *
+   * The generic nature of AutoClean makes it a potential target for escalating
+   * serialization vulnerabilities, and there's no good reason for serializing it.
+   */
+  public function __sleep() {
+    throw new \RuntimeException("CRM_Utils_AutoClean is a runtime helper. It is not intended for serialization.");
+  }
+
+  /**
+   * Prohibit (de)serialization of CRM_Utils_AutoClean.
+   *
+   * The generic nature of AutoClean makes it a potential target for escalating
+   * serialization vulnerabilities, and there's no good reason for deserializing it.
+   */
+  public function __wakeup() {
+    throw new \RuntimeException("CRM_Utils_AutoClean is a runtime helper. It is not intended for deserialization.");
+  }
+
 }
index b51905f71496dfb2ae0e9d56a922130476bd2322..efd0b79c6141a4e279011617bbc0ebcbf1840949 100644 (file)
@@ -1066,4 +1066,29 @@ HTACCESS;
     return FALSE;
   }
 
+  /**
+   * Get the extensions that this MimeTpe is for
+   * @param string $mimeType the mime-type we want extensions for
+   * @return array
+   */
+  public static function getAcceptableExtensionsForMimeType($mimeType = NULL) {
+    $mapping = \MimeType\Mapping::$types;
+    $extensions = [];
+    foreach ($mapping as $extension => $type) {
+      if ($mimeType == $type) {
+        $extensions[] = $extension;
+      }
+    }
+    return $extensions;
+  }
+
+  /**
+   * Get the extension of a file based on its path
+   * @param string $path path of the file to query
+   * @return string
+   */
+  public static function getExtensionFromPath($path) {
+    return pathinfo($path, PATHINFO_EXTENSION);
+  }
+
 }
index 50d72d38352e74e4d484241548f7464638a20c15..940128d7156c9487cd53d365c2b60faa2cc62a36 100644 (file)
@@ -488,6 +488,8 @@ class CRM_Utils_Rule {
    */
   public static function commaSeparatedIntegers($value) {
     foreach (explode(',', $value) as $val) {
+      // Remove any Whitespace around the key.
+      $val = trim($val);
       if (!self::positiveInteger($val)) {
         return FALSE;
       }
index 00d04d34215c39bde99cb5a66a0990faafc9440e..5ff1bf3bf82772bd5405389cc0e1fe4b5f0b7916 100644 (file)
@@ -33,8 +33,6 @@ namespace Civi\Core;
  */
 class SettingsMetadata {
 
-  const ALL = 'all';
-
   /**
    * WARNING: This interface may change.
    *
@@ -53,6 +51,7 @@ class SettingsMetadata {
    *
    * @param array $filters
    * @param int $domainID
+   * @param bool $loadOptions
    *
    * @return array
    *   the following information as appropriate for each setting
@@ -64,37 +63,31 @@ class SettingsMetadata {
    *   - is_contact
    *   - description
    *   - help_text
+   *   - options
+   *   - pseudoconstant
    */
-  public static function getMetadata($filters = [], $domainID = NULL) {
+  public static function getMetadata($filters = [], $domainID = NULL, $loadOptions = FALSE) {
     if ($domainID === NULL) {
       $domainID = \CRM_Core_Config::domainID();
     }
 
     $cache = \Civi::cache('settings');
     $cacheString = 'settingsMetadata_' . $domainID . '_';
-    // the caching into 'All' seems to be a duplicate of caching to
-    // settingsMetadata__ - I think the reason was to cache all settings as defined & then those altered by a hook
     $settingsMetadata = $cache->get($cacheString);
-    $cached = is_array($settingsMetadata);
-
-    if (!$cached) {
-      $settingsMetadata = $cache->get(self::ALL);
-      if (empty($settingsMetadata)) {
-        global $civicrm_root;
-        $metaDataFolders = [$civicrm_root . '/settings'];
-        \CRM_Utils_Hook::alterSettingsFolders($metaDataFolders);
-        $settingsMetadata = self::loadSettingsMetaDataFolders($metaDataFolders);
-        $cache->set(self::ALL, $settingsMetadata);
-      }
-    }
-
-    \CRM_Utils_Hook::alterSettingsMetaData($settingsMetadata, $domainID, NULL);
 
-    if (!$cached) {
+    if (!is_array($settingsMetadata)) {
+      global $civicrm_root;
+      $metaDataFolders = [$civicrm_root . '/settings'];
+      \CRM_Utils_Hook::alterSettingsFolders($metaDataFolders);
+      $settingsMetadata = self::loadSettingsMetaDataFolders($metaDataFolders);
+      \CRM_Utils_Hook::alterSettingsMetaData($settingsMetadata, $domainID, NULL);
       $cache->set($cacheString, $settingsMetadata);
     }
 
     self::_filterSettingsSpecification($filters, $settingsMetadata);
+    if ($loadOptions) {
+      self::loadOptions($settingsMetadata);
+    }
 
     return $settingsMetadata;
   }
@@ -145,20 +138,41 @@ class SettingsMetadata {
    *   Metadata to filter.
    */
   protected static function _filterSettingsSpecification($filters, &$settingSpec) {
-    if (empty($filters)) {
-      return;
-    }
-    elseif (array_keys($filters) == ['name']) {
-      $settingSpec = [$filters['name'] => \CRM_Utils_Array::value($filters['name'], $settingSpec, '')];
-      return;
+    if (!empty($filters['name'])) {
+      $settingSpec = array_intersect_key($settingSpec, array_flip((array) $filters['name']));
+      // FIXME: This is a workaround for settingsBag::setDb() called by unit tests with settings names that don't exist
+      $settingSpec += array_fill_keys((array) $filters['name'], []);
+      unset($filters['name']);
     }
-    else {
+    if (!empty($filters)) {
       foreach ($settingSpec as $field => $fieldValues) {
         if (array_intersect_assoc($fieldValues, $filters) != $filters) {
           unset($settingSpec[$field]);
         }
       }
-      return;
+    }
+  }
+
+  /**
+   * Retrieve options from settings metadata
+   *
+   * @param array $settingSpec
+   */
+  protected static function loadOptions(&$settingSpec) {
+    foreach ($settingSpec as &$spec) {
+      if (empty($spec['pseudoconstant'])) {
+        continue;
+      }
+      // It would be nice if we could leverage CRM_Core_PseudoConstant::get() somehow,
+      // but it's tightly coupled to DAO/field. However, if you really need to support
+      // more pseudoconstant types, then probably best to refactor it. For now, KISS.
+      if (!empty($spec['pseudoconstant']['callback'])) {
+        $spec['options'] = Resolver::singleton()->call($spec['pseudoconstant']['callback'], []);
+      }
+      elseif (!empty($spec['pseudoconstant']['optionGroupName'])) {
+        $keyColumn = \CRM_Utils_Array::value('keyColumn', $spec['pseudoconstant'], 'value');
+        $spec['options'] = \CRM_Core_OptionGroup::values($spec['pseudoconstant']['optionGroupName'], FALSE, FALSE, TRUE, NULL, 'label', TRUE, FALSE, $keyColumn);
+      }
     }
   }
 
similarity index 100%
rename from agpl-3.0.txt
rename to LICENSE
index c88419ccb40b0ebd3226d3867079b0bc2795674c..6dfb1c81383a37cc8b2930a345fe11b7279c8ff8 100644 (file)
@@ -432,7 +432,7 @@ function civicrm_api3_generic_getoptions($apiRequest) {
   // Validate 'context' from params
   $context = CRM_Utils_Array::value('context', $apiRequest['params']);
   CRM_Core_DAO::buildOptionsContext($context);
-  unset($apiRequest['params']['context'], $apiRequest['params']['field']);
+  unset($apiRequest['params']['context'], $apiRequest['params']['field'], $apiRequest['params']['condition']);
 
   $baoName = _civicrm_api3_get_BAO($apiRequest['entity']);
   $options = $baoName::buildOptions($fieldName, $context, $apiRequest['params']);
index e7d2338c254f9859b9130045d5d3a37f1d67b9ee..a7ce04b76fcd4356bdd4268096e20674851b1897 100644 (file)
@@ -148,33 +148,14 @@ function _civicrm_api3_setting_getdefaults_spec(&$params) {
  * @throws \API_Exception
  */
 function civicrm_api3_setting_getoptions($params) {
-  $specs = CRM_Core_BAO_Setting::getSettingSpecification();
+  $domainId = CRM_Utils_Array::value('domain_id', $params);
+  $specs = \Civi\Core\SettingsMetadata::getMetadata(['name' => $params['field']], $domainId, TRUE);
 
-  if (empty($specs[$params['field']]) || empty($specs[$params['field']]['pseudoconstant'])) {
+  if (empty($specs[$params['field']]) || !is_array(CRM_Utils_Array::value('options', $specs[$params['field']]))) {
     throw new API_Exception("The field '" . $params['field'] . "' has no associated option list.");
   }
 
-  $pseudoconstant = $specs[$params['field']]['pseudoconstant'];
-
-  // It would be nice if we could leverage CRM_Core_PseudoConstant::get() somehow,
-  // but it's tightly coupled to DAO/field. However, if you really need to support
-  // more pseudoconstant types, then probably best to refactor it. For now, KISS.
-  if (!empty($pseudoconstant['callback'])) {
-    $values = Civi\Core\Resolver::singleton()->call($pseudoconstant['callback'], []);
-    return civicrm_api3_create_success($values, $params, 'Setting', 'getoptions');
-  }
-  elseif (!empty($pseudoconstant['optionGroupName'])) {
-    $keyColumn = 'value';
-    if (!empty($pseudoconstant['keyColumn'])) {
-      $keyColumn = $pseudoconstant['keyColumn'];
-    }
-    return civicrm_api3_create_success(
-      CRM_Core_OptionGroup::values($pseudoconstant['optionGroupName'], FALSE, FALSE, TRUE, NULL, 'label', TRUE, FALSE, $keyColumn),
-      $params, 'Setting', 'getoptions'
-    );
-  }
-
-  throw new API_Exception("The field '" . $params['field'] . "' uses an unsupported option list.");
+  return civicrm_api3_create_success($specs[$params['field']]['options'], $params, 'Setting', 'getoptions');
 }
 
 /**
index b970b3e9ada7cdee86e1684673f56d0332942401..c4aaaa21526f2fdbdf86aca5ca65d78e68520987 100644 (file)
@@ -18,7 +18,7 @@
     "d3-3.5.x": "d3#~3.5.17",
     "dc-2.1.x": "dc.js#~2.1.8",
     "crossfilter-1.3.x": "crossfilter2#~1.3.11",
-    "jquery": "~1.12",
+    "jquery": "civicrm/jquery#1.12.4-civicrm-1.1",
     "jquery-ui": "~1.12",
     "lodash-compat": "~3.0",
     "google-code-prettify": "~1.0",
@@ -35,6 +35,7 @@
     "es6-promise": "^4.2.4"
   },
   "resolutions": {
-    "angular": "~1.5.11"
+    "angular": "~1.5.11",
+    "jquery": "1.12.4-civicrm-1.1"
   }
 }
index fb0a9154945ff3c3c0388a21c8235ce3b9b5dede..bdbf553c4a607c5dc91284cdd18305cf9087f75b 100644 (file)
@@ -61,7 +61,8 @@
     "psr/simple-cache": "~1.0.1",
     "cweagans/composer-patches": "~1.0",
     "pear/log": "1.13.1",
-    "ezyang/htmlpurifier": "4.10"
+    "ezyang/htmlpurifier": "4.10",
+    "katzien/php-mime-type": "2.1.0"
   },
   "scripts": {
     "post-install-cmd": [
index 4e2fd42aa8fef4683b1ede4a4b3d0acbefb22ff2..b0f5e5e881cfd82c6579e431b0efff75f577ec01 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "93a9f686f7eb00fb9d766d262eedb09b",
+    "content-hash": "2a06373b9174ae3aa2bfb820e2e5a35e",
     "packages": [
         {
             "name": "civicrm/civicrm-cxn-rpc",
             ],
             "time": "2017-03-20T17:10:46+00:00"
         },
+        {
+            "name": "katzien/php-mime-type",
+            "version": "2.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/katzien/PhpMimeType.git",
+                "reference": "159dfbdcd5906442f3dad89951127f0b9dfa3b78"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/katzien/PhpMimeType/zipball/159dfbdcd5906442f3dad89951127f0b9dfa3b78",
+                "reference": "159dfbdcd5906442f3dad89951127f0b9dfa3b78",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "5.*",
+                "satooshi/php-coveralls": "1.*"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "MimeType\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Kat Zien"
+                }
+            ],
+            "description": "A PHP library to detect the mime type of files.",
+            "homepage": "https://github.com/katzien/PhpMimeType",
+            "keywords": [
+                "mimetype",
+                "php"
+            ],
+            "time": "2017-03-23T02:05:33+00:00"
+        },
         {
             "name": "marcj/topsort",
             "version": "1.1.0",
         },
         {
             "name": "tecnickcom/tcpdf",
-            "version": "6.2.13",
+            "version": "6.2.26",
             "source": {
                 "type": "git",
                 "url": "https://github.com/tecnickcom/TCPDF.git",
-                "reference": "95c5938aafe4b20df1454dbddb3e5005c0b26f64"
+                "reference": "367241059ca166e3a76490f4448c284e0a161f15"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/95c5938aafe4b20df1454dbddb3e5005c0b26f64",
-                "reference": "95c5938aafe4b20df1454dbddb3e5005c0b26f64",
+                "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/367241059ca166e3a76490f4448c284e0a161f15",
+                "reference": "367241059ca166e3a76490f4448c284e0a161f15",
                 "shasum": ""
             },
             "require": {
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "LGPLv3"
+                "LGPL-3.0"
             ],
             "authors": [
                 {
                     "name": "Nicola Asuni",
                     "email": "info@tecnick.com",
-                    "homepage": "http://nicolaasuni.tecnick.com"
+                    "role": "lead"
                 }
             ],
             "description": "TCPDF is a PHP class for generating PDF documents and barcodes.",
                 "pdf417",
                 "qrcode"
             ],
-            "time": "2017-04-26T08:14:48+00:00"
+            "time": "2018-10-16T17:24:05+00:00"
         },
         {
             "name": "totten/ca-config",
index c6cd5c63ba15c28607f5dbd5ad346e69d914e93a..6eaf8a50302199acda493b587ebfe779ff650ca4 100644 (file)
@@ -63,14 +63,16 @@ global $installURLPath;
 // Set the install type
 // this is sent as a query string when the page is first loaded
 // and subsequently posted to the page as a hidden field
-if (isset($_POST['civicrm_install_type'])) {
+// only permit acceptable installation types to prevent issues;
+$acceptableInstallTypes = ['drupal', 'wordpress', 'backdrop'];
+if (isset($_POST['civicrm_install_type']) && in_array($_POST['civicrm_install_type'], $acceptableInstallTypes)) {
   $installType = $_POST['civicrm_install_type'];
 }
-elseif (isset($_GET['civicrm_install_type'])) {
+elseif (isset($_GET['civicrm_install_type']) && in_array(strtolower($_GET['civicrm_install_type']), $acceptableInstallTypes)) {
   $installType = strtolower($_GET['civicrm_install_type']);
 }
 else {
-  // default value if not set
+  // default value if not set and not an acceptable install type.
   $installType = "drupal";
 }
 
index 6bdcf86a8ca62fcc1cfa0895e6bbd2aae453fcb3..2150d478e4e422f4c9483ffe75578e5481dcad0f 100644 (file)
@@ -14,9 +14,15 @@ Other resources for identifying changes are:
     * https://github.com/civicrm/civicrm-joomla
     * https://github.com/civicrm/civicrm-wordpress
 
+## CiviCRM 5.13.4
+
+Released May 15, 2019
+
+- **[Security advisories](release-notes/5.3.4.md#security)**
+
 ## CiviCRM 5.13.3
 
-Released May 13, 2019
+Released May 14, 2019
 
 - **[Synopsis](release-notes/5.13.3.md#synopsis)**
 - **[Features](release-notes/5.13.3.md#features)**
index f5b361fa3b33ed58b7254506134ecd8f26692e54..c9e516212d2d9714a76818c2d9f10194f3d49f1e 100644 (file)
@@ -1,6 +1,6 @@
 # CiviCRM 5.13.3
 
-Released May 13, 2019
+Released May 14, 2019
 
 - **[Synopsis](#synopsis)**
 - **[Bugs resolved](#bugs)**
diff --git a/release-notes/5.13.4.md b/release-notes/5.13.4.md
new file mode 100644 (file)
index 0000000..bc5c34d
--- /dev/null
@@ -0,0 +1,23 @@
+# CiviCRM 5.13.4
+
+Released May 15, 2019
+
+- **[Security advisories](#security)**
+- **[Features](#features)**
+- **[Bugs resolved](#bugs)**
+- **[Miscellany](#misc)**
+- **[Credits](#credits)**
+
+## <a name="security"></a>Security advisories
+
+- **[CIVI-SA-2019-09](https://civicrm.org/advisory/civi-sa-2019-09-xxe-in-phpword)**: XXE in PHPWord
+- **[CIVI-SA-2019-10](https://civicrm.org/advisory/civi-sa-2019-10-tcpdf-xss-and-rce-vulerabilities)**: TCPDF XSS and RCE vulnerabilities
+- **[CIVI-SA-2019-11](https://civicrm.org/advisory/civi-sa-2019-11-jquery-objectprototype-pollution)**: jQuery Object.prototype pollution
+- **[CIVI-SA-2019-12](https://civicrm.org/advisory/civi-sa-2019-12-sqli-in-country-et-al)**: SQLI in "Country", et al
+- **[CIVI-SA-2019-13](https://civicrm.org/advisory/civi-sa-2019-13-harden-against-unserialize-vulnerabilities)**: Harden against unserialize vulnerabilities
+- **[CIVI-SA-2019-14](https://civicrm.org/advisory/civi-sa-2019-14-sqli-in-apiv3-getoptions)**: SQLI in APIv3 GetOptions
+- **[CIVI-SA-2019-15](https://civicrm.org/advisory/civi-sa-2019-15-xss-via-forged-mime-type)**: XSS via forged MIME type
+- **[CIVI-SA-2019-16](https://civicrm.org/advisory/civi-sa-2019-16-sqli-in-certain-checkboxes)**: SQLI in certain checkboxes
+- **[CIVI-SA-2019-17](https://civicrm.org/advisory/civi-sa-2019-17-sqli-in-manage-events)**: SQLI in "Manage Events"
+- **[CIVI-SA-2019-18](https://civicrm.org/advisory/civi-sa-2019-18-xss-in-civicrm-installer)**: XSS in CiviCRM installer
+- **[CIVIEXT-SA-2019-01](https://civicrm.org/advisory/civiext-sa-2019-01-multiple-security-issues-in-apiv4)**: Multiple security issues in APIv4
index 27af8a8f5b4f1618f3c17f03248827ad6c64ecfa..cc69e3f982d74b048f47882b5170dfc3efda1522 100644 (file)
@@ -1052,4 +1052,18 @@ return [
     'help_text' => NULL,
     'validate_callback' => 'CRM_Utils_Rule::color',
   ],
+  'requestableMimeTypes' => [
+    'group_name' => 'CiviCRM Preferences',
+    'group' => 'core',
+    'name' => 'requestableMimeTypes',
+    'type' => 'String',
+    'html_type' => 'Text',
+    'default' => 'image/jpeg,image/pjpeg,image/gif,image/x-png,image/png,image/jpg,text/html,application/pdf',
+    'add' => '5.13',
+    'title' => ts('Mime Types that can be passed as URL params'),
+    'is_domain' => 1,
+    'is_contact' => 0,
+    'description' => ts('Acceptable Mime Types that can be used as part of file urls'),
+    'help_text' => NULL,
+  ],
 ];
index 96f440873f980496c62fc6eaf0cdf8823f637c15..d065d0ef838b56785f0bb72b8b959ba4b629fd80 100644 (file)
@@ -485,6 +485,26 @@ class CRM_Dedupe_MergerTest extends CiviUnitTestCase {
     ), $pairs);
   }
 
+  /**
+   * Test the special info handling is unchanged after cleanup.
+   *
+   * Note the handling is silly - we are testing to lock in over short term changes not to imply any contract on the
+   * function.
+   */
+  public function testgetRowsElementsAndInfoSpecialInfo() {
+    $contact1 = $this->individualCreate(['preferred_communication_method' => [], 'communication_style_id' => 'Familiar', 'prefix_id' => 'Mrs.', 'suffix_id' => 'III']);
+    $contact2 = $this->individualCreate(['preferred_communication_method' => ['SMS', 'Fax'], 'communication_style_id' => 'Formal', 'gender_id' => 'Female']);
+    $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($contact1, $contact2);
+    $rows = $rowsElementsAndInfo['rows'];
+    $this->assertEquals(['main' => 'Mrs.', 'other' => 'Mr.', 'title' => 'Individual Prefix'], $rows['move_prefix_id']);
+    $this->assertEquals(['main' => 'III', 'other' => 'II', 'title' => 'Individual Suffix'], $rows['move_suffix_id']);
+    $this->assertEquals(['main' => '', 'other' => 'Female', 'title' => 'Gender'], $rows['move_gender_id']);
+    $this->assertEquals(['main' => 'Familiar', 'other' => 'Formal', 'title' => 'Communication Style'], $rows['move_communication_style_id']);
+    $this->assertEquals(1, $rowsElementsAndInfo['migration_info']['move_communication_style_id']);
+    $this->assertEquals(['main' => '', 'other' => 'SMS, Fax', 'title' => 'Preferred Communication Method'], $rows['move_preferred_communication_method']);
+    $this->assertEquals('\ 14\ 15\ 1', $rowsElementsAndInfo['migration_info']['move_preferred_communication_method']);
+  }
+
   /**
    * Test migration of Membership.
    */
index aa4cd9e4313a619862cd286eddaa4ee0c2ca7745..9e70d946bfb8c90c0f68df9500540963e6de25f1 100644 (file)
@@ -214,20 +214,23 @@ class CRM_Event_BAO_AdditionalPaymentTest extends CiviUnitTestCase {
     $result = $this->addParticipantWithPayment($feeAmt, $amtPaid);
     $contributionID = $result['contribution']['id'];
 
-    //Complete the partial payment.
-    $submittedValues = array(
+    $this->callAPISuccess('Payment', 'create', [
+      'contribution_id' => $contributionID,
       'total_amount' => 20,
       'payment_instrument_id' => 3,
-    );
-    CRM_Contribute_BAO_Contribution::recordAdditionalPayment($contributionID, $submittedValues, 'owed', $result['participant']['id']);
+      'participant_id' => $result['participant']['id'],
+    ]);
 
     //Change selection to a lower amount.
     $params['price_2'] = 50;
     CRM_Price_BAO_LineItem::changeFeeSelections($params, $result['participant']['id'], 'participant', $contributionID, $result['feeBlock'], $result['lineItem']);
 
-    //Record a refund of the remaining amount.
-    $submittedValues['total_amount'] = 50;
-    CRM_Contribute_BAO_Contribution::recordAdditionalPayment($contributionID, $submittedValues, 'refund', $result['participant']['id']);
+    $this->callAPISuccess('Payment', 'create', [
+      'total_amount' => -50,
+      'contribution_id' => $contributionID,
+      'participant_id' => $result['participant']['id'],
+      'payment_instrument_id' => 3,
+    ]);
     $paymentInfo = CRM_Contribute_BAO_Contribution::getPaymentInfo($result['participant']['id'], 'event', TRUE);
     $transaction = $paymentInfo['transaction'];
 
index a2fc34fbbcefbb96a72955bc8b693e852905af32..047fcb652d6ae48131ddee1f95445787de88f23a 100644 (file)
@@ -308,12 +308,12 @@ class CRM_Event_BAO_ChangeFeeSelectionTest extends CiviUnitTestCase {
     $contributionBalance = ($this->_cheapFee - $actualPaidAmount);
     $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution::getContributionBalance($this->_contributionId));
 
-    //Complete the refund payment.
-    $submittedValues = array(
-      'total_amount' => 120,
+    $this->callAPISuccess('Payment', 'create', [
+      'contribution_id' => $this->_contributionId,
+      'total_amount' => -120,
       'payment_instrument_id' => 3,
-    );
-    CRM_Contribute_BAO_Contribution::recordAdditionalPayment($this->_contributionId, $submittedValues, 'refund', $this->_participantId);
+      'participant_id' => $this->_participantId,
+    ]);
     $contributionBalance += 120;
     $this->assertEquals($contributionBalance, CRM_Contribute_BAO_Contribution::getContributionBalance($this->_contributionId));
 
index f52784ae4fb226c7591ac287c6018b33c7be91c6..692bbee80380ba4610019b0a5f48e0f54c2a2ae6 100644 (file)
@@ -95,4 +95,39 @@ class CRM_Utils_FileTest extends CiviUnitTestCase {
     $this->assertEquals($expectedResult, CRM_Utils_File::isValidFileName($fileName));
   }
 
+  public function pathToFileExtension() {
+    $cases = [];
+    $cases[] = ['/evil.pdf', 'pdf'];
+    $cases[] = ['/helloworld.jpg', 'jpg'];
+    $cases[] = ['/smartwatch_1736683_1280_9af3657015e8660cc234eb1601da871.jpg', 'jpg'];
+    return $cases;
+  }
+
+  /**
+   * Test returning appropriate file extension
+   * @dataProvider pathToFileExtension
+   * @param string $path
+   * @param string $expectedExtension
+   */
+  public function testPathToExtension($path, $expectedExtension) {
+    $this->assertEquals($expectedExtension, CRM_Utils_File::getExtensionFromPath($path));
+  }
+
+  public function mimeTypeToExtension() {
+    $cases = [];
+    $cases[] = ['text/plain', ['txt', 'text', 'conf', 'def', 'list', 'log', 'in']];
+    $cases[] = ['image/jpeg', ['jpeg', 'jpg', 'jpe']];
+    $cases[] = ['image/png', ['png']];
+    return $cases;
+  }
+
+  /**
+   * @dataProvider mimeTypeToExtension
+   * @param stirng $mimeType
+   * @param array $expectedExtensions
+   */
+  public function testMimeTypeToExtension($mimeType, $expectedExtensions) {
+    $this->assertEquals($expectedExtensions, CRM_Utils_File::getAcceptableExtensionsForMimeType($mimeType));
+  }
+
 }
index 39b9811ea8a6342dfc170d6097bc7bcd13f042c6..c6c473e4303a3521c2278b2f327069e76860001a 100644 (file)
@@ -1942,13 +1942,13 @@ AND    ( TABLE_NAME LIKE 'civicrm_value_%' )
    * @return void
    */
   public function setMockSettingsMetaData($extras) {
-    Civi::service('settings_manager')->flush();
-
     CRM_Utils_Hook::singleton()
       ->setHook('civicrm_alterSettingsMetaData', function (&$metadata, $domainId, $profile) use ($extras) {
         $metadata = array_merge($metadata, $extras);
       });
 
+    Civi::service('settings_manager')->flush();
+
     $fields = $this->callAPISuccess('setting', 'getfields', array());
     foreach ($extras as $key => $spec) {
       $this->assertNotEmpty($spec['title']);
index bbbc21cf9a7d2cac2018d3bc7ddd524e65ad888c..308b012d60e9fb2bba3ecd8b391fa2dee76d4b29 100644 (file)
@@ -479,4 +479,47 @@ class api_v3_AddressTest extends CiviUnitTestCase {
     $this->assertEquals($expectState, $created['state_province_id']);
   }
 
+  public function testBuildStateProvinceOptionsWithDodgyProvinceLimit() {
+    $provinceLimit = [1228, "abcd;ef"];
+    $this->callAPISuccess('setting', 'create', [
+     'provinceLimit' => $provinceLimit,
+    ]);
+    $result = $this->callAPIFailure('address', 'getoptions', ['field' => 'state_province_id']);
+    // confirm that we hit our error not a SQLI.
+    $this->assertEquals('Province limit or default country setting is incorrect', $result['error_message']);
+    $this->callAPISuccess('setting', 'create', [
+     'provinceLimit' => [1228],
+    ]);
+    // Now confirm with a correct province setting it works fine
+    $this->callAPISuccess('address', 'getoptions', ['field' => 'state_province_id']);
+  }
+
+  public function testBuildCountryWithDodgyCountryLimitSetting() {
+    $countryLimit = [1228, "abcd;ef"];
+    $this->callAPISuccess('setting', 'create', [
+     'countryLimit' => $countryLimit,
+    ]);
+    $result = $this->callAPIFailure('address', 'getoptions', ['field' => 'country_id']);
+    // confirm that we hit our error not a SQLI.
+    $this->assertEquals('Available Country setting is incorrect', $result['error_message']);
+    $this->callAPISuccess('setting', 'create', [
+     'countryLimit' => [1228],
+    ]);
+    // Now confirm with a correct province setting it works fine
+    $this->callAPISuccess('address', 'getoptions', ['field' => 'country_id']);
+  }
+
+  public function testBuildCountyWithDodgeStateProvinceFiltering() {
+    $result = $this->callAPIFailure('Address', 'getoptions', [
+      'field' => 'county_id',
+      'state_province_id' => "abcd;ef",
+    ]);
+    $this->assertEquals('Can only accept Integers for state_province_id filtering', $result['error_message']);
+    $goodResult = $this->callAPISuccess('Address', 'getoptions', [
+      'field' => 'county_id',
+      'state_province_id' => 1004,
+    ]);
+    $this->assertEquals('San Francisco', $goodResult['values'][4]);
+  }
+
 }
index aa75cf711572617c653ccb5d98c8753c923f53a3..f8d6ed962064d9eee8ed375fdf68cb42688112b0 100644 (file)
@@ -110,7 +110,6 @@ class api_v3_SettingTest extends CiviUnitTestCase {
   public function testGetFieldsCaching() {
     $settingsMetadata = array();
     Civi::cache('settings')->set('settingsMetadata_' . \CRM_Core_Config::domainID() . '_', $settingsMetadata);
-    Civi::cache('settings')->set(\Civi\Core\SettingsMetadata::ALL, $settingsMetadata);
     $result = $this->callAPISuccess('setting', 'getfields', array());
     $this->assertArrayNotHasKey('customCSSURL', $result['values']);
     $this->quickCleanup(array('civicrm_cache'));