From f9bdf0620ef7c09cc0b1957a9dda6fa3bb7d3b3c Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 14 Mar 2018 15:00:15 +1300 Subject: [PATCH] CRM-21659 Add hook to CRM_Utils_System::redirect fixup CRM-21659 Add hook to CRM_Utils_System::redirect This renames the hook function to be stylistically more consistent. In other tests, the function name matches the hook name. --- CRM/Utils/Hook.php | 22 +++ CRM/Utils/System.php | 12 +- CRM/Utils/Url.php | 55 ++++++ composer.json | 3 +- composer.lock | 233 ++++++++++++++++++++++++- tests/phpunit/CRM/Utils/SystemTest.php | 71 ++++++++ 6 files changed, 392 insertions(+), 4 deletions(-) create mode 100644 CRM/Utils/Url.php diff --git a/CRM/Utils/Hook.php b/CRM/Utils/Hook.php index 6a4fe0012a..3127ff1d20 100644 --- a/CRM/Utils/Hook.php +++ b/CRM/Utils/Hook.php @@ -1635,6 +1635,28 @@ abstract class CRM_Utils_Hook { ); } + /** + * Alter redirect. + * + * This hook is called when the browser is being re-directed and allows the url + * to be altered. + * + * @param \Psr\Http\Message\UriInterface $url + * @param array $context + * Additional information about context + * - output - if this is 'json' then it will return json. + * + * @return null + * the return value is ignored + */ + public static function alterRedirect($url, &$context) { + return self::singleton()->invoke(array('url', 'context'), $url, + $context, self::$_nullObject, + self::$_nullObject, self::$_nullObject, self::$_nullObject, + 'civicrm_alterRedirect' + ); + } + /** * @param $varType * @param $var diff --git a/CRM/Utils/System.php b/CRM/Utils/System.php index d316eaf9d1..ca0f9a9b9f 100644 --- a/CRM/Utils/System.php +++ b/CRM/Utils/System.php @@ -433,8 +433,10 @@ class CRM_Utils_System { * * @param string $url * The URL to provide to the browser via the Location header. + * @param array $context + * Optional additional information for the hook. */ - public static function redirect($url = NULL) { + public static function redirect($url = NULL, $context = []) { if (!$url) { $url = self::url('civicrm/dashboard', 'reset=1'); } @@ -442,8 +444,14 @@ class CRM_Utils_System { // this is kinda hackish but not sure how to do it right $url = str_replace('&', '&', $url); + $context['output'] = CRM_Utils_Array::value('snippet', $_GET); + + $parsedUrl = CRM_Utils_Url::parseUrl($url); + CRM_Utils_Hook::alterRedirect($parsedUrl, $context); + $url = CRM_Utils_Url::unparseUrl($parsedUrl); + // If we are in a json context, respond appropriately - if (CRM_Utils_Array::value('snippet', $_GET) === 'json') { + if ($context['output'] === 'json') { CRM_Core_Page_AJAX::returnJsonResponse(array( 'status' => 'redirect', 'userContext' => $url, diff --git a/CRM/Utils/Url.php b/CRM/Utils/Url.php new file mode 100644 index 0000000000..0c97ae48ba --- /dev/null +++ b/CRM/Utils/Url.php @@ -0,0 +1,55 @@ +__toString(); + } + +} diff --git a/composer.json b/composer.json index b5d55ed5d5..9d81135dbb 100644 --- a/composer.json +++ b/composer.json @@ -54,7 +54,8 @@ "pear/Auth_SASL": "1.1.0", "pear/Net_SMTP": "1.6.*", "pear/Net_socket": "1.0.*", - "civicrm/civicrm-setup": "~0.2.0" + "civicrm/civicrm-setup": "~0.2.0", + "guzzlehttp/guzzle": "^6.3" }, "repositories": [ { diff --git a/composer.lock b/composer.lock index 5e1fb35b44..0cce99a2a8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "3bf4a12287208a8b232989680663e7a2", + "content-hash": "9c5441f5ce4c51ed3a8cc326693cd904", "packages": [ { "name": "civicrm/civicrm-cxn-rpc", @@ -182,6 +182,187 @@ "homepage": "http://code.google.com/p/phpquery/", "time": "2013-03-21T12:39:33+00:00" }, + { + "name": "guzzlehttp/guzzle", + "version": "6.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0 || ^5.0", + "psr/log": "^1.0" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.2-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2017-06-22T18:50:49+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2017-03-20T17:10:46+00:00" + }, { "name": "marcj/topsort", "version": "1.1.0", @@ -862,6 +1043,56 @@ ], "time": "2017-06-05T06:30:30+00:00" }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, { "name": "psr/log", "version": "1.0.0", diff --git a/tests/phpunit/CRM/Utils/SystemTest.php b/tests/phpunit/CRM/Utils/SystemTest.php index 2874bfe873..633abc64eb 100644 --- a/tests/phpunit/CRM/Utils/SystemTest.php +++ b/tests/phpunit/CRM/Utils/SystemTest.php @@ -1,5 +1,7 @@ assertEquals('http://example.com/?cms=UnitTests', CRM_Utils_System::evalUrl('http://example.com/?cms={uf}')); } + /** + * Test the redirect hook. + * + * @param string $url + * @param array $parsedUrl + * + * @dataProvider getURLs + */ + public function testRedirectHook($url, $parsedUrl) { + $this->hookClass->setHook('civicrm_alterRedirect', array($this, 'hook_civicrm_alterRedirect')); + try { + CRM_Utils_System::redirect($url, [ + 'expected' => $parsedUrl, + 'original' => $url + ]); + } + catch (CRM_Core_Exception $e) { + $this->assertEquals(ts('hook called'), $e->getMessage()); + return; + } + $this->fail('Exception should have been thrown if hook was called'); + } + + /** + * Hook for alterRedirect. + * + * We do some checks here. + * + * @param UriInterface $urlQuery + * @param array $context + * + * @throws \CRM_Core_Exception + */ + public function hook_civicrm_alterRedirect($urlQuery, $context) { + $this->assertEquals(CRM_Utils_Array::value('scheme', $context['expected']), $urlQuery->getScheme()); + $this->assertEquals(CRM_Utils_Array::value('host', $context['expected']), $urlQuery->getHost()); + $this->assertEquals(CRM_Utils_Array::value('query', $context['expected']), $urlQuery->getQuery()); + $this->assertEquals($context['original'], CRM_Utils_Url::unparseUrl($urlQuery)); + + throw new CRM_Core_Exception(ts('hook called')); + } + + /** + * Get urls for testing. + * + * @return array + */ + public function getURLs() { + return [ + ['https://example.com?ab=cd', [ + 'scheme' => 'https', + 'host' => 'example.com', + 'query' => 'ab=cd', + ]], + ['http://myuser:mypass@foo.bar:123/whiz?a=b&c=d', [ + 'scheme' => 'http', + 'host' => 'foo.bar', + 'port' => 123, + 'user' => 'myuser', + 'pass' => 'mypass', + 'path' => '/whiz', + 'query' => 'a=b&c=d', + ]], + ['/foo/bar', [ + 'path' => '/foo/bar' + ]], + ]; + } + } -- 2.25.1