diff --git a/classes/CalendarController.php b/classes/CalendarController.php index 20c6bcb..f445b16 100644 --- a/classes/CalendarController.php +++ b/classes/CalendarController.php @@ -41,8 +41,8 @@ abstract class CalendarController /** @var string */ protected $mode; - /** @var string */ - private $scriptName; + /** @var Url */ + protected $url; /** @var string */ protected $pluginFolder; @@ -70,7 +70,7 @@ abstract class CalendarController * @param array $lang */ public function __construct( - string $scriptName, + Url $url, string $pluginFolder, ?CsrfProtector $csrfProtector, array $config, @@ -81,7 +81,7 @@ public function __construct( bool $isAdmin, string $type ) { - $this->scriptName = $scriptName; + $this->url = $url; $this->pluginFolder = $pluginFolder; $this->csrfProtector = $csrfProtector; $this->config = $config; @@ -184,7 +184,7 @@ protected function renderModeLinkView(): HtmlString { return new HtmlString($this->view->render('mode-link', [ 'mode' => $mode = $this->mode === 'calendar' ? 'list' : 'calendar', - 'url' => $this->modifyUrl(array('ocal_action' => $mode)), + 'url' => $this->url->replace(['ocal_action' => $mode]), ])); } @@ -201,13 +201,4 @@ protected function renderToolbarView(): HtmlString 'states' => range(0, $this->config['state_max']), ])); } - - /** @param array $newParams */ - protected function modifyUrl(array $newParams): string - { - parse_str($_SERVER['QUERY_STRING'], $params); - $params = array_merge($params, $newParams); - $query = str_replace('=&', '&', http_build_query($params)); - return "{$this->scriptName}?$query"; - } } diff --git a/classes/DailyCalendarController.php b/classes/DailyCalendarController.php index a52bbe8..88ad7fa 100644 --- a/classes/DailyCalendarController.php +++ b/classes/DailyCalendarController.php @@ -38,7 +38,7 @@ class DailyCalendarController extends CalendarController * @param array $lang */ public function __construct( - string $scriptName, + Url $url, string $pluginFolder, ?CsrfProtector $csrfProtector, array $config, @@ -49,7 +49,7 @@ public function __construct( bool $isAdmin ) { parent::__construct( - $scriptName, + $url, $pluginFolder, $csrfProtector, $config, @@ -208,11 +208,11 @@ private function getPaginationItems(): array ); $paginationItems = $pagination->getItems(); foreach ($paginationItems as $item) { - $item->url = $this->modifyUrl(array( + $item->url = $this->url->replace([ 'ocal_year' => $item->year, 'ocal_month' => $item->monthOrWeek, 'ocal_action' => $this->mode - )); + ]); } return $paginationItems; } diff --git a/classes/Dic.php b/classes/Dic.php index e69d738..929ded2 100644 --- a/classes/Dic.php +++ b/classes/Dic.php @@ -39,10 +39,10 @@ public static function makeDefaultAdminController(): DefaultAdminController public static function makeDailyCalendarController(): DailyCalendarController { - global $sn, $pth, $plugin_cf, $plugin_tx, $_XH_csrfProtection; + global $sn, $su, $pth, $plugin_cf, $plugin_tx, $_XH_csrfProtection; return new DailyCalendarController( - $sn, + new Url($sn, $su, ($su !== "" ? array_slice($_GET, 1) : $_GET)), "{$pth['folder']['plugins']}ocal/", $_XH_csrfProtection, $plugin_cf['ocal'], @@ -56,10 +56,10 @@ public static function makeDailyCalendarController(): DailyCalendarController public static function makeHourlyCalendarController(): HourlyCalendarController { - global $sn, $pth, $plugin_cf, $plugin_tx, $_XH_csrfProtection; + global $sn, $su, $pth, $plugin_cf, $plugin_tx, $_XH_csrfProtection; return new HourlyCalendarController( - $sn, + new Url($sn, $su, ($su !== "" ? array_slice($_GET, 1) : $_GET)), "{$pth['folder']['plugins']}ocal/", $_XH_csrfProtection, $plugin_cf['ocal'], diff --git a/classes/HourlyCalendarController.php b/classes/HourlyCalendarController.php index a17bfa7..6ba713a 100644 --- a/classes/HourlyCalendarController.php +++ b/classes/HourlyCalendarController.php @@ -38,7 +38,7 @@ class HourlyCalendarController extends CalendarController * @param array $lang */ public function __construct( - string $scriptName, + Url $url, string $pluginFolder, ?CsrfProtector $csrfProtector, array $config, @@ -49,7 +49,7 @@ public function __construct( bool $isAdmin ) { parent::__construct( - $scriptName, + $url, $pluginFolder, $csrfProtector, $config, @@ -204,11 +204,11 @@ private function getPaginationItems(int $weekCount): array ); $items = $pagination->getItems($weekCount); foreach ($items as $item) { - $item->url = $this->modifyUrl(array( + $item->url = $this->url->replace([ 'ocal_year' => $item->year, 'ocal_week' => $item->monthOrWeek, 'ocal_action' => $this->mode - )); + ]); } return $items; } diff --git a/classes/Url.php b/classes/Url.php new file mode 100644 index 0000000..3cb545f --- /dev/null +++ b/classes/Url.php @@ -0,0 +1,56 @@ +. + */ + +namespace Ocal; + +class Url +{ + /** @var string */ + private $base; + + /** @var string */ + private $page; + + /** @var array */ + private $params; + + /** @param array $params */ + public function __construct(string $base, string $page, array $params) + { + $this->base = $base; + $this->page = $page; + $this->params = $params; + } + + /** @param array $params */ + public function replace(array $params): self + { + return new Url($this->base, $this->page, array_replace($this->params, $params)); + } + + public function __toString(): string + { + $rest = http_build_query($this->params, "", "&", PHP_QUERY_RFC3986); + $rest = preg_replace('/=(?=&|$)/', '', $rest); + $query = $this->page . ($rest !== "" ? "&{$rest}": ""); + return $this->base . ($query !== "" ? "?{$query}" : ""); + } +} diff --git a/tests/DailyCalendarControllerTest.php b/tests/DailyCalendarControllerTest.php index 95e6b09..caeb659 100644 --- a/tests/DailyCalendarControllerTest.php +++ b/tests/DailyCalendarControllerTest.php @@ -55,7 +55,7 @@ public function setUp(): void $this->listService = $this->createStub(ListService::class); $this->db = $this->createStub(Db::class); $this->sut = new DailyCalendarController( - "/", + new Url("/", "", []), "./", $this->csrfProtector, $config, diff --git a/tests/DailyCalendarControllerVisitorTest.php b/tests/DailyCalendarControllerVisitorTest.php index 396af55..07af957 100644 --- a/tests/DailyCalendarControllerVisitorTest.php +++ b/tests/DailyCalendarControllerVisitorTest.php @@ -39,7 +39,7 @@ public function testContructorDoesNotCrash(): void $listService = $this->createStub(ListService::class); $db = $this->createStub(Db::class); new DailyCalendarController( - "/", + new Url("/", "", []), "./", null, $config, diff --git a/tests/HourlyCalendarControllerTest.php b/tests/HourlyCalendarControllerTest.php index 37a584b..63751ff 100644 --- a/tests/HourlyCalendarControllerTest.php +++ b/tests/HourlyCalendarControllerTest.php @@ -55,7 +55,7 @@ public function setUp(): void $this->listService = $this->createStub(ListService::class); $this->db = $this->createStub(Db::class); $this->sut = new HourlyCalendarController( - "/", + new Url("/", "", []), "./", $this->csrfProtector, $config, diff --git a/tests/UrlTest.php b/tests/UrlTest.php new file mode 100644 index 0000000..0771ad7 --- /dev/null +++ b/tests/UrlTest.php @@ -0,0 +1,86 @@ +. + */ + +namespace Ocal; + +use PHPUnit\Framework\TestCase; + +class UrlTest extends TestCase +{ + /** @dataProvider dataForUrlHasDesiredStringRepresentation */ + public function testUrlHasDesiredStringRepresentation(Url $url, string $expected): void + { + $actual = (string) $url; + $this->assertEquals($expected, $actual); + } + + public function dataForUrlHasDesiredStringRepresentation(): array + { + return [ + 'no_page' => [new Url("/", "", []), "/"], + 'with_page' => [new Url("/", "Page", []), "/?Page"], + 'with_params' => [new Url("/", "Page", ["foo" => "bar", "qux" => "bar"]), "/?Page&foo=bar&qux=bar"], + 'params_but_no_page' => [new Url("/", "", ["foo" => "bar"]), "/?&foo=bar"], + 'gh-35' => [new Url("/", "Ocal-1.0", []), "/?Ocal-1.0"], + ]; + } + + /** + * @param array $params + * @dataProvider dataForUrlCanBeModified + */ + public function testUrlCanBeModified(Url $url, array $params, string $expected): void + { + $actual = (string) $url->replace($params); + $this->assertEquals($expected, $actual); + } + + public function dataForUrlCanBeModified(): array + { + return [ + [ + new Url("/", "Page", []), + [], + "/?Page", + ], + [ + new Url("/", "", []), + ["foo" => "bar"], + "/?&foo=bar", + ], + [ + new Url("/", "Page", ["foo" => "bar"]), + [], + "/?Page&foo=bar", + ], + [ + new Url("/", "Page", ["foo" => "bar"]), + ["baz" => "qux"], + "/?Page&foo=bar&baz=qux", + ], + [ + new Url("/", "Page", ["foo" => "bar"]), + ["foo" => "baz"], + "/?Page&foo=baz", + ], + ]; + } +} diff --git a/tests/approvals/DailyCalendarControllerTest.testDefaultActionHandlesAjaxRequest.approved.html b/tests/approvals/DailyCalendarControllerTest.testDefaultActionHandlesAjaxRequest.approved.html index c4e1867..294ac28 100644 --- a/tests/approvals/DailyCalendarControllerTest.testDefaultActionHandlesAjaxRequest.approved.html +++ b/tests/approvals/DailyCalendarControllerTest.testDefaultActionHandlesAjaxRequest.approved.html @@ -2,7 +2,7 @@

- List view + List view

@@ -90,8 +90,8 @@

- Today - Next Month - Next Year + Today + Next Month + Next Year

diff --git a/tests/approvals/DailyCalendarControllerTest.testDefaultActionRendersCalendar.approved.html b/tests/approvals/DailyCalendarControllerTest.testDefaultActionRendersCalendar.approved.html index c4e1867..294ac28 100644 --- a/tests/approvals/DailyCalendarControllerTest.testDefaultActionRendersCalendar.approved.html +++ b/tests/approvals/DailyCalendarControllerTest.testDefaultActionRendersCalendar.approved.html @@ -2,7 +2,7 @@

- List view + List view

@@ -90,8 +90,8 @@

- Today - Next Month - Next Year + Today + Next Month + Next Year

diff --git a/tests/approvals/DailyCalendarControllerTest.testListActionHandlesAjaxRequest.approved.html b/tests/approvals/DailyCalendarControllerTest.testListActionHandlesAjaxRequest.approved.html index 3824728..c4254c6 100644 --- a/tests/approvals/DailyCalendarControllerTest.testListActionHandlesAjaxRequest.approved.html +++ b/tests/approvals/DailyCalendarControllerTest.testListActionHandlesAjaxRequest.approved.html @@ -2,7 +2,7 @@ diff --git a/tests/approvals/DailyCalendarControllerTest.testListActionRendersListWithAnEntry.approved.html b/tests/approvals/DailyCalendarControllerTest.testListActionRendersListWithAnEntry.approved.html index 8f36a28..5a56d94 100644 --- a/tests/approvals/DailyCalendarControllerTest.testListActionRendersListWithAnEntry.approved.html +++ b/tests/approvals/DailyCalendarControllerTest.testListActionRendersListWithAnEntry.approved.html @@ -2,7 +2,7 @@ diff --git a/tests/approvals/DailyCalendarControllerTest.testListActionRendersListWithoutEntries.approved.html b/tests/approvals/DailyCalendarControllerTest.testListActionRendersListWithoutEntries.approved.html index 3824728..c4254c6 100644 --- a/tests/approvals/DailyCalendarControllerTest.testListActionRendersListWithoutEntries.approved.html +++ b/tests/approvals/DailyCalendarControllerTest.testListActionRendersListWithoutEntries.approved.html @@ -2,7 +2,7 @@ diff --git a/tests/approvals/HourlyCalendarControllerTest.testDefaultActionHandlesAjaxRequest.approved.html b/tests/approvals/HourlyCalendarControllerTest.testDefaultActionHandlesAjaxRequest.approved.html index be7fe52..2e57053 100644 --- a/tests/approvals/HourlyCalendarControllerTest.testDefaultActionHandlesAjaxRequest.approved.html +++ b/tests/approvals/HourlyCalendarControllerTest.testDefaultActionHandlesAjaxRequest.approved.html @@ -2,7 +2,7 @@

- List view + List view

@@ -153,7 +153,7 @@

- Today - Next interval + Today + Next interval

diff --git a/tests/approvals/HourlyCalendarControllerTest.testDefaultActionRendersCalendar.approved.html b/tests/approvals/HourlyCalendarControllerTest.testDefaultActionRendersCalendar.approved.html index be7fe52..2e57053 100644 --- a/tests/approvals/HourlyCalendarControllerTest.testDefaultActionRendersCalendar.approved.html +++ b/tests/approvals/HourlyCalendarControllerTest.testDefaultActionRendersCalendar.approved.html @@ -2,7 +2,7 @@

- List view + List view

@@ -153,7 +153,7 @@

- Today - Next interval + Today + Next interval

diff --git a/tests/approvals/HourlyCalendarControllerTest.testListActionHandlesAjaxRequest.approved.html b/tests/approvals/HourlyCalendarControllerTest.testListActionHandlesAjaxRequest.approved.html index 42352a8..c37fb71 100644 --- a/tests/approvals/HourlyCalendarControllerTest.testListActionHandlesAjaxRequest.approved.html +++ b/tests/approvals/HourlyCalendarControllerTest.testListActionHandlesAjaxRequest.approved.html @@ -2,7 +2,7 @@ diff --git a/tests/approvals/HourlyCalendarControllerTest.testListActionRendersListWithAnEntry.approved.html b/tests/approvals/HourlyCalendarControllerTest.testListActionRendersListWithAnEntry.approved.html index b53a61d..4bcab1e 100644 --- a/tests/approvals/HourlyCalendarControllerTest.testListActionRendersListWithAnEntry.approved.html +++ b/tests/approvals/HourlyCalendarControllerTest.testListActionRendersListWithAnEntry.approved.html @@ -2,7 +2,7 @@ diff --git a/tests/approvals/HourlyCalendarControllerTest.testListActionRendersListWithoutEntries.approved.html b/tests/approvals/HourlyCalendarControllerTest.testListActionRendersListWithoutEntries.approved.html index 42352a8..c37fb71 100644 --- a/tests/approvals/HourlyCalendarControllerTest.testListActionRendersListWithoutEntries.approved.html +++ b/tests/approvals/HourlyCalendarControllerTest.testListActionRendersListWithoutEntries.approved.html @@ -2,7 +2,7 @@ diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1da2007..2e40f37 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -25,5 +25,6 @@ require_once "./classes/Month.php"; require_once "./classes/Response.php"; require_once "./classes/SystemChecker.php"; +require_once "./classes/Url.php"; require_once "./classes/Week.php"; require_once "./classes/View.php"; diff --git a/views/mode-link.php b/views/mode-link.php index 5e93b25..684f245 100644 --- a/views/mode-link.php +++ b/views/mode-link.php @@ -1,11 +1,12 @@