CRM-21659 Add hook to CRM_Utils_System::redirect
authoreileen <emcnaughton@wikimedia.org>
Wed, 14 Mar 2018 02:00:15 +0000 (15:00 +1300)
committereileen <emcnaughton@wikimedia.org>
Wed, 14 Mar 2018 02:23:07 +0000 (15:23 +1300)
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
CRM/Utils/System.php
CRM/Utils/Url.php [new file with mode: 0644]
composer.json
composer.lock
tests/phpunit/CRM/Utils/SystemTest.php

index 6a4fe0012ab1838829b1b1d58307dcb65ef5e534..3127ff1d207ccc4d3913eeb8b4b0d99c11e96fa9 100644 (file)
@@ -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
index d316eaf9d1f9e2971ef79e4b5dcc6995851250b8..ca0f9a9b9fcb86f336c1c98a85bd7cc6b856ce98 100644 (file)
@@ -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('&amp;', '&', $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 (file)
index 0000000..0c97ae4
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7                                                |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2017                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+use GuzzleHttp\Psr7\Uri;
+use Psr\Http\Message\UriInterface;
+
+class CRM_Utils_Url {
+
+  /**
+   * Parse url to a UriInterface.
+   *
+   * @param string $url
+   *
+   * @return UriInterface
+   */
+  public static function parseUrl($url) {
+    return new Uri($url);
+  }
+
+  /**
+   * Unparse url back to a string.
+   *
+   * @param UriInterface $parsed
+   *
+   * @return string
+   */
+  public static function unparseUrl(UriInterface $parsed) {
+    return $parsed->__toString();
+  }
+
+}
index b5d55ed5d55790c3595f86e068ecef069d5b8ba4..9d81135dbbec3aef07669abd4aa19d64343e6c29 100644 (file)
@@ -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": [
     {
index 5e1fb35b44aa379e4d1bcac642355aa0edadd2ac..0cce99a2a89b969d505f497708f6b7606efa8dec 100644 (file)
@@ -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",
             "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",
             ],
             "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",
index 2874bfe873da2b64eb1f221b93e742f8e566b689..633abc64ebc6ece08f9376a88d91c440aa78b343 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use Psr\Http\Message\UriInterface;
+
 /**
  * Class CRM_Utils_SystemTest
  * @group headless
@@ -35,4 +37,73 @@ class CRM_Utils_SystemTest extends CiviUnitTestCase {
     $this->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'
+      ]],
+    ];
+  }
+
 }