From d275a55368f1de2f6ec9c019862180facc72de83 Mon Sep 17 00:00:00 2001 From: Camilo Rodriguez Date: Thu, 23 Nov 2017 11:24:30 +0000 Subject: [PATCH 1/4] HC-58: Add API Wrapper to Change Params Array Structure A bug injected on CiviCRM v4.7.19 caused chained API calls using '$value.' to chain values to previous call to be interpreted as operators with syntax: array('OP' => $val). This only happened if the first parameter for the chained API call uses '$value.'. Fixed by implementing civicrm_apiWrappers hook to preprocess parameters array to make sure the first parameter of a chained API call is is not a chained value. --- CRM/Booking/APIWrapper.php | 53 ++++++++++++++++++++++++++++++++++++++ booking.php | 12 +++++++++ 2 files changed, 65 insertions(+) create mode 100755 CRM/Booking/APIWrapper.php mode change 100644 => 100755 booking.php diff --git a/CRM/Booking/APIWrapper.php b/CRM/Booking/APIWrapper.php new file mode 100755 index 00000000..c0df7ea1 --- /dev/null +++ b/CRM/Booking/APIWrapper.php @@ -0,0 +1,53 @@ +fixParametersArray($apiRequest['params']); + return $apiRequest; + } + + /** + * alter the result before returning it to the caller. + * + * @param array $apiRequest + * @param array $result + * + * @return array + */ + public function toApiOutput($apiRequest, $result) { + return $result; + } + + /** + * Fixes parameters array so that if chained API calls are made, any chained + * fields with '$value.' are moved to the end of the array. + * + * @param array $params + */ + private function fixParametersArray(&$params) { + $chainedValues = array(); + + foreach ($params as $parameter => &$value) { + if (stripos($parameter, 'api.') === 0 && is_array($value)) { + $this->fixParametersArray($value); + } + elseif (stripos($value, '$value.') === 0) { + unset($params[$parameter]); + $chainedValues[$parameter] = $value; + } + } + + $params = array_merge($params, $chainedValues); + } + +} \ No newline at end of file diff --git a/booking.php b/booking.php old mode 100644 new mode 100755 index fa718f34..65e38cbc --- a/booking.php +++ b/booking.php @@ -558,3 +558,15 @@ function booking_civicrm_alterAPIPermissions($entity, $action, &$params, &$permi } } + +/** + * Implements hook _civicrm_apiWrappers() + * + * @param array $wrappers + * @param $apiRequest + */ +function booking_civicrm_apiWrappers(&$wrappers, $apiRequest) { + if ($apiRequest['entity'] == 'Resource' && $apiRequest['action'] == 'get') { + $wrappers[] = new CRM_Booking_APIWrapper(); + } +} \ No newline at end of file From ca36474447737b8c60bcc01a6d7393effa25120a Mon Sep 17 00:00:00 2001 From: Camilo Rodriguez Date: Thu, 23 Nov 2017 12:16:49 +0000 Subject: [PATCH 2/4] HC-58: Handle Case Where All Parameters are Chained Values As what the implemented fix does is move chained values to the last position of the array, if all the parameters of the call are chained values, the problem would still present itself. Added check to verify if all values are chained and if so, added sequential flag with 0 value as parameter. --- CRM/Booking/APIWrapper.php | 14 +++++++++++++- booking.php | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CRM/Booking/APIWrapper.php b/CRM/Booking/APIWrapper.php index c0df7ea1..b8509b67 100755 --- a/CRM/Booking/APIWrapper.php +++ b/CRM/Booking/APIWrapper.php @@ -1,7 +1,7 @@ &$value) { if (stripos($parameter, 'api.') === 0 && is_array($value)) { + $allChainedValues = true; + + foreach ($value as $chainedParameter => $chainedValue) { + if (stripos($value, '$value.') !== 0) { + $allChainedValues = false; + } + } + + if ($allChainedValues) { + $value['sequential'] = 0; + } + $this->fixParametersArray($value); } elseif (stripos($value, '$value.') === 0) { diff --git a/booking.php b/booking.php index 65e38cbc..9ae280aa 100755 --- a/booking.php +++ b/booking.php @@ -560,13 +560,13 @@ function booking_civicrm_alterAPIPermissions($entity, $action, &$params, &$permi } /** - * Implements hook _civicrm_apiWrappers() + * Implements hook_civicrm_apiWrappers() * * @param array $wrappers - * @param $apiRequest + * @param array $apiRequest */ function booking_civicrm_apiWrappers(&$wrappers, $apiRequest) { if ($apiRequest['entity'] == 'Resource' && $apiRequest['action'] == 'get') { $wrappers[] = new CRM_Booking_APIWrapper(); } -} \ No newline at end of file +} From 12eeea2458d9d2c682c6be5f93ad7da21078a908 Mon Sep 17 00:00:00 2001 From: Camilo Rodriguez Date: Thu, 23 Nov 2017 13:16:45 +0000 Subject: [PATCH 3/4] HC-58: Add Newline to End of File --- CRM/Booking/APIWrapper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CRM/Booking/APIWrapper.php b/CRM/Booking/APIWrapper.php index b8509b67..8b399dfb 100755 --- a/CRM/Booking/APIWrapper.php +++ b/CRM/Booking/APIWrapper.php @@ -62,4 +62,4 @@ private function fixParametersArray(&$params) { $params = array_merge($params, $chainedValues); } -} \ No newline at end of file +} From f635ff52f465973ca35cdca9210eef5b84cd249f Mon Sep 17 00:00:00 2001 From: Camilo Rodriguez Date: Thu, 23 Nov 2017 14:48:23 +0000 Subject: [PATCH 4/4] HC-58: Fix Cycle to Check if All Values are Chained Cycle to check if values of a chained call were all chained was botched, as it was checking the whole array of parameters on each cycle, instead of each individual value. --- CRM/Booking/APIWrapper.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CRM/Booking/APIWrapper.php b/CRM/Booking/APIWrapper.php index 8b399dfb..18f1f44e 100755 --- a/CRM/Booking/APIWrapper.php +++ b/CRM/Booking/APIWrapper.php @@ -13,6 +13,7 @@ class CRM_Booking_APIWrapper implements API_Wrapper { */ public function fromApiInput($apiRequest) { $this->fixParametersArray($apiRequest['params']); + return $apiRequest; } @@ -39,11 +40,11 @@ private function fixParametersArray(&$params) { foreach ($params as $parameter => &$value) { if (stripos($parameter, 'api.') === 0 && is_array($value)) { - $allChainedValues = true; + $allChainedValues = TRUE; foreach ($value as $chainedParameter => $chainedValue) { - if (stripos($value, '$value.') !== 0) { - $allChainedValues = false; + if (stripos($chainedValue, '$value.') !== 0) { + $allChainedValues = FALSE; } }