From 76b2a807ffacf2e036c4da816baf2097b71cfb0f Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Mon, 24 Jul 2023 22:01:38 -0700 Subject: [PATCH] Smarty - Add support for `{url}frontend://civicrm/profile{/url}` (etal) --- CRM/Core/Smarty/plugins/block.url.php | 60 ++++++++++++ .../CRM/Core/Smarty/plugins/UrlTest.php | 92 +++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 CRM/Core/Smarty/plugins/block.url.php create mode 100644 tests/phpunit/CRM/Core/Smarty/plugins/UrlTest.php diff --git a/CRM/Core/Smarty/plugins/block.url.php b/CRM/Core/Smarty/plugins/block.url.php new file mode 100644 index 0000000000..71814af2a1 --- /dev/null +++ b/CRM/Core/Smarty/plugins/block.url.php @@ -0,0 +1,60 @@ +addVars($params); + + // This could be neat, but see discussion in CRM_Core_Smarty_plugins_UrlTest for why it's currently off. + // $url->setVarsCallback([$smarty, 'get_template_vars']); + + if ($assign !== NULL) { + $smarty->assign([$assign => $url]); + return ''; + } + else { + return $url; + } +} diff --git a/tests/phpunit/CRM/Core/Smarty/plugins/UrlTest.php b/tests/phpunit/CRM/Core/Smarty/plugins/UrlTest.php new file mode 100644 index 0000000000..b9a2141391 --- /dev/null +++ b/tests/phpunit/CRM/Core/Smarty/plugins/UrlTest.php @@ -0,0 +1,92 @@ +useTransaction(); + } + + /** + * @return array + */ + public function urlCases() { + $literal = function(string $s) { + return '!' . preg_quote($s, '!') . '!'; + }; + + $cases = []; + $cases[] = [ + // Generate an ordinary, HTML-style URL. + $literal('q=civicrm/profile/view&id=123&gid=456'), + '{url}//civicrm/profile/view?id=123&gid=456{/url}', + ]; + $cases[] = [ + // Here, we assign the plain-text variable and then use it for JS expression + '!window.location = ".*q=civicrm/profile/view&id=123&gid=456"!', + '{url assign=myUrl flags=t}//civicrm/profile/view?id=123&gid=456{/url}' . + 'window.location = "{$myUrl}";', + ]; + $cases[] = [ + $literal('q=civicrm/profile/view&id=999&message=hello+world'), + '{url 1="999" 2="hello world"}//civicrm/profile/view?id=[1]&message=[2]{/url}', + ]; + $cases[] = [ + $literal('q=civicrm/profile/view&id=123&message=hello+world'), + '{url msg="hello world"}//civicrm/profile/view?id=123&message=[msg]{/url}', + ]; + $cases[] = [ + // Define a temporary variable for use in the URL. + $literal('q=civicrm/profile/view&id=123&message=this+%26+that'), + '{url msg="this & that"}//civicrm/profile/view?id=123&message=[msg]{/url}', + ]; + $cases[] = [ + // We have a Smarty variable which already included escaped data. Smarty should do substitution. + $literal('q=civicrm/profile/view&id=123&message=this+%2B+that'), + '{assign var=msg value="this+%2B+that"}' . + '{url flags=%}//civicrm/profile/view?id=123&message={$msg}{/url}', + ]; + $cases[] = [ + // Generate client-side route (with Angular path and params) + $literal('q=civicrm/a/#/mailing/100?angularDebug=1'), + '{url id=100}backend://civicrm/a/#/mailing/[id]?angularDebug=1{/url}', + ]; + + // This example is neat - you just replace `{$msg}` with `[msg]`, and then you get encoded URL data. + // But... it's pretty shallow. You can't use Smarty expressions or modifiers. Additionally, + // enabling this mode increases the risk of accidental collisions between Smarty variables + // and deep-form-params. So I've left it disabled for now. + // + // $cases[] = [ + // // We have a Smarty variable with canonical (unescaped) data. Use it as URL variable. + // $literal('q=civicrm/profile/view&id=123&message=this+%2B+that'), + // '{assign var=msg value="this + that"}' . + // '{url}//civicrm/profile/view?id=123&message=[msg]{/url}', + // ]; + + // return CRM_Utils_Array::subset($cases, [2]); + return $cases; + } + + /** + * @dataProvider urlCases + * @param string $expected + * @param string $input + */ + public function testUrl($expected, $input) { + $smarty = CRM_Core_Smarty::singleton(); + $actual = $smarty->fetch('string:' . $input); + $this->assertRegExp($expected, $actual, "Process input=[$input]"); + } + +} -- 2.25.1