diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bbea7a9d5..a06b0c6d5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -41,22 +41,24 @@ The following is an overview of the source code directory structure:
settings for the database, SSO setup (if any),
e-mail, hyperlinks, branding, etc.
+ controller_config.php
+ Controller configuration. Here we map request targets
+ onto controllers.
+
engine_config.php
Model configuration. This maps the model interfaces
- onto the concrete implementations.
+ onto concrete implementations.
ui_config.php
- Controller and navigation configuration. Controller
- configuration maps request targets onto controllers;
- navigation configuration defines menu items, access
- controls, and implementations.
+ User interface configuration. This defines menu items,
+ access controls, and implementations.
controllers/
Controllers are responsible for processing requests
that are received by the application. Controllers are
instantiated and invoked by the Dispatcher, whose
operation is specified via metadata in
- config/ui_config.php.
+ config/controller_config.php.
css/
CSS assets. These files are automatically whitespace
diff --git a/config/controller_config.php b/config/controller_config.php
new file mode 100644
index 000000000..e7ae66ee4
--- /dev/null
+++ b/config/controller_config.php
@@ -0,0 +1,19 @@
+ ZK\UI\UIController::class,
+ 'addexp' => ZK\Controllers\ExportAdd::class,
+ 'export' => ZK\Controllers\ExportPlaylist::class,
+ 'afile' => ZK\Controllers\ExportAfile::class,
+ 'opensearch' => ZK\Controllers\OpenSearch::class,
+ 'daily' => ZK\Controllers\RunDaily::class,
+ 'print' => ZK\Controllers\PrintTags::class,
+ 'rss' => ZK\Controllers\RSS::class,
+ 'api' => ZK\Controllers\API::class,
+ 'sso' => ZK\Controllers\SSOLogin::class,
+ 'push' => ZK\Controllers\PushServer::class,
+];
diff --git a/config/ui_config.php b/config/ui_config.php
index 403780361..a9c99b932 100644
--- a/config/ui_config.php
+++ b/config/ui_config.php
@@ -1,10 +1,9 @@
ZK\UI\Main::class,
- 'addexp' => ZK\Controllers\ExportAdd::class,
- 'export' => ZK\Controllers\ExportPlaylist::class,
- 'afile' => ZK\Controllers\ExportAfile::class,
- 'opensearch' => ZK\Controllers\OpenSearch::class,
- 'daily' => ZK\Controllers\RunDaily::class,
- 'print' => ZK\Controllers\PrintTags::class,
- 'rss' => ZK\Controllers\RSS::class,
- 'api' => ZK\Controllers\API::class,
- 'sso' => ZK\Controllers\SSOLogin::class,
- 'push' => ZK\Controllers\PushServer::class,
-];
diff --git a/controllers/API.php b/controllers/API.php
index f7a5ea0b9..eb09c0aec 100644
--- a/controllers/API.php
+++ b/controllers/API.php
@@ -360,7 +360,7 @@ class API extends CommandTarget implements IController {
private $limit;
private $serializer;
- public function processRequest($dispatcher) {
+ public function processRequest() {
$wantXml = $_REQUEST["xml"] ||
substr($_SERVER["HTTP_ACCEPT"], 0, 8) == "text/xml";
$this->serializer = $wantXml?new XMLSerializer():new JSONSerializer();
diff --git a/controllers/Dispatcher.php b/controllers/Dispatcher.php
index 966f7da6e..c413ace96 100644
--- a/controllers/Dispatcher.php
+++ b/controllers/Dispatcher.php
@@ -38,79 +38,16 @@
use ZK\Engine\Engine;
class Dispatcher {
- private $menu;
private $controllers;
public function __construct() {
- // UI configuration file
- $this->menu = new Config('ui_config', 'menu');
- $customMenu = Engine::param('custom_menu');
- if($customMenu)
- $this->menu->merge($customMenu);
-
// Controllers
- $this->controllers = new Config('ui_config', 'controllers');
+ $this->controllers = new Config('controller_config', 'controllers');
$customControllers = Engine::param('custom_controllers');
if($customControllers)
$this->controllers->merge($customControllers);
}
- /**
- * return menu entry that matches the specified action
- */
- public function match($action) {
- return $this->menu->iterate(function($entry) use($action) {
- if($entry[1] == $action || substr($entry[1], -1) == '%' &&
- substr($entry[1], 0, -1) == substr($action, 0, strlen($entry[1])-1))
- return $entry;
- });
- }
-
- /**
- * dispatch action to the appropriate menu item/command target
- */
- public function dispatch($action, $subaction, $session) {
- $entry = $this->match($action);
-
- // If no action was selected or if action is unauthorized,
- // default to the first one
- if(!$entry || !$session->isAuth($entry[0]))
- $entry = $this->menu->default();
-
- $handler = new $entry[3]();
- if($handler instanceof CommandTarget)
- $handler->process($action, $subaction, $session);
- }
-
- /**
- * indicate whether the specified action is authorized for the session
- */
- public function isActionAuth($action, $session) {
- $entry = $this->match($action);
- return !$entry || $session->isAuth($entry[0]);
- }
-
- /**
- * compose the menu for the specified session
- */
- public function composeMenu($action, $session) {
- $result = [];
- $this->menu->iterate(function($entry) use(&$result, $action, $session) {
- if($entry[2] && $session->isAuth($entry[0])) {
- $baseAction = substr($entry[1], -1) == '%'?
- substr($entry[1], 0, -1):$entry[1];
- $selected = $entry[1] == $action ||
- substr($entry[1], -1) == '%' &&
- substr($entry[1], 0, -1) ==
- substr($action, 0, strlen($entry[1]) - 1);
- $result[] = [ 'action' => $baseAction,
- 'label' => $entry[2],
- 'selected' => $selected ];
- }
- });
- return $result;
- }
-
/**
* dispatch request to the specified controller
*/
@@ -120,6 +57,6 @@ public function processRequest($controller="") {
$this->controllers->default():$p;
$impl = new $implClass();
if($impl instanceof IController)
- $impl->processRequest($this);
+ $impl->processRequest();
}
}
diff --git a/controllers/ExportAdd.php b/controllers/ExportAdd.php
index d3d641046..707a68694 100644
--- a/controllers/ExportAdd.php
+++ b/controllers/ExportAdd.php
@@ -28,7 +28,7 @@
use ZK\Engine\IChart;
class ExportAdd implements IController {
- public function processRequest($dispatcher) {
+ public function processRequest() {
// Ensure there's a date
$date = $_REQUEST["date"];
if(strlen($date) != 10 ||
diff --git a/controllers/ExportAfile.php b/controllers/ExportAfile.php
index 1e75ab713..164dc5306 100644
--- a/controllers/ExportAfile.php
+++ b/controllers/ExportAfile.php
@@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason
- * @copyright Copyright (C) 1997-2019 Jim Mason
+ * @copyright Copyright (C) 1997-2021 Jim Mason
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
@@ -29,7 +29,7 @@
use ZK\UI\AddManager;
class ExportAfile implements IController {
- public function processRequest($dispatcher) {
+ public function processRequest() {
$userAgent = $_SERVER["HTTP_USER_AGENT"];
?>
diff --git a/controllers/ExportPlaylist.php b/controllers/ExportPlaylist.php
index 786dcff6b..2d7ae0301 100644
--- a/controllers/ExportPlaylist.php
+++ b/controllers/ExportPlaylist.php
@@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason
- * @copyright Copyright (C) 1997-2018 Jim Mason
+ * @copyright Copyright (C) 1997-2021 Jim Mason
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
@@ -48,7 +48,7 @@ class ExportPlaylist extends CommandTarget implements IController {
private $time;
private $records;
- public function processRequest($dispatcher) {
+ public function processRequest() {
// Ensure user has selected a playlist
$playlist = intval($_REQUEST["playlist"]);
if($playlist == 0) {
diff --git a/controllers/IController.php b/controllers/IController.php
index 707e805a9..4170e0d61 100644
--- a/controllers/IController.php
+++ b/controllers/IController.php
@@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason
- * @copyright Copyright (C) 1997-2018 Jim Mason
+ * @copyright Copyright (C) 1997-2021 Jim Mason
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
@@ -25,5 +25,5 @@
namespace ZK\Controllers;
interface IController {
- public function processRequest($dispatcher);
+ public function processRequest();
}
diff --git a/controllers/OpenSearch.php b/controllers/OpenSearch.php
index f9bac3e1c..2503eb72a 100644
--- a/controllers/OpenSearch.php
+++ b/controllers/OpenSearch.php
@@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason
- * @copyright Copyright (C) 1997-2018 Jim Mason
+ * @copyright Copyright (C) 1997-2021 Jim Mason
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
@@ -29,7 +29,7 @@
use ZK\UI\UICommon as UI;
class OpenSearch implements IController {
- public function processRequest($dispatcher) {
+ public function processRequest() {
$baseURL = UI::getBaseURL();
$favicon = Engine::param('favicon', 'favicon.ico');
$banner = Engine::param("station")." ".Engine::param("application");
diff --git a/controllers/PrintTags.php b/controllers/PrintTags.php
index 0755bdc96..89bde44c1 100644
--- a/controllers/PrintTags.php
+++ b/controllers/PrintTags.php
@@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason
- * @copyright Copyright (C) 1997-2018 Jim Mason
+ * @copyright Copyright (C) 1997-2021 Jim Mason
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
@@ -48,7 +48,7 @@ class PrintTags implements IController {
],
];
- public function processRequest($dispatcher) {
+ public function processRequest() {
header("Content-type: application/pdf");
$form = empty($_REQUEST["form"])?self::LABEL_FORM:$_REQUEST["form"];
diff --git a/controllers/PushServer.php b/controllers/PushServer.php
index 2812f8ee1..8f8776ec2 100644
--- a/controllers/PushServer.php
+++ b/controllers/PushServer.php
@@ -218,7 +218,7 @@ public static function sendAsyncNotification($show = null, $spin = null) {
socket_close($socket);
}
- public function processRequest($dispatcher) {
+ public function processRequest() {
if(php_sapi_name() != "cli") {
http_response_code(400);
return;
diff --git a/controllers/RSS.php b/controllers/RSS.php
index 781cc5d51..7b67dda14 100644
--- a/controllers/RSS.php
+++ b/controllers/RSS.php
@@ -57,7 +57,7 @@ private static function htmlnumericentities($str) {
// $str);
}
- public function processRequest($dispatcher) {
+ public function processRequest() {
$this->session = Engine::session();
header("Content-type: text/xml");
diff --git a/controllers/RunDaily.php b/controllers/RunDaily.php
index 057d7a9f3..1cefa6204 100644
--- a/controllers/RunDaily.php
+++ b/controllers/RunDaily.php
@@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason
- * @copyright Copyright (C) 1997-2018 Jim Mason
+ * @copyright Copyright (C) 1997-2021 Jim Mason
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
@@ -34,7 +34,7 @@
class RunDaily implements IController {
private $catCodes;
- public function processRequest($dispatcher) {
+ public function processRequest() {
if(php_sapi_name() != "cli") {
http_response_code(400);
return;
diff --git a/controllers/SSOLogin.php b/controllers/SSOLogin.php
index aae30d461..f1e9f8ead 100644
--- a/controllers/SSOLogin.php
+++ b/controllers/SSOLogin.php
@@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason
- * @copyright Copyright (C) 1997-2020 Jim Mason
+ * @copyright Copyright (C) 1997-2021 Jim Mason
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
@@ -33,7 +33,7 @@ class SSOLogin implements IController {
private $action;
private $ssoOptions;
- public function processRequest($dispatcher) {
+ public function processRequest() {
$params = SSOCommon::zkQSParams();
$state = $params["state"];
if($state) {
diff --git a/custom/KzsuUIController.php b/custom/KzsuUIController.php
index ffe110112..21277ec33 100644
--- a/custom/KzsuUIController.php
+++ b/custom/KzsuUIController.php
@@ -3,7 +3,7 @@
* Zookeeper Online
*
* @author Jim Mason
- * @copyright Copyright (C) 1997-2020 Jim Mason
+ * @copyright Copyright (C) 1997-2021 Jim Mason
* @link https://zookeeper.ibinx.com/
* @license GPL-3.0
*
@@ -36,8 +36,8 @@
* The file is provided as an example of a custom UI controller;
* it is not part of the basic functionality of Zookeeper Online.
*/
-class KzsuUIController extends Main {
- protected function emitBodyHeader($dispatcher) {
+class KzsuUIController extends UIController {
+ protected function emitBodyHeader() {
$urls = Engine::param('urls');
$station_full = Engine::param('station_full');
?>
diff --git a/ui/Main.php b/ui/UIController.php
similarity index 84%
rename from ui/Main.php
rename to ui/UIController.php
index 9e2c3694b..d779ad112 100644
--- a/ui/Main.php
+++ b/ui/UIController.php
@@ -26,6 +26,7 @@
use ZK\Controllers\IController;
use ZK\Controllers\SSOCommon;
+use ZK\Engine\Config;
use ZK\Engine\Engine;
use ZK\Engine\IUser;
use ZK\Engine\Session;
@@ -34,31 +35,114 @@
use JSMin\JSMin;
-class Main implements IController {
+class MenuEntry {
+ private const FIELDS = [ 'access', 'action', 'label', 'implementation' ];
+
+ private $entry;
+
+ public function __construct($entry) {
+ $this->entry = $entry;
+ }
+
+ public function __get($var) {
+ $index = array_search($var, self::FIELDS);
+ return $index !== false?$this->entry[$index]:null;
+ }
+}
+
+class UIController implements IController {
protected $ssoUser;
protected $dn;
protected $session;
+ protected $menu;
+
+ /**
+ * return menu item that matches the specified action
+ */
+ protected function match($action) {
+ return $this->menu->iterate(function($entry) use($action) {
+ $item = new MenuEntry($entry);
+ $itemAction = $item->action;
+ if($itemAction == $action || substr($itemAction, -1) == '%' &&
+ substr($itemAction, 0, -1) == substr($action, 0, strlen($itemAction)-1))
+ return $item;
+ });
+ }
+
+ /**
+ * dispatch action to the appropriate menu item/command target
+ */
+ public function dispatch($action, $subaction) {
+ $item = $this->match($action);
- public function processRequest($dispatcher) {
+ // If no action was selected or if action is unauthorized,
+ // default to the first one
+ if(!$item || !$this->session->isAuth($item->access))
+ $item = new MenuEntry($this->menu->default());
+ $implClass = $item->implementation;
+ $menuItem = new $implClass();
+ if($menuItem instanceof MenuItem)
+ $menuItem->process($action, $subaction, $this->session);
+ }
+
+ /**
+ * indicate whether the specified action is authorized for the session
+ */
+ public function isActionAuth($action) {
+ $item = $this->match($action);
+ return !$item || $this->session->isAuth($item->access);
+ }
+
+ /**
+ * compose the menu for the current session
+ */
+ public function composeMenu($action) {
+ $result = [];
+ $this->menu->iterate(function($entry) use(&$result, $action) {
+ $item = new MenuEntry($entry);
+ $itemAction = $item->action;
+ if($item->label && $this->session->isAuth($item->access)) {
+ $baseAction = substr($itemAction, -1) == '%'?
+ substr($itemAction, 0, -1):$itemAction;
+ $selected = $itemAction == $action ||
+ substr($itemAction, -1) == '%' &&
+ substr($itemAction, 0, -1) ==
+ substr($action, 0, strlen($itemAction) - 1);
+ $result[] = [ 'action' => $baseAction,
+ 'label' => $item->label,
+ 'selected' => $selected ];
+ }
+ });
+ return $result;
+ }
+
+ public function processRequest() {
$this->session = Engine::session();
- $this->preProcessRequest($dispatcher);
+
+ // UI configuration file
+ $this->menu = new Config('ui_config', 'menu');
+ $customMenu = Engine::param('custom_menu');
+ if($customMenu)
+ $this->menu->merge($customMenu);
+
+ $this->preProcessRequest();
$isJson = substr($_SERVER["HTTP_ACCEPT"], 0, 16) === 'application/json';
if ($isJson) {
$action = $_REQUEST["action"];
$subaction = $_REQUEST["subaction"];
- $dispatcher->dispatch($action, $subaction, $this->session);
+ $this->dispatch($action, $subaction);
} else {
$this->emitResponseHeader();
- $this->emitBody($dispatcher);
+ $this->emitBody();
}
}
- protected function preProcessRequest($dispatcher) {
+ protected function preProcessRequest() {
// Validate the requested action is authorized
- if(!empty($_REQUEST["session"]) && !empty($_REQUEST["action"]) &&
- !$dispatcher->isActionAuth($_REQUEST["action"], $this->session) &&
+ if(!empty($_REQUEST["action"]) &&
+ !$this->isActionAuth($_REQUEST["action"]) &&
$_REQUEST["action"] != "loginValidate" &&
$_REQUEST["action"] != "logout") {
$_REQUEST["action"] = "invalidAction";
@@ -137,10 +221,10 @@ protected function emitResponseHeader() {
".Engine::param('application')."
\n";
echo " \n";
- $menu = $dispatcher->composeMenu($action, $this->session);
+ $menu = $this->composeMenu($action);
foreach($menu as $item) {
echo " | " .
"\n";
}
- protected function emitMain($dispatcher, $action, $subaction) {
+ protected function emitMain($action, $subaction) {
switch($action) {
case "login":
$this->emitLogin();
@@ -197,12 +281,12 @@ protected function emitMain($dispatcher, $action, $subaction) {
// fall through...
default:
// dispatch action
- $dispatcher->dispatch($action, $subaction, $this->session);
+ $this->dispatch($action, $subaction);
break;
}
}
- protected function emitBodyHeader($dispatcher) {
+ protected function emitBodyHeader() {
$urls = Engine::param('urls');
$station_full = Engine::param('station_full');
?>
@@ -217,19 +301,19 @@ protected function emitBodyHeader($dispatcher) {
\n";
echo " \n";
- $this->emitNavBar($dispatcher, $_REQUEST["action"]);
+ $this->emitNavBar($_REQUEST["action"]);
echo " \n";
echo " \n";
- $this->emitMain($dispatcher, $_REQUEST["action"], $_REQUEST["subaction"]);
+ $this->emitMain($_REQUEST["action"], $_REQUEST["subaction"]);
?>
|