Skip to content

Commit 221fc70

Browse files
authored
feat: Add option to disable previous URL storage (#82)
* fix: Disable save previous URL * docs: Update configuration.md * feat: Add in config parameter `storePreviousURL`
1 parent 463a474 commit 221fc70

File tree

5 files changed

+176
-0
lines changed

5 files changed

+176
-0
lines changed

docs/configuration.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,13 @@ Since these decorators are used automatically in the `development` mode (or to b
3636
We can add the defined string as an `id` or `class` to the html tag.
3737

3838
In the `production` environment these decorators are ignored by design. So this is useful only for the `development` mode.
39+
40+
### $storePreviousURL
41+
42+
Specifies whether the HTMX request URL should be stored in the session, for use with the `previous_url()` helper function.
43+
For more information, see the [user guide](https://codeigniter.com/user_guide/helpers/url_helper.html#previous_url).
44+
45+
Basically, if you use HTMX extensively, including for navigating your site, you will probably want to leave it as `true`,
46+
and in cases where storing the request is not desirable, even if it uses HTMX, you can use custom header, to indicate the
47+
AJAX call or [ajax-header](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/ajax-header/README.md) extension,
48+
which will add the necessary headers automatically. URLs from AJAX requests are always excluded from session storage.

src/CodeIgniter.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
namespace Michalsn\CodeIgniterHtmx;
4+
5+
use CodeIgniter\CodeIgniter as BaseCodeIgniter;
6+
use CodeIgniter\HTTP\CLIRequest;
7+
use CodeIgniter\HTTP\DownloadResponse;
8+
use CodeIgniter\HTTP\RedirectResponse;
9+
use CodeIgniter\HTTP\URI;
10+
use Michalsn\CodeIgniterHtmx\HTTP\IncomingRequest;
11+
12+
/**
13+
* @property CLIRequest|IncomingRequest|null $request
14+
*/
15+
class CodeIgniter extends BaseCodeIgniter
16+
{
17+
/**
18+
* Web access?
19+
*/
20+
private function isWeb(): bool
21+
{
22+
return $this->context === 'web';
23+
}
24+
25+
/**
26+
* If we have a session object to use, store the current URI
27+
* as the previous URI. This is called just prior to sending the
28+
* response to the client, and will make it available next request.
29+
*
30+
* This helps provider safer, more reliable previous_url() detection.
31+
*
32+
* @param string|URI $uri
33+
*
34+
* @return void
35+
*/
36+
public function storePreviousURL($uri)
37+
{
38+
// Ignore CLI requests
39+
if (! $this->isWeb()) {
40+
return;
41+
}
42+
43+
// Ignore AJAX requests
44+
if (method_exists($this->request, 'isAJAX') && $this->request->isAJAX()) {
45+
return;
46+
}
47+
48+
// Ignore HTMX requests
49+
if (config('Htmx')->storePreviousURL === false
50+
&& method_exists($this->request, 'isHTMX') && $this->request->isHTMX()) {
51+
return;
52+
}
53+
54+
// Ignore unroutable responses
55+
if ($this->response instanceof DownloadResponse || $this->response instanceof RedirectResponse) {
56+
return;
57+
}
58+
59+
// Ignore non-HTML responses
60+
if (! str_contains($this->response->getHeaderLine('Content-Type'), 'text/html')) {
61+
return;
62+
}
63+
64+
// This is mainly needed during testing...
65+
if (is_string($uri)) {
66+
$uri = service('uri', $uri, false);
67+
}
68+
69+
if (isset($_SESSION)) {
70+
session()->set('_ci_previous_url', URI::createURIString(
71+
$uri->getScheme(),
72+
$uri->getAuthority(),
73+
$uri->getPath(),
74+
$uri->getQuery(),
75+
$uri->getFragment()
76+
));
77+
}
78+
}
79+
}

src/Config/Htmx.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,9 @@ class Htmx extends BaseConfig
2222
* when they are enabled.
2323
*/
2424
public string $skipViewDecoratorsString = 'htmxSkipViewDecorators';
25+
26+
/**
27+
* Enable / disable storing previous URLs for HTMX requests.
28+
*/
29+
public bool $storePreviousURL = true;
2530
}

src/Config/Services.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Config\Services as AppServices;
1212
use Config\Toolbar as ToolbarConfig;
1313
use Config\View as ViewConfig;
14+
use Michalsn\CodeIgniterHtmx\CodeIgniter;
1415
use Michalsn\CodeIgniterHtmx\Debug\Toolbar;
1516
use Michalsn\CodeIgniterHtmx\HTTP\IncomingRequest;
1617
use Michalsn\CodeIgniterHtmx\HTTP\RedirectResponse;
@@ -19,6 +20,22 @@
1920

2021
class Services extends BaseService
2122
{
23+
/**
24+
* CodeIgniter, the core of the framework.
25+
*
26+
* @return CodeIgniter
27+
*/
28+
public static function codeigniter(?App $config = null, bool $getShared = true)
29+
{
30+
if ($getShared) {
31+
return static::getSharedInstance('codeigniter', $config);
32+
}
33+
34+
$config ??= config(App::class);
35+
36+
return new CodeIgniter($config);
37+
}
38+
2239
/**
2340
* The Renderer class is the class that actually displays a file to the user.
2441
* The default View class within CodeIgniter is intentionally simple, but this

tests/CodeIgniterTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests;
6+
7+
use CodeIgniter\Test\CIUnitTestCase;
8+
9+
/**
10+
* @internal
11+
*/
12+
final class CodeIgniterTest extends CIUnitTestCase
13+
{
14+
protected function setUp(): void
15+
{
16+
parent::setUp();
17+
18+
$this->resetServices();
19+
}
20+
21+
protected function tearDown(): void
22+
{
23+
parent::tearDown();
24+
25+
$this->resetServices();
26+
}
27+
28+
public function testStorePreviousURLIsHTMX(): void
29+
{
30+
$_SESSION['_ci_previous_url'] = 'https://example.com/index.php/?previous=original';
31+
32+
config('Htmx')->storePreviousURL = true;
33+
34+
service('uri')->setPath('/')->setQuery('previous=saved_from_htmx');
35+
service('request')->appendHeader('HX-Request', 'true');
36+
37+
ob_start();
38+
service('codeigniter', null, false)->setContext('web')->run();
39+
ob_get_clean();
40+
41+
$this->assertTrue(service('request')->isHTMX());
42+
$this->assertArrayHasKey('_ci_previous_url', $_SESSION);
43+
$this->assertSame('https://example.com/index.php/?previous=saved_from_htmx', $_SESSION['_ci_previous_url']);
44+
}
45+
46+
public function testDontStorePreviousURLIsHTMX(): void
47+
{
48+
$_SESSION['_ci_previous_url'] = 'https://example.com/index.php/?previous=original';
49+
50+
service('uri')->setPath('/')->setQuery('previous=original');
51+
52+
config('Htmx')->storePreviousURL = false;
53+
54+
service('uri')->setPath('/')->setQuery('previous=not_saved_from_htmx');
55+
service('request')->appendHeader('HX-Request', 'true');
56+
57+
ob_start();
58+
service('codeigniter', null, false)->setContext('web')->run();
59+
ob_get_clean();
60+
61+
$this->assertTrue(service('request')->isHTMX());
62+
$this->assertArrayHasKey('_ci_previous_url', $_SESSION);
63+
$this->assertSame('https://example.com/index.php/?previous=original', $_SESSION['_ci_previous_url']);
64+
}
65+
}

0 commit comments

Comments
 (0)