From f314993043bb125bfdfc7eecb5e4d91e4747a7e0 Mon Sep 17 00:00:00 2001 From: Jacob Pilon Date: Mon, 12 Oct 2020 05:15:22 -0400 Subject: [PATCH 1/8] Add "current_full_url" to the cascade contextual variables --- src/View/Cascade.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/View/Cascade.php b/src/View/Cascade.php index cb39126c3a..199eac87e6 100644 --- a/src/View/Cascade.php +++ b/src/View/Cascade.php @@ -172,6 +172,7 @@ private function contextualVariables() // Request 'current_url' => $this->request->url(), + 'current_full_url' => $this->request->fullUrl(), 'current_uri' => URL::format($this->request->path()), 'get_post' => Arr::sanitize($this->request->all()), 'get' => Arr::sanitize($this->request->query->all()), From e32f170ebfd49fb56ea49c24dba91092a6092710 Mon Sep 17 00:00:00 2001 From: Jacob Pilon Date: Mon, 12 Oct 2020 05:16:01 -0400 Subject: [PATCH 2/8] Update request related cascade unit test --- tests/View/CascadeTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/View/CascadeTest.php b/tests/View/CascadeTest.php index 8616cd0a82..75c9aaea9b 100644 --- a/tests/View/CascadeTest.php +++ b/tests/View/CascadeTest.php @@ -122,10 +122,11 @@ public function it_hydrates_dates() /** @test */ public function it_hydrates_request_variables() { - $this->get('/test'); + $this->get('/test?test=test'); tap($this->cascade()->hydrate()->toArray(), function ($cascade) { $this->assertEquals('http://test.com/test', $cascade['current_url']); + $this->assertEquals('http://test.com/test?test=test', $cascade['current_full_url']); $this->assertEquals('/test', $cascade['current_uri']); }); } From fabdb922a70ab90caf7d6ba9d91276d10d5d65ab Mon Sep 17 00:00:00 2001 From: Jacob Pilon Date: Mon, 12 Oct 2020 05:16:51 -0400 Subject: [PATCH 3/8] Add 3 modifiers: addQueryParam, setQueryParam and removeQueryParam --- src/Modifiers/CoreModifiers.php | 73 +++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/Modifiers/CoreModifiers.php b/src/Modifiers/CoreModifiers.php index e68447ebb8..b54762be63 100644 --- a/src/Modifiers/CoreModifiers.php +++ b/src/Modifiers/CoreModifiers.php @@ -37,6 +37,28 @@ public function add($value, $params, $context) return $value + $this->getMathModifierNumber($params, $context); } + /** + * Adds a query param matching the specified key/value pair. + * + * @param $value + * @param $params + * @return string + */ + public function addQueryParam($value, $params) + { + if (isset($params[0])) { + // If a "?" is present in the URL, it means we should prepend "&" to the query param. Else, prepend "?". + $character = (strpos($value, '?') !== false) ? '&' : '?'; + + // Build the query param. If the second param is not set, just set the value as empty. + $queryParam = "{$params[0]}=" . ($params[1] ?? ''); + + return "{$value}{$character}{$queryParam}"; + } + + return $value; + } + /** * Creates a sentence list from the given array and the ability to set the glue. * @@ -1521,6 +1543,31 @@ public function removeLeft($value, $params) return Stringy::removeLeft($value, Arr::get($params, 0)); } + /** + * Removes a query param matching the specified key if it exists. + * + * @param $value + * @param $params + * @return string + */ + public function removeQueryParam($value, $params) + { + if (isset($params[0])) { + // Remove query params from the URL. + $url = strtok($value, '?'); + + // Build an associative array based on the query string. + parse_str(parse_url($value, PHP_URL_QUERY), $queryAssociativeArray); + + // Remove the query param matching the specified key. + unset($queryAssociativeArray[$params[0]]); + + return $url . (empty($queryAssociativeArray) ? '' : '?' . http_build_query($queryAssociativeArray)); + } + + return $value; + } + /** * Returns a new string with the suffix $params[0] removed, if present. * @@ -1694,6 +1741,32 @@ public function sentenceList($value, $params) return Str::makeSentenceList($value, $glue, $oxford_comma); } + /** + * Sets a query param matching the specified key/value pair. + * If the key exists, its value gets updated. Else, the key/value pair gets added. + * + * @param $value + * @param $params + * @return string + */ + public function setQueryParam($value, $params) + { + if (isset($params[0])) { + // Remove query params from the URL. + $url = strtok($value, '?'); + + // Build an associative array based on the query string. + parse_str(parse_url($value, PHP_URL_QUERY), $queryAssociativeArray); + + // Update the existing param that matches the specified key, or add it if it doesn't exist. + $queryAssociativeArray[$params[0]] = $params[1] ?? ''; + + return "{$url}?" . http_build_query($queryAssociativeArray); + } + + return $value; + } + /** * Because sometimes you just gotta /shrug. * From eb6986c7285b529f3fd29fbd19150c476d90eec9 Mon Sep 17 00:00:00 2001 From: Jacob Pilon Date: Mon, 12 Oct 2020 05:17:16 -0400 Subject: [PATCH 4/8] Add query param modifiers unit tests --- tests/Modifiers/AddQueryParamTest.php | 35 +++++++++++++++++++ tests/Modifiers/RemoveQueryParamTest.php | 43 +++++++++++++++++++++++ tests/Modifiers/SetQueryParamTest.php | 44 ++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 tests/Modifiers/AddQueryParamTest.php create mode 100644 tests/Modifiers/RemoveQueryParamTest.php create mode 100644 tests/Modifiers/SetQueryParamTest.php diff --git a/tests/Modifiers/AddQueryParamTest.php b/tests/Modifiers/AddQueryParamTest.php new file mode 100644 index 0000000000..e3124ff5f8 --- /dev/null +++ b/tests/Modifiers/AddQueryParamTest.php @@ -0,0 +1,35 @@ +assertSame("{$this->baseUrl}?q=", $this->modify($this->baseUrl, ['q'])); + $this->assertSame("{$this->baseUrl}?q=test", $this->modify($this->baseUrl, $this->queryParam)); + $this->assertSame("{$this->baseUrl}?sourceid=chrome&q=test", $this->modify("{$this->baseUrl}?sourceid=chrome", $this->queryParam)); + } + + /** @test */ + public function it_does_nothing_if_no_parameters_are_passed() + { + $this->assertSame($this->baseUrl, $this->modify($this->baseUrl)); + } + + private function modify(string $url, ?array $queryParam = null) + { + if (is_null($queryParam)) { + return Modify::value($url)->addQueryParam()->fetch(); + } + + return Modify::value($url)->addQueryParam($queryParam)->fetch(); + } +} diff --git a/tests/Modifiers/RemoveQueryParamTest.php b/tests/Modifiers/RemoveQueryParamTest.php new file mode 100644 index 0000000000..2add849f0d --- /dev/null +++ b/tests/Modifiers/RemoveQueryParamTest.php @@ -0,0 +1,43 @@ +assertSame($this->baseUrl, $this->modify("{$this->baseUrl}?q=statamic", $this->queryParamKey)); + $this->assertSame($this->baseUrl, $this->modify("{$this->baseUrl}?q=statamic", $this->queryParamKey)); + $this->assertSame("{$this->baseUrl}?sourceid=chrome", $this->modify("{$this->baseUrl}?q=statamic&sourceid=chrome", $this->queryParamKey)); + $this->assertSame("{$this->baseUrl}?sourceid=chrome", $this->modify("{$this->baseUrl}?sourceid=chrome&q=statamic", $this->queryParamKey)); + } + + /** @test */ + public function it_does_nothing_if_the_query_param_key_does_not_exist() + { + $this->assertSame($this->baseUrl, $this->modify($this->baseUrl, $this->queryParamKey)); + $this->assertSame("{$this->baseUrl}?sourceid=chrome", $this->modify("{$this->baseUrl}?sourceid=chrome", $this->queryParamKey)); + } + + /** @test */ + public function it_does_nothing_if_no_parameters_are_passed() + { + $this->assertSame($this->baseUrl, $this->modify($this->baseUrl)); + } + + private function modify(string $url, ?string $queryParamKey = null) + { + if (is_null($queryParamKey)) { + return Modify::value($url)->removeQueryParam()->fetch(); + } + + return Modify::value($url)->removeQueryParam($queryParamKey)->fetch(); + } +} diff --git a/tests/Modifiers/SetQueryParamTest.php b/tests/Modifiers/SetQueryParamTest.php new file mode 100644 index 0000000000..2eb8dd6b0b --- /dev/null +++ b/tests/Modifiers/SetQueryParamTest.php @@ -0,0 +1,44 @@ +assertSame("{$this->baseUrl}?q=", $this->modify("{$this->baseUrl}?q=statamic", ['q'])); + $this->assertSame("{$this->baseUrl}?q=test", $this->modify("{$this->baseUrl}?q=statamic", $this->queryParam)); + $this->assertSame("{$this->baseUrl}?q=test&sourceid=chrome", $this->modify("{$this->baseUrl}?q=statamic&sourceid=chrome", $this->queryParam)); + $this->assertSame("{$this->baseUrl}?sourceid=chrome&q=test", $this->modify("{$this->baseUrl}?sourceid=chrome&q=statamic", $this->queryParam)); + } + + /** @test */ + public function it_adds_a_non_existant_query_param() + { + $this->assertSame("{$this->baseUrl}?q=", $this->modify($this->baseUrl, ['q'])); + $this->assertSame("{$this->baseUrl}?q=test", $this->modify($this->baseUrl, $this->queryParam)); + $this->assertSame("{$this->baseUrl}?sourceid=chrome&q=test", $this->modify("{$this->baseUrl}?sourceid=chrome", $this->queryParam)); + } + + /** @test */ + public function it_does_nothing_if_no_parameters_are_passed() + { + $this->assertSame($this->baseUrl, $this->modify($this->baseUrl)); + } + + private function modify(string $url, ?array $queryParam = null) + { + if (is_null($queryParam)) { + return Modify::value($url)->setQueryParam()->fetch(); + } + + return Modify::value($url)->setQueryParam($queryParam)->fetch(); + } +} From c17493446acadb369b570c1034bf85a96f703eb8 Mon Sep 17 00:00:00 2001 From: Jacob Pilon Date: Mon, 12 Oct 2020 13:25:53 -0400 Subject: [PATCH 5/8] Add accountability for a possible anchor on all 3 modifiers --- src/Modifiers/CoreModifiers.php | 35 ++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/Modifiers/CoreModifiers.php b/src/Modifiers/CoreModifiers.php index b54762be63..b4d0304520 100644 --- a/src/Modifiers/CoreModifiers.php +++ b/src/Modifiers/CoreModifiers.php @@ -47,13 +47,20 @@ public function add($value, $params, $context) public function addQueryParam($value, $params) { if (isset($params[0])) { + // Remove anchor from the URL. + $url = strtok($value, '#'); + + // Get the anchor value an preprend it with a "#" if a value is retrieved. + $fragment = parse_url($value, PHP_URL_FRAGMENT); + $anchor = is_null($fragment) ? '' : "#{$fragment}"; + // If a "?" is present in the URL, it means we should prepend "&" to the query param. Else, prepend "?". $character = (strpos($value, '?') !== false) ? '&' : '?'; // Build the query param. If the second param is not set, just set the value as empty. $queryParam = "{$params[0]}=" . ($params[1] ?? ''); - return "{$value}{$character}{$queryParam}"; + $value = "{$url}{$character}{$queryParam}{$anchor}"; } return $value; @@ -1553,16 +1560,23 @@ public function removeLeft($value, $params) public function removeQueryParam($value, $params) { if (isset($params[0])) { - // Remove query params from the URL. + // Remove query params (and any following anchor) from the URL. $url = strtok($value, '?'); + $url = strtok($url, '#'); + + // Parse the URL to retrieve the possible query string and anchor. + $parsedUrl = parse_url($value); + + // Get the anchor value an preprend it with a "#" if a value is retrieved. + $anchor = isset($parsedUrl['fragment']) ? "#{$parsedUrl['fragment']}" : ''; // Build an associative array based on the query string. - parse_str(parse_url($value, PHP_URL_QUERY), $queryAssociativeArray); + parse_str($parsedUrl['query'] ?? '', $queryAssociativeArray); // Remove the query param matching the specified key. unset($queryAssociativeArray[$params[0]]); - return $url . (empty($queryAssociativeArray) ? '' : '?' . http_build_query($queryAssociativeArray)); + $value = $url . (empty($queryAssociativeArray) ? '' : '?' . http_build_query($queryAssociativeArray)) . $anchor; } return $value; @@ -1752,16 +1766,23 @@ public function sentenceList($value, $params) public function setQueryParam($value, $params) { if (isset($params[0])) { - // Remove query params from the URL. + // Remove query params (and any following anchor) from the URL. $url = strtok($value, '?'); + $url = strtok($url, '#'); + + // Parse the URL to retrieve the possible query string and anchor. + $parsedUrl = parse_url($value); + + // Get the anchor value an preprend it with a "#" if a value is retrieved. + $anchor = isset($parsedUrl['fragment']) ? "#{$parsedUrl['fragment']}" : ''; // Build an associative array based on the query string. - parse_str(parse_url($value, PHP_URL_QUERY), $queryAssociativeArray); + parse_str($parsedUrl['query'] ?? '', $queryAssociativeArray); // Update the existing param that matches the specified key, or add it if it doesn't exist. $queryAssociativeArray[$params[0]] = $params[1] ?? ''; - return "{$url}?" . http_build_query($queryAssociativeArray); + $value = "{$url}?" . http_build_query($queryAssociativeArray) . $anchor; } return $value; From 032a85b1c754816cae7c5035a41fd570710fd16e Mon Sep 17 00:00:00 2001 From: Jacob Pilon Date: Mon, 12 Oct 2020 13:26:29 -0400 Subject: [PATCH 6/8] Add unit tests checking cases with an anchor --- tests/Modifiers/AddQueryParamTest.php | 2 ++ tests/Modifiers/RemoveQueryParamTest.php | 9 ++------- tests/Modifiers/SetQueryParamTest.php | 2 ++ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/Modifiers/AddQueryParamTest.php b/tests/Modifiers/AddQueryParamTest.php index e3124ff5f8..1f95968224 100644 --- a/tests/Modifiers/AddQueryParamTest.php +++ b/tests/Modifiers/AddQueryParamTest.php @@ -16,12 +16,14 @@ public function it_adds_a_new_query_param() $this->assertSame("{$this->baseUrl}?q=", $this->modify($this->baseUrl, ['q'])); $this->assertSame("{$this->baseUrl}?q=test", $this->modify($this->baseUrl, $this->queryParam)); $this->assertSame("{$this->baseUrl}?sourceid=chrome&q=test", $this->modify("{$this->baseUrl}?sourceid=chrome", $this->queryParam)); + $this->assertSame("{$this->baseUrl}?q=test#test", $this->modify("{$this->baseUrl}#test", $this->queryParam)); } /** @test */ public function it_does_nothing_if_no_parameters_are_passed() { $this->assertSame($this->baseUrl, $this->modify($this->baseUrl)); + $this->assertSame("{$this->baseUrl}#test", $this->modify("{$this->baseUrl}#test")); } private function modify(string $url, ?array $queryParam = null) diff --git a/tests/Modifiers/RemoveQueryParamTest.php b/tests/Modifiers/RemoveQueryParamTest.php index 2add849f0d..ca2d15cc68 100644 --- a/tests/Modifiers/RemoveQueryParamTest.php +++ b/tests/Modifiers/RemoveQueryParamTest.php @@ -14,7 +14,7 @@ class RemoveQueryParamTest extends TestCase public function it_removes_an_existing_query_param() { $this->assertSame($this->baseUrl, $this->modify("{$this->baseUrl}?q=statamic", $this->queryParamKey)); - $this->assertSame($this->baseUrl, $this->modify("{$this->baseUrl}?q=statamic", $this->queryParamKey)); + $this->assertSame("{$this->baseUrl}#test", $this->modify("{$this->baseUrl}?q=statamic#test", $this->queryParamKey)); $this->assertSame("{$this->baseUrl}?sourceid=chrome", $this->modify("{$this->baseUrl}?q=statamic&sourceid=chrome", $this->queryParamKey)); $this->assertSame("{$this->baseUrl}?sourceid=chrome", $this->modify("{$this->baseUrl}?sourceid=chrome&q=statamic", $this->queryParamKey)); } @@ -23,15 +23,10 @@ public function it_removes_an_existing_query_param() public function it_does_nothing_if_the_query_param_key_does_not_exist() { $this->assertSame($this->baseUrl, $this->modify($this->baseUrl, $this->queryParamKey)); + $this->assertSame("{$this->baseUrl}#test", $this->modify("{$this->baseUrl}#test", $this->queryParamKey)); $this->assertSame("{$this->baseUrl}?sourceid=chrome", $this->modify("{$this->baseUrl}?sourceid=chrome", $this->queryParamKey)); } - /** @test */ - public function it_does_nothing_if_no_parameters_are_passed() - { - $this->assertSame($this->baseUrl, $this->modify($this->baseUrl)); - } - private function modify(string $url, ?string $queryParamKey = null) { if (is_null($queryParamKey)) { diff --git a/tests/Modifiers/SetQueryParamTest.php b/tests/Modifiers/SetQueryParamTest.php index 2eb8dd6b0b..9f572a3eb7 100644 --- a/tests/Modifiers/SetQueryParamTest.php +++ b/tests/Modifiers/SetQueryParamTest.php @@ -15,6 +15,7 @@ public function it_updates_an_existing_query_param() { $this->assertSame("{$this->baseUrl}?q=", $this->modify("{$this->baseUrl}?q=statamic", ['q'])); $this->assertSame("{$this->baseUrl}?q=test", $this->modify("{$this->baseUrl}?q=statamic", $this->queryParam)); + $this->assertSame("{$this->baseUrl}?q=test#test", $this->modify("{$this->baseUrl}?q=statamic#test", $this->queryParam)); $this->assertSame("{$this->baseUrl}?q=test&sourceid=chrome", $this->modify("{$this->baseUrl}?q=statamic&sourceid=chrome", $this->queryParam)); $this->assertSame("{$this->baseUrl}?sourceid=chrome&q=test", $this->modify("{$this->baseUrl}?sourceid=chrome&q=statamic", $this->queryParam)); } @@ -24,6 +25,7 @@ public function it_adds_a_non_existant_query_param() { $this->assertSame("{$this->baseUrl}?q=", $this->modify($this->baseUrl, ['q'])); $this->assertSame("{$this->baseUrl}?q=test", $this->modify($this->baseUrl, $this->queryParam)); + $this->assertSame("{$this->baseUrl}?q=test#test", $this->modify("{$this->baseUrl}#test", $this->queryParam)); $this->assertSame("{$this->baseUrl}?sourceid=chrome&q=test", $this->modify("{$this->baseUrl}?sourceid=chrome", $this->queryParam)); } From a7068ca5a80072831edcfd48c8a9639498519e30 Mon Sep 17 00:00:00 2001 From: Jacob Pilon Date: Mon, 12 Oct 2020 18:23:22 -0400 Subject: [PATCH 7/8] Comply to StyleCI --- src/Modifiers/CoreModifiers.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Modifiers/CoreModifiers.php b/src/Modifiers/CoreModifiers.php index b4d0304520..d587dd6aac 100644 --- a/src/Modifiers/CoreModifiers.php +++ b/src/Modifiers/CoreModifiers.php @@ -58,7 +58,7 @@ public function addQueryParam($value, $params) $character = (strpos($value, '?') !== false) ? '&' : '?'; // Build the query param. If the second param is not set, just set the value as empty. - $queryParam = "{$params[0]}=" . ($params[1] ?? ''); + $queryParam = "{$params[0]}=".($params[1] ?? ''); $value = "{$url}{$character}{$queryParam}{$anchor}"; } @@ -1576,7 +1576,7 @@ public function removeQueryParam($value, $params) // Remove the query param matching the specified key. unset($queryAssociativeArray[$params[0]]); - $value = $url . (empty($queryAssociativeArray) ? '' : '?' . http_build_query($queryAssociativeArray)) . $anchor; + $value = $url.(empty($queryAssociativeArray) ? '' : '?'.http_build_query($queryAssociativeArray)).$anchor; } return $value; @@ -1782,7 +1782,7 @@ public function setQueryParam($value, $params) // Update the existing param that matches the specified key, or add it if it doesn't exist. $queryAssociativeArray[$params[0]] = $params[1] ?? ''; - $value = "{$url}?" . http_build_query($queryAssociativeArray) . $anchor; + $value = "{$url}?".http_build_query($queryAssociativeArray).$anchor; } return $value; From 7ea5169fb6c43f2f564ac77e191c77caca4ed8f1 Mon Sep 17 00:00:00 2001 From: Jacob Pilon Date: Mon, 12 Oct 2020 23:25:35 -0400 Subject: [PATCH 8/8] Restore a mistakenly deleted test --- tests/Modifiers/RemoveQueryParamTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/Modifiers/RemoveQueryParamTest.php b/tests/Modifiers/RemoveQueryParamTest.php index ca2d15cc68..83174366ab 100644 --- a/tests/Modifiers/RemoveQueryParamTest.php +++ b/tests/Modifiers/RemoveQueryParamTest.php @@ -27,6 +27,12 @@ public function it_does_nothing_if_the_query_param_key_does_not_exist() $this->assertSame("{$this->baseUrl}?sourceid=chrome", $this->modify("{$this->baseUrl}?sourceid=chrome", $this->queryParamKey)); } + /** @test */ + public function it_does_nothing_if_no_parameters_are_passed() + { + $this->assertSame($this->baseUrl, $this->modify($this->baseUrl)); + } + private function modify(string $url, ?string $queryParamKey = null) { if (is_null($queryParamKey)) {