diff --git a/decisionlog.md b/decisionlog.md new file mode 100644 index 0000000..ca9d8c6 --- /dev/null +++ b/decisionlog.md @@ -0,0 +1,16 @@ +# ADR + +An Architectural Decision (AD) is a software design choice that addresses a functional or non-functional requirement that is architecturally significant. An Architecturally Significant Requirement (ASR) is a requirement that has a measurable effect on a software system’s architecture and quality. An Architectural Decision Record (ADR) captures a single AD, such as often done when writing personal notes or meeting minutes; the collection of ADRs created and maintained in a project constitute its decision log. All these are within the topic of Architectural Knowledge Management (AKM). + +## Overall goals + +A) Strive to simplify. +B) Everyone contributes. + + +# Decision Record + +Decision IDs are auto incremented. IDs not in the document no longer apply. + +- 003 Cuttlefish only generates static websites. +- 004 A content project consists of content assets and a php configuration file. All other code is in this project. diff --git a/src/blog/ControllerAdmin.php b/src/blog/ControllerAdmin.php new file mode 100644 index 0000000..ad84024 --- /dev/null +++ b/src/blog/ControllerAdmin.php @@ -0,0 +1,109 @@ + 'Overview', + 'clearCache' => 'Clear cache', + 'generateSite' => 'Generate static site', + 'logout' => 'Logout', + ); + + // admin section does not use content files + protected string $contents; + + protected function isAllowedMethod($action): bool + { + return array_key_exists($action, $this->allowed_methods); + } + + protected function showTasks(): string + { + $output = ''; + + return $output; + } + + protected function showLogin(): string + { + return App::getInstance()->Security->login(); + } + + protected function clearCache() + { + $App = App::getInstance(); + $App->Security->maybeLoginRedirect(); + + return $App->Cache->clear(); + } + + protected function generateSite(): void + { + $App = App::getInstance(); + $App->Security->maybeLoginRedirect(); + echo $App->Cache->generateSite(); + } + + + /** + * @return void + */ + public function init() + { + App::getInstance()->Cache->abort(); + + $action = ( isset($this->args[0]) ) ? $this->args[0] : 'index'; + if ($this->isAllowedMethod($action)) { + $this->contents = $this->$action(); + } else { + exit("Method $action is not allowed"); + } + + parent::init(); + } + + /** + * @return void + */ + public function view() + { + parent::view(); + + $this->View = new Html($this->contents, array( + 'layout' => 'layout.php', + 'controller' => 'admin', + 'model' => 'page', + )); + } + + public function index(): string + { + if (App::getInstance()->Security->isLoggedIn()) { + return $this->showTasks(); + } + + return $this->showLogin(); + } + + public function logout() + { + $App = App::getInstance(); + $App->Security->maybeLoginRedirect(); + + return $App->Security->logout(); + } +} diff --git a/src/blog/ControllerArchive.php b/src/blog/ControllerArchive.php new file mode 100644 index 0000000..7878f94 --- /dev/null +++ b/src/blog/ControllerArchive.php @@ -0,0 +1,42 @@ + $content_dir ), $this->ext); + $this->records = $Files->files(); + } + + /** + * @return void + */ + public function model() + { + $this->Model = new ModelPost($this->records); + } + + /** + * @return void + */ + public function view() + { + parent::view(); + $this->View = new Html($this->Model->contents, array( + 'layout' => 'layout.php', + 'controller' => 'archive', + 'model' => 'post', + )); + } +} diff --git a/src/blog/ControllerError.php b/src/blog/ControllerError.php new file mode 100644 index 0000000..e1b006a --- /dev/null +++ b/src/blog/ControllerError.php @@ -0,0 +1,45 @@ +args) . '.' . $this->ext; + $this->records = [ Filesystem::convertUrlToPath($url) ]; + } + + /** + * @return void + */ + public function model() + { + $this->Model = new ModelPage($this->records); + } + + /** + * @return void + */ + public function view() + { + parent::view(); + + $this->View = new Html($this->Model->contents, array( + 'layout' => 'layout.php', + 'controller' => 'errors', + 'model' => 'page', + )); + } +} diff --git a/src/blog/ControllerFeed.php b/src/blog/ControllerFeed.php new file mode 100644 index 0000000..5b1c4c4 --- /dev/null +++ b/src/blog/ControllerFeed.php @@ -0,0 +1,41 @@ + $content_dir ), $this->ext); + $this->records = $Records->limit($limit + 5); + } + + /** + * @return void + */ + public function model() + { + $Model = new ModelPost($this->records); + $this->Model = $Model->limit(10); + } + + /** + * @return void + */ + public function view() + { + parent::view(); + $this->View = new Feed($this->Model->contents); + } +} diff --git a/src/blog/ControllerHome.php b/src/blog/ControllerHome.php new file mode 100644 index 0000000..d339008 --- /dev/null +++ b/src/blog/ControllerHome.php @@ -0,0 +1,45 @@ + $content_dir ), $this->ext); + $this->records = $Files->limit($limit + 5); + } + + /** + * @return void + */ + public function model() + { + $Model = new ModelPost($this->records); + $this->Model = $Model->limit(Configuration::POSTS_HOMEPAGE); + } + + /** + * @return void + */ + public function view() + { + parent::view(); + $this->View = new Html($this->Model->contents, array( + 'layout' => 'layout.php', + 'controller' => 'home', + 'model' => 'post', + )); + } +} diff --git a/src/blog/ControllerImage.php b/src/blog/ControllerImage.php new file mode 100644 index 0000000..6566434 --- /dev/null +++ b/src/blog/ControllerImage.php @@ -0,0 +1,39 @@ +records = [ Filesystem::convertUrlToPath($content_dir . implode('/', $this->args)) ]; + } + + /** + * @return void + */ + public function model() + { + $this->Model = new ModelFile($this->records); + } + + /** + * @return void + */ + public function view() + { + parent::view(); + $this->View = new File($this->Model->contents[0]); + $this->View->render(); + } +} diff --git a/src/blog/ControllerPage.php b/src/blog/ControllerPage.php new file mode 100644 index 0000000..090b049 --- /dev/null +++ b/src/blog/ControllerPage.php @@ -0,0 +1,44 @@ +args) . '.' . $this->ext; + $this->records = [ Filesystem::convertUrlToPath($url) ]; + } + + /** + * @return void + */ + public function model() + { + $this->Model = new ModelPage($this->records); + } + + /** + * @return void + */ + public function view() + { + parent::view(); + + $this->View = new Html($this->Model->contents, array( + 'layout' => 'layout.php', + 'controller' => 'pages', + 'model' => 'page', + )); + } +} diff --git a/src/blog/ControllerPost.php b/src/blog/ControllerPost.php new file mode 100644 index 0000000..775a193 --- /dev/null +++ b/src/blog/ControllerPost.php @@ -0,0 +1,44 @@ +args) . '.' . $this->ext; + $this->records = [ Filesystem::convertUrlToPath($url) ]; + } + + /** + * @return void + */ + public function model() + { + $this->Model = new ModelPost($this->records); + } + + /** + * @return void + */ + public function view() + { + parent::view(); + + $this->View = new Html($this->Model->contents, array( + 'layout' => 'layout.php', + 'controller' => 'posts', + 'model' => 'post', + )); + } +} diff --git a/src/blog/ModelFile.php b/src/blog/ModelFile.php new file mode 100644 index 0000000..11ac036 --- /dev/null +++ b/src/blog/ModelFile.php @@ -0,0 +1,21 @@ +contents = $records; + } +} diff --git a/src/blog/ModelPage.php b/src/blog/ModelPage.php new file mode 100644 index 0000000..20f8dcc --- /dev/null +++ b/src/blog/ModelPage.php @@ -0,0 +1,24 @@ + 'content', + ); + + /** + * @return void + */ + public function contents($records) + { + foreach ($records as $record) { + $this->contents[] = $this->listContents($record); + } + } +} diff --git a/src/blog/ModelPost.php b/src/blog/ModelPost.php new file mode 100644 index 0000000..f4d560c --- /dev/null +++ b/src/blog/ModelPost.php @@ -0,0 +1,35 @@ + 'metadata', + 'markdown|html' => 'content', + ); + + /** + * @return int + */ + public function sortByPublished($a, $b) + { + return strcmp($b->metadata->Published, $a->metadata->Published); + } + + /** + * @return self + */ + public function contents($records) + { + foreach ($records as $record) { + $this->contents[] = $this->listContents($record); + } + usort($this->contents, array( $this, 'sortByPublished' )); + + return $this; + } +} diff --git a/src/blog/functions.php b/src/blog/functions.php new file mode 100644 index 0000000..68992b0 --- /dev/null +++ b/src/blog/functions.php @@ -0,0 +1,12 @@ +Cache = new Cache(); @@ -37,4 +39,20 @@ public function run() new Router(); $this->Cache->end(); } + + + /** + * Singleton. The object is created from within the class itself + * only if the class has no instance. + * + * @return void + */ + public static function getInstance() + { + if (self::$instance == null) { + self::$instance = new App(); + } + + return self::$instance; + } } diff --git a/src/system/Cache.php b/src/system/Cache.php index e4216d3..4d6de40 100644 --- a/src/system/Cache.php +++ b/src/system/Cache.php @@ -215,7 +215,6 @@ public function generateSite(): string */ public function clear() { - global $App; $dir = $this->getCacheFolder(); $output = sprintf('Removing all files in %s
', $dir); $Files = new Files($dir); @@ -224,7 +223,7 @@ public function clear() foreach ($dirs as $dir) { Filesystem::removeDirs(realpath($dir . '/.')); } - $App->Environment->writeHtaccess(); + App::getInstance()->Environment->writeAccess(); return (string) $output; } diff --git a/src/system/Defaults.php b/src/system/Defaults.php index 4513917..24746cb 100644 --- a/src/system/Defaults.php +++ b/src/system/Defaults.php @@ -20,4 +20,6 @@ class Defaults public const CONTENT_FOLDER = '../content'; public const LOGS_FOLDER = '../_logs'; public const THEMES_FOLDER = 'themes'; + + const routes = []; } diff --git a/src/system/Environment.php b/src/system/Environment.php index f3e1996..eeaaea6 100644 --- a/src/system/Environment.php +++ b/src/system/Environment.php @@ -16,7 +16,7 @@ public function __construct() } // Externals environment - $this->registerExternals(); + $this->registerPlugins(); session_start(); } @@ -57,9 +57,9 @@ public function writeHtaccess(): void fclose($fp); } - protected function registerExternals(): void + protected function registerPlugins(): void { - $Files = new Files('/system/Ext', 'php'); + $Files = new Files('/plugins', 'php'); foreach ($Files->files() as $key => $filepath) { $this->register[ pathinfo($filepath, PATHINFO_FILENAME) ] = true; $this->addIncludePath(pathinfo($filepath, PATHINFO_DIRNAME)); diff --git a/src/system/Router.php b/src/system/Router.php index 29bb565..070d3ea 100644 --- a/src/system/Router.php +++ b/src/system/Router.php @@ -68,6 +68,9 @@ protected function classNotCallable($controller_class): void { $Url = new Url('/errors/404'); $log_message = "Not callable '$controller_class' or missing parameter."; + if (empty($controller_class)) { + $log_message = "Missing route"; + } $this->redirect($Url, $log_message); } @@ -81,16 +84,7 @@ protected function classNotCallable($controller_class): void */ protected function redirect($Url, string $log_message): void { - echo( "Location: " . $Url->url_absolute ); + echo( "Location: " . $Url->url_absolute . PHP_EOL ); exit($log_message); } - - protected function matchRoute($pattern, $routes) - { - $keys = array_flip($routes); - $matches = preg_grep($pattern, $keys); - if ($matches) { - return array_shift($matches); - } - } } diff --git a/src/themes/basic/functions.php b/src/themes/basic/functions.php index c0f5d92..da68e05 100644 --- a/src/themes/basic/functions.php +++ b/src/themes/basic/functions.php @@ -48,7 +48,5 @@ function pages() */ function isLoggedIn() { - global $App; - - return $App->Security->isLoggedIn(); + return Cuttlefish\App::getInstance()->Security->isLoggedIn(); }