Merge pull request #19595 from eileenmcnaughton/tokens
authorSeamus Lee <seamuslee001@gmail.com>
Mon, 15 Feb 2021 20:06:14 +0000 (07:06 +1100)
committerGitHub <noreply@github.com>
Mon, 15 Feb 2021 20:06:14 +0000 (07:06 +1100)
[NFC] Code cleanup - use use statements, hints

15 files changed:
CRM/Contact/BAO/Contact.php
CRM/Core/BAO/CustomField.php
CRM/Core/BAO/CustomGroup.php
CRM/Member/BAO/Membership.php
CRM/Member/Form/Membership.php
CRM/Member/Form/MembershipConfig.php
CRM/Member/Page/AJAX.php
CRM/Member/Tokens.php
CRM/PCP/Form/Campaign.php
CRM/Utils/EchoLogger.php [new file with mode: 0644]
CRM/Utils/GuzzleMiddleware.php [new file with mode: 0644]
Civi/Api4/Generic/BasicGetFieldsAction.php
Civi/Test/GuzzleTestTrait.php
Civi/Test/HttpTestTrait.php [new file with mode: 0644]
templates/CRM/Event/Form/Participant.tpl

index 0edeb2f09a4101f923a0fc136f278a95597c85c6..05e7a4a2c85c61c0bb9e99b4acfab77c96f55566 100644 (file)
@@ -2657,7 +2657,7 @@ LEFT JOIN civicrm_email    ON ( civicrm_contact.id = civicrm_email.contact_id )
         return CRM_Contribute_BAO_Contribution::contributionCount($contactId);
 
       case 'membership':
-        return CRM_Member_BAO_Membership::getContactMembershipCount($contactId, TRUE);
+        return CRM_Member_BAO_Membership::getContactMembershipCount((int) $contactId, TRUE);
 
       case 'participant':
         return CRM_Event_BAO_Participant::getContactParticipantCount($contactId);
index 2ca7070c76cb464c25e66bf1e7ae4793cde6f19a..3057d18153f164b3be6f6c9ca4506a1db4fa52de 100644 (file)
@@ -1187,7 +1187,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
         if ($field['data_type'] == 'Money' && isset($value)) {
           // $value can also be an array(while using IN operator from search builder or api).
           foreach ((array) $value as $val) {
-            $disp[] = CRM_Utils_Money::format($val, NULL, NULL, TRUE);
+            $disp[] = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($val);
           }
           $display = implode(', ', $disp);
         }
index 61da4970e4f8c3e300a871d8fa73c11f9dc792c1..2ea810c725a72875eaad379a9074f7ba3aa85e65 100644 (file)
@@ -1383,7 +1383,7 @@ ORDER BY civicrm_custom_group.weight,
             elseif ($field['data_type'] == 'Money' &&
               $field['html_type'] == 'Text'
             ) {
-              $defaults[$elementName] = CRM_Utils_Money::format($value, NULL, '%a');
+              $defaults[$elementName] = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($value);
             }
             else {
               $defaults[$elementName] = $value;
index 2d6af50517c2c1062b1543a16a7ce537fe2ef172..471a15e39416b86d7a100c94c145ffa92f07502e 100644 (file)
@@ -9,6 +9,9 @@
  +--------------------------------------------------------------------+
  */
 
+use Civi\API\Exception\UnauthorizedException;
+use Civi\Api4\MembershipType;
+
 /**
  *
  * @package CRM
@@ -1572,29 +1575,35 @@ WHERE  civicrm_membership.contact_id = civicrm_contact.id
    * @param int $contactID
    * @param bool $activeOnly
    *
-   * @return null|string
+   * @return int
+   * @throws \API_Exception
    */
-  public static function getContactMembershipCount($contactID, $activeOnly = FALSE) {
-    $membershipTypes = \Civi\Api4\MembershipType::get(TRUE)
-      ->execute()
-      ->indexBy('id')
-      ->column('name');
-    $addWhere = " AND membership_type_id IN (0)";
-    if (!empty($membershipTypes)) {
-      $addWhere = " AND membership_type_id IN (" . implode(',', array_keys($membershipTypes)) . ")";
-    }
+  public static function getContactMembershipCount(int $contactID, $activeOnly = FALSE): int {
+    try {
+      $membershipTypes = MembershipType::get(TRUE)
+        ->execute()
+        ->indexBy('id')
+        ->column('name');
+      $addWhere = " AND membership_type_id IN (0)";
+      if (!empty($membershipTypes)) {
+        $addWhere = " AND membership_type_id IN (" . implode(',', array_keys($membershipTypes)) . ")";
+      }
 
-    $select = "SELECT count(*) FROM civicrm_membership ";
-    $where = "WHERE civicrm_membership.contact_id = {$contactID} AND civicrm_membership.is_test = 0 ";
+      $select = "SELECT COUNT(*) FROM civicrm_membership ";
+      $where = "WHERE civicrm_membership.contact_id = {$contactID} AND civicrm_membership.is_test = 0 ";
 
-    // CRM-6627, all status below 3 (active, pending, grace) are considered active
-    if ($activeOnly) {
-      $select .= " INNER JOIN civicrm_membership_status ON civicrm_membership.status_id = civicrm_membership_status.id ";
-      $where .= " and civicrm_membership_status.is_current_member = 1";
-    }
+      // CRM-6627, all status below 3 (active, pending, grace) are considered active
+      if ($activeOnly) {
+        $select .= " INNER JOIN civicrm_membership_status ON civicrm_membership.status_id = civicrm_membership_status.id ";
+        $where .= " and civicrm_membership_status.is_current_member = 1";
+      }
 
-    $query = $select . $where . $addWhere;
-    return CRM_Core_DAO::singleValueQuery($query);
+      $query = $select . $where . $addWhere;
+      return (int) CRM_Core_DAO::singleValueQuery($query);
+    }
+    catch (UnauthorizedException $e) {
+      return 0;
+    }
   }
 
   /**
index 3b0b0b140ede0a05c7146f3c0c7f3a41b4e08b77..f42d033084d0fd5817d912a6d2f6b107f21e847c 100644 (file)
@@ -488,7 +488,7 @@ DESC limit 1");
       // - set the max related block
       $allMembershipInfo[$key] = [
         'financial_type_id' => $values['financial_type_id'] ?? NULL,
-        'total_amount' => CRM_Utils_Money::format($totalAmount, NULL, '%a'),
+        'total_amount' => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($totalAmount),
         'total_amount_numeric' => $totalAmount,
         'auto_renew' => $values['auto_renew'] ?? NULL,
         'has_related' => isset($values['relationship_type_id']),
index b1a8937256377c758db56c4e69bc225af5157a80..6e887244a4563d9643e536ad1f13af12aabfdd08 100644 (file)
@@ -52,7 +52,7 @@ class CRM_Member_Form_MembershipConfig extends CRM_Core_Form {
     }
 
     if (isset($defaults['minimum_fee'])) {
-      $defaults['minimum_fee'] = CRM_Utils_Money::format($defaults['minimum_fee'], NULL, '%a');
+      $defaults['minimum_fee'] = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($defaults['minimum_fee']);
     }
 
     if (isset($defaults['status'])) {
index c9e14a83a948bdcf0546ca872bb53ead9e67945d..ce7ecd79ba50b32994f627c42dfa85b4a1d90020 100644 (file)
@@ -46,7 +46,7 @@ WHERE   id = %1";
     }
     $details['total_amount_numeric'] = $details['total_amount'];
     // fix the display of the monetary value, CRM-4038
-    $details['total_amount'] = CRM_Utils_Money::format($details['total_amount'], NULL, '%a');
+    $details['total_amount'] = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($details['total_amount']);
     $options = CRM_Core_SelectValues::memberAutoRenew();
     $details['auto_renew'] = $options[$details]['auto_renew'] ?? NULL;
     CRM_Utils_JSON::output($details);
index 64d3e5b79c583f93cdbed05491aa27e97980be57..686cc71f4652102c7c29e474dfd94c4102fb0592 100644 (file)
@@ -79,7 +79,7 @@ class CRM_Member_Tokens extends \Civi\Token\AbstractTokenSubscriber {
       $row->tokens($entity, $field, \CRM_Utils_Date::customFormat($actionSearchResult->$field));
     }
     elseif ($field == 'fee') {
-      $row->tokens($entity, $field, \CRM_Utils_Money::format($actionSearchResult->$field, NULL, NULL, TRUE));
+      $row->tokens($entity, $field, \CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($actionSearchResult->$field));
     }
     elseif (isset($actionSearchResult->$field)) {
       $row->tokens($entity, $field, $actionSearchResult->$field);
index 4c244a29dd9eacdc11cd6781fa7de7d84e49ae5b..6445366044aac3d33b80abd2ca04b51725221a5c 100644 (file)
@@ -65,7 +65,7 @@ class CRM_PCP_Form_Campaign extends CRM_Core_Form {
       }
       // fix the display of the monetary value, CRM-4038
       if (isset($defaults['goal_amount'])) {
-        $defaults['goal_amount'] = CRM_Utils_Money::format($defaults['goal_amount'], NULL, '%a');
+        $defaults['goal_amount'] = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($defaults['goal_amount']);
       }
 
       $defaults['pcp_title'] = $defaults['title'] ?? NULL;
diff --git a/CRM/Utils/EchoLogger.php b/CRM/Utils/EchoLogger.php
new file mode 100644 (file)
index 0000000..c6db7c0
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+class CRM_Utils_EchoLogger extends Psr\Log\AbstractLogger implements \Psr\Log\LoggerInterface {
+
+  /**
+   * Logs with an arbitrary level.
+   *
+   * @param mixed $level
+   * @param string $message
+   * @param array $context
+   */
+  public function log($level, $message, array $context = []) {
+    echo $message . "\n";
+  }
+
+}
diff --git a/CRM/Utils/GuzzleMiddleware.php b/CRM/Utils/GuzzleMiddleware.php
new file mode 100644 (file)
index 0000000..500d29c
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Additional helpers/utilities for use as Guzzle middleware.
+ */
+class CRM_Utils_GuzzleMiddleware {
+
+  /**
+   * Add this as a Guzzle handler/middleware if you wish to simplify
+   * the construction of Civi-related URLs. It enables URL schemes for:
+   *
+   * - route://ROUTE_NAME (aka) route:ROUTE_NAME
+   * - var://PATH_EXPRESSION (aka) var:PATH_EXPRESSION
+   * - ext://EXTENSION/FILE (aka) ext:EXTENSION/FILE
+   * - assetBuilder://ASSET_NAME?PARAMS (aka) assetBuilder:ASSET_NAME?PARAMS
+   *
+   * Compare:
+   *
+   * $http->get(CRM_Utils_System::url('civicrm/dashboard', NULL, TRUE, NULL, FALSE))
+   * $http->get('route:civicrm/dashboard')
+   *
+   * $http->get(Civi::paths()->getUrl('[civicrm.files]/foo.txt'))
+   * $http->get('var:[civicrm.files]/foo.txt')
+   *
+   * $http->get(Civi::resources()->getUrl('my.other.ext', 'foo.js'))
+   * $http->get('ext:my.other.ext/foo.js')
+   *
+   * $http->get(Civi::service('asset_builder')->getUrl('my-asset.css', ['a'=>1, 'b'=>2]))
+   * $http->get('assetBuilder:my-asset.css?a=1&b=2')
+   *
+   * Note: To further simplify URL expressions, Guzzle allows you to set a 'base_uri'
+   * option (which is applied as a prefix to any relative URLs). Consider using
+   * `base_uri=auto:`. This allows you to implicitly use the most common types
+   * (routes+variables):
+   *
+   * $http->get('civicrm/dashboard')
+   * $http->get('[civicrm.files]/foo.txt')
+   *
+   * @return \Closure
+   */
+  public static function url() {
+    return function(callable $handler) {
+      return function (RequestInterface $request, array $options) use ($handler) {
+        $newUri = self::filterUri($request->getUri());
+        if ($newUri !== NULL) {
+          $request = $request->withUri(\CRM_Utils_Url::parseUrl($newUri));
+        }
+
+        return $handler($request, $options);
+      };
+    };
+  }
+
+  /**
+   * @param \Psr\Http\Message\UriInterface $oldUri
+   *
+   * @return string|null
+   *   The string formation of the new URL, or NULL for unchanged URLs.
+   */
+  protected static function filterUri(\Psr\Http\Message\UriInterface $oldUri) {
+    // Copy the old ?query-params and #fragment-params on top of $newBase.
+    $copyParams = function ($newBase) use ($oldUri) {
+      if ($oldUri->getQuery()) {
+        $newBase .= strpos($newBase, '?') !== FALSE ? '&' : '?';
+        $newBase .= $oldUri->getQuery();
+      }
+      if ($oldUri->getFragment()) {
+        $newBase .= '#' . $oldUri->getFragment();
+      }
+      return $newBase;
+    };
+
+    $hostPath = urldecode($oldUri->getHost() . $oldUri->getPath());
+    $scheme = $oldUri->getScheme();
+    if ($scheme === 'auto') {
+      // Ex: 'auto:civicrm/my-page' ==> Router
+      // Ex: 'auto:[civicrm.root]/js/foo.js' ==> Resource file
+      $scheme = ($hostPath[0] === '[') ? 'var' : 'route';
+    }
+
+    switch ($scheme) {
+      case 'assetBuilder':
+        // Ex: 'assetBuilder:dynamic.css' or 'assetBuilder://dynamic.css?foo=bar'
+        // Note: It's more useful to pass params to the asset-builder than to the final HTTP request.
+        $assetParams = [];
+        parse_str('' . $oldUri->getQuery(), $assetParams);
+        return \Civi::service('asset_builder')->getUrl($hostPath, $assetParams);
+
+      case 'ext':
+        // Ex: 'ext:other.ext.name/file.js' or 'ext://other.ext.name/file.js'
+        [$ext, $file] = explode('/', $hostPath, 2);
+        return $copyParams(\Civi::resources()->getUrl($ext, $file));
+
+      case 'var':
+        // Ex: 'var:[civicrm.files]/foo.txt' or  'var://[civicrm.files]/foo.txt'
+        return $copyParams(\Civi::paths()->getUrl($hostPath, 'absolute'));
+
+      case 'route':
+        // Ex: 'route:civicrm/my-page' or 'route://civicrm/my-page'
+        return $copyParams(\CRM_Utils_System::url($hostPath, NULL, TRUE, NULL, FALSE));
+
+      default:
+        return NULL;
+    }
+  }
+
+  /**
+   * This logs the list of outgoing requests in curl format.
+   */
+  public static function curlLog(\Psr\Log\LoggerInterface $logger) {
+
+    $curlFmt = new class() extends \GuzzleHttp\MessageFormatter {
+
+      public function format(RequestInterface $request, ResponseInterface $response = NULL, \Exception $error = NULL) {
+        $cmd = '$ curl';
+        if ($request->getMethod() !== 'GET') {
+          $cmd .= ' -X ' . escapeshellarg($request->getMethod());
+        }
+        foreach ($request->getHeaders() as $header => $lines) {
+          foreach ($lines as $line) {
+            $cmd .= ' -H ' . escapeshellarg("$header: $line");
+          }
+        }
+        $body = (string) $request->getBody();
+        if ($body !== '') {
+          $cmd .= ' -d ' . escapeshellarg($body);
+        }
+        $cmd .= ' ' . escapeshellarg((string) $request->getUri());
+        return $cmd;
+      }
+
+    };
+
+    return \GuzzleHttp\Middleware::log($logger, $curlFmt);
+  }
+
+}
index 5f5d25ef3ded38c2c1ee0edcc212aeb6f4173030..7f4faf7ae9885ea7d6831403b13b5b01e2b2832c 100644 (file)
@@ -254,10 +254,30 @@ class BasicGetFieldsAction extends BasicGetAction {
       [
         'name' => 'data_type',
         'data_type' => 'String',
+        'options' => [
+          'Integer' => ts('Integer'),
+          'Boolean' => ts('Boolean'),
+          'String' => ts('String'),
+          'Text' => ts('Text'),
+          'Date' => ts('Date'),
+          'Timestamp' => ts('Timestamp'),
+          'Array' => ts('Array'),
+        ],
       ],
       [
         'name' => 'input_type',
         'data_type' => 'String',
+        'options' => [
+          'Text' => ts('Text'),
+          'Number' => ts('Number'),
+          'Select' => ts('Select'),
+          'CheckBox' => ts('CheckBox'),
+          'Radio' => ts('Radio'),
+          'Date' => ts('Date'),
+          'File' => ts('File'),
+          'EntityRef' => ts('EntityRef'),
+          'ChainSelect' => ts('ChainSelect'),
+        ],
       ],
       [
         'name' => 'input_attrs',
index c372d3eee9e6ac4794b95e276bd6c2736c90310e..c65e029dd76ab356d9afbebefc550624e1e3f251 100644 (file)
@@ -11,7 +11,11 @@ use GuzzleHttp\Client;
 /**
  * Class GuzzleTestTrait
  *
- * This trait defines a number of helper functions for testing guzzle.
+ * This trait defines a number of helper functions for testing Guzzle-based logic,
+ * such as a payment-processing gateway.
+ *
+ * Use this in a headless test for which you need to mock outbound HTTP requests from Civi.
+ * Alternatively, to write an E2E test with inbound HTTP requests to Civi, see HttpTestTrait.
  */
 trait GuzzleTestTrait {
   /**
diff --git a/Civi/Test/HttpTestTrait.php b/Civi/Test/HttpTestTrait.php
new file mode 100644 (file)
index 0000000..80be445
--- /dev/null
@@ -0,0 +1,202 @@
+<?php
+
+namespace Civi\Test;
+
+use GuzzleHttp\HandlerStack;
+use GuzzleHttp\MessageFormatter;
+use GuzzleHttp\Middleware;
+
+/**
+ * Class HttpTestTrait
+ *
+ * @package Civi\Test
+ *
+ * This trait provides helpers/assertions for testing Civi's HTTP interfaces, eg
+ *
+ * - createGuzzle() - Create HTTP client for sending requests to Civi.
+ * - callApi4AjaxSuccess() - Use an HTTP client to send an AJAX-style request to APIv4
+ * - callApi4AjaxError() - Use an HTTP client to send an AJAX-style request to APIv4
+ * - assertStatusCode() - Check the status code. If it fails, output detailed information.
+ * - assertContentType() - Check the content-type. If it fails, output detailed information.
+ *
+ * Use this in an E2E test for which you need to send inbound HTTP requests to Civi.
+ * Alternatively, for a headless test which mocks outbound HTTP, see GuzzleTestTrait.
+ */
+trait HttpTestTrait {
+
+  /**
+   * List of HTTP requests that have been made by this test.
+   *
+   * @var array
+   */
+  protected $httpHistory = [];
+
+  /**
+   * Create an HTTP client suitable for simulating AJAX requests.
+   *
+   * @param array $options
+   * @return \GuzzleHttp\Client
+   */
+  protected function createGuzzle($options = []) {
+    $handler = HandlerStack::create();
+    $handler->unshift(\CRM_Utils_GuzzleMiddleware::url(), 'civi_url');
+    $handler->push(Middleware::history($this->httpHistory), 'history');
+
+    if (getenv('DEBUG') >= 2) {
+      $handler->push(Middleware::log(new \CRM_Utils_EchoLogger(), new MessageFormatter(MessageFormatter::DEBUG)), 'log');
+    }
+    elseif (getenv('DEBUG') >= 1) {
+      $handler->push(\CRM_Utils_GuzzleMiddleware::curlLog(new \CRM_Utils_EchoLogger()), 'curl_log');
+    }
+
+    $defaults = [
+      'handler' => $handler,
+      'base_uri' => 'auto:',
+    ];
+
+    $options = array_merge($defaults, $options);
+    return new \GuzzleHttp\Client($options);
+  }
+
+  /**
+   * @param string $entity
+   * @param string $action
+   * @param array $params
+   *
+   * @return mixed
+   */
+  protected function callApi4AjaxSuccess(string $entity, string $action, $params = []) {
+    $method = \CRM_Utils_String::startsWith($action, 'get') ? 'GET' : 'POST';
+    $response = $this->createGuzzle()->request($method, "civicrm/ajax/api4/$entity/$action", [
+      'headers' => ['X-Requested-With' => 'XMLHttpRequest'],
+      // This should probably be 'form_params', but 'query' is more representative of frontend.
+      'query' => ['params' => json_encode($params)],
+      'http_errors' => FALSE,
+    ]);
+    $this->assertContentType('application/json', $response);
+    $this->assertStatusCode(200, $response);
+    $result = json_decode((string) $response->getBody(), 1);
+    if (json_last_error() !== JSON_ERROR_NONE) {
+      $this->fail("Failed to decode APIv4 JSON.\n" . $this->formatFailure($response));
+    }
+    return $result;
+  }
+
+  /**
+   * @param string $entity
+   * @param string $action
+   * @param array $params
+   *
+   * @return mixed
+   */
+  protected function callApi4AjaxError(string $entity, string $action, $params = []) {
+    $method = \CRM_Utils_String::startsWith($action, 'get') ? 'GET' : 'POST';
+    $response = $this->createGuzzle()->request($method, "civicrm/ajax/api4/$entity/$action", [
+      'headers' => ['X-Requested-With' => 'XMLHttpRequest'],
+      // This should probably be 'form_params', but 'query' is more representative of frontend.
+      'query' => ['params' => json_encode($params)],
+      'http_errors' => FALSE,
+    ]);
+    $this->assertContentType('application/json', $response);
+    $this->assertTrue($response->getStatusCode() >= 400, 'Should return an error' . $this->formatFailure($response));
+    $result = json_decode((string) $response->getBody(), 1);
+    if (json_last_error() !== JSON_ERROR_NONE) {
+      $this->fail("Failed to decode APIv4 JSON.\n" . $this->formatFailure($response));
+    }
+    return $result;
+  }
+
+  /**
+   * @param $expectCode
+   * @param \Psr\Http\Message\ResponseInterface|NULL $response
+   *   If NULL, then it uses the last response.
+   *
+   * @return $this
+   */
+  protected function assertStatusCode($expectCode, $response = NULL) {
+    $response = $this->resolveResponse($response);
+    $actualCode = $response->getStatusCode();
+    $fmt = $actualCode === $expectCode ? '' : $this->formatFailure($response);
+    $this->assertEquals($expectCode, $actualCode, "Expected HTTP response $expectCode. Received HTTP response $actualCode.\n$fmt");
+    return $this;
+  }
+
+  /**
+   * @param $expectType
+   * @param \Psr\Http\Message\ResponseInterface|NULL $response
+   *   If NULL, then it uses the last response.
+   *
+   * @return $this
+   */
+  protected function assertContentType($expectType, $response = NULL) {
+    $response = $this->resolveResponse($response);
+    list($actualType) = explode(';', $response->getHeader('Content-Type')[0]);
+    $fmt = $actualType === $expectType ? '' : $this->formatFailure($response);
+    $this->assertEquals($expectType, $actualType, "Expected content-type $expectType. Received content-type $actualType.\n$fmt");
+    return $this;
+  }
+
+  /**
+   * @param \Psr\Http\Message\ResponseInterface|NULL $response
+   * @return \Psr\Http\Message\ResponseInterface
+   */
+  protected function resolveResponse($response) {
+    return $response ?: $this->httpHistory[count($this->httpHistory) - 1]['response'];
+  }
+
+  /**
+   * Given that an HTTP request has yielded a failed response, format a blurb
+   * to summarize the details of the request+response.
+   *
+   * @param \Psr\Http\Message\ResponseInterface $response
+   *
+   * @return false|string
+   */
+  protected function formatFailure(\Psr\Http\Message\ResponseInterface $response) {
+    $details = [];
+
+    $condenseArray = function($v) {
+      if (is_array($v) && count($v) === 1 && isset($v[0])) {
+        return $v[0];
+      }
+      else {
+        return $v;
+      }
+    };
+
+    /** @var \Psr\Http\Message\RequestInterface $request */
+    $request = NULL;
+    foreach ($this->httpHistory as $item) {
+      if ($item['response'] === $response) {
+        $request = $item['request'];
+        break;
+      }
+    }
+
+    if ($request) {
+      $details['request'] = [
+        'uri' => (string) $request->getUri(),
+        'method' => $request->getMethod(),
+        // Most headers only have one item. JSON pretty-printer adds several newlines. This output is meant for dev's reading the error-log.
+        'headers' => array_map($condenseArray, $request->getHeaders()),
+        'body' => (string) $request->getBody(),
+      ];
+    }
+
+    $details['response'] = [
+      'status' => $response->getStatusCode() . ' ' . $response->getReasonPhrase(),
+      // Most headers only have one item. JSON pretty-printer adds several newlines. This output is meant for dev's reading the error-log.
+      'headers' => array_map($condenseArray, $response->getHeaders()),
+      'body' => (string) $response->getBody(),
+    ];
+
+    // If we get a full HTML document, then it'll be hard to read the error messages. Give an alternate rendition.
+    if (preg_match(';\<(!DOCTYPE|HTML);', $details['response']['body'])) {
+      // $details['bodyText'] = strip_tags($details['body']); // too much <style> noise
+      $details['response']['body'] = \CRM_Utils_String::htmlToText($details['response']['body']);
+    }
+
+    return json_encode($details, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+  }
+
+}
index 498eb2697fce29ce568ab5cfc674630900a94303..da6a15c9ccfcab98b325c8aa74029883e31bad8a 100644 (file)
       userModifiedAmount = cj(this).val();
       userModifiedAmount = Number(userModifiedAmount.replace(/[^0-9\.]+/g,""));
       if (userModifiedAmount < feeAmount) {
-        cj('#status_id').val(partiallyPaidStatusId).change();
+        cj('.crm-participant-form-block-status_id #status_id').val(partiallyPaidStatusId).change();
       }
     }
   );
           return true;
         }
       }
-      var userSubmittedStatus = cj('#status_id').val();
-      var statusLabel = cj('#status_id option:selected').text();
+      var userSubmittedStatus = cj('.crm-participant-form-block-status_id #status_id').val();
+      var statusLabel = cj('.crm-participant-form-block-status_id #status_id option:selected').text();
       if (userModifiedAmount < feeAmount && userSubmittedStatus != partiallyPaidStatusId) {
         var msg = "{/literal}{ts escape="js" 1="%1"}Payment amount is less than the amount owed. Expected participant status is 'Partially paid'. Are you sure you want to set the participant status to %1? Click OK to continue, Cancel to change your entries.{/ts}{literal}";
         var result = confirm(ts(msg, {1: statusLabel}));
   function sendNotification() {
     var notificationStatusIds = {/literal}"{$notificationStatusIds}"{literal};
     notificationStatusIds = notificationStatusIds.split(',');
-    if (cj.inArray(cj('select#status_id option:selected').val(), notificationStatusIds) > -1) {
+    if (cj.inArray(cj('.crm-participant-form-block-status_id select#status_id option:selected').val(), notificationStatusIds) > -1) {
       cj("#notify").show();
       cj("#is_notify").prop('checked', false);
     }