From 6a122e3beda50c7f8ff74969675fd1aa1362cd53 Mon Sep 17 00:00:00 2001 From: Fabian de Boer Date: Fri, 17 Jul 2026 12:34:37 +0200 Subject: [PATCH 01/10] Brave request Fix. Path seperator fix. --- .gitignore | 1 + conf/init.php | 2 +- lib/ImgSearch.class.php | 2 +- resource/Brave.class.php | 19 +++++++++++++------ 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 8c12c70..54e8b97 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ sessions/* ignore kanban/ .pi-tasks +docs/framework_review.md diff --git a/conf/init.php b/conf/init.php index ed0dbc1..c65b349 100755 --- a/conf/init.php +++ b/conf/init.php @@ -52,7 +52,7 @@ spl_autoload_register(function ($class_name) { } $include_path = get_include_path(); - $include_path_tokens = explode(':', $include_path); + $include_path_tokens = explode(PATH_SEPARATOR, $include_path); foreach ($include_path_tokens as $prefix) { $path[0] = $prefix . DIRECTORY_SEPARATOR . $class_name . '.interface.php'; diff --git a/lib/ImgSearch.class.php b/lib/ImgSearch.class.php index c1aaf46..9e9d44b 100755 --- a/lib/ImgSearch.class.php +++ b/lib/ImgSearch.class.php @@ -62,7 +62,7 @@ class ImgSearch { public function getImageData() { $clazz = ucfirst($this->engine); $search = new $clazz(); - $search->setQuery(urlencode($this->word)); + $search->setQuery($this->word); $search->setParam('start', $this->hitPosition); $search->setParam('size', $this->numberResults); diff --git a/resource/Brave.class.php b/resource/Brave.class.php index a55f164..74e727e 100644 --- a/resource/Brave.class.php +++ b/resource/Brave.class.php @@ -21,8 +21,20 @@ class Brave implements Search { public function setQuery($word) { $this->word = $word; + } - $request = $this->q_prefix . urlencode($word); + public function getData() { + $this->buildRequest(); + $return_array = $this->prepareData(); + return $return_array; + } + + /** + * Build the request URL and cURL handle after all setParam() calls have been made. + * This ensures start/offset and size are available when constructing the URL. + */ + private function buildRequest() { + $request = $this->q_prefix . urlencode($this->word); if (isset($this->start)) { $request .= '&offset=' . $this->start; } @@ -38,11 +50,6 @@ class Brave implements Search { $this->$key = $value; } - public function getData() { - $return_array = $this->prepareData(); - return $return_array; - } - /** * Prepare a project wide universal array with results */ From d185a26f874daf056bb7fb3b3ada023f6aec5d77 Mon Sep 17 00:00:00 2001 From: Fabian de Boer Date: Fri, 17 Jul 2026 21:58:05 +0200 Subject: [PATCH 02/10] default no debug. clever solution for api keys in git ignored local conf file. --- .gitignore | 1 + README.md | 15 ++++++++++++--- conf/conf.local.php.dist | 10 ++++++++++ conf/conf.php | 5 +++-- conf/init.php | 11 +++++++++++ 5 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 conf/conf.local.php.dist diff --git a/.gitignore b/.gitignore index 54e8b97..4dfc547 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ sessions/* +conf/conf.local.php ignore kanban/ .pi-tasks diff --git a/README.md b/README.md index 36f1b16..e45046a 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,22 @@ ApFab Zap Machine needs some PHP extensions: - CUrl (default installed) - Imagick (Image Magick image processing Class) -You need to acquire the following API key and put it into `conf/conf.php`: +You need to acquire the following API key: 1. **Brave Search API Key** - https://api.search.brave.com/ -See `conf/conf.php` for the exact constant names to use. +Then set up your configuration: -Adjust permission (chmod 777) for 'sessions' folder, run index.php in the www/ directory, fill in the form and voila. +```bash +# Create your local config from the template +cp conf/conf.local.php.dist conf/conf.local.php +# Edit it with your real keys +nano conf/conf.local.php +``` + +`conf/conf.local.php` is gitignored so your secrets stay safe. You can also set the `BRAVE_API_KEY` environment variable as an alternative. + +Adjust permission (chmod 777) for 'sessions' folder, then run index.php in the www/ directory, fill in the form and voila. # yet to come - Logfile also generated in imgProcess Class (0.9.2) diff --git a/conf/conf.local.php.dist b/conf/conf.local.php.dist new file mode 100644 index 0000000..a0c9fdb --- /dev/null +++ b/conf/conf.local.php.dist @@ -0,0 +1,10 @@ + environment variable > placeholder + */ +define('BRAVE_API_KEY', 'your-real-api-key-here'); \ No newline at end of file diff --git a/conf/conf.php b/conf/conf.php index f213c65..801c3e6 100755 --- a/conf/conf.php +++ b/conf/conf.php @@ -13,12 +13,13 @@ define('ZAP_CANVAS_NAME', 'collage'); define('ZAP_DELETE_SOURCE', false); define('ZAP_ADULT', false); define('ZAP_LOG', true); -define('ZAP_DEBUG', true); +define('ZAP_DEBUG', false); // Brave Search API // Get your API key: https://api.search.brave.com/ -define('BRAVE_API_KEY', 'your api key'); +// Put your key in conf/conf.local.php (gitignored) or set BRAVE_API_KEY env var +// See conf/conf.local.php.dist for a template define('BRAVE_BASE', 'https://api.search.brave.com/res/v1/images/search'); $search_engines = array('brave'); diff --git a/conf/init.php b/conf/init.php index c65b349..717c6f8 100755 --- a/conf/init.php +++ b/conf/init.php @@ -1,6 +1,17 @@ Date: Sat, 18 Jul 2026 11:56:54 +0200 Subject: [PATCH 03/10] made ZapHandler library --- docs/ZapHandler.md | 16 ++++++++++++++++ lib/ZAPController.class.php | 3 +++ lib/ZAPDisplay.class.php | 2 +- lib/ZAPHandler.interface.php | 12 ++++++++++++ lib/ZAPHome.class.php | 2 +- lib/ZAPZap.class.php | 2 +- 6 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 docs/ZapHandler.md create mode 100644 lib/ZAPHandler.interface.php diff --git a/docs/ZapHandler.md b/docs/ZapHandler.md new file mode 100644 index 0000000..b6c6e9a --- /dev/null +++ b/docs/ZapHandler.md @@ -0,0 +1,16 @@ +# ZAPHandler Interface + +| File | Change | +|------|--------| +| **`lib/ZAPHandler.interface.php`** | **Created** — defines `ZAPHandler` interface with `public function getContent()` | +| **`lib/ZAPHome.class.php`** | Added `implements ZAPHandler` | +| **`lib/ZAPDisplay.class.php`** | Added `implements ZAPHandler` | +| **`lib/ZAPZap.class.php`** | Added `implements ZAPHandler` | +| **`lib/ZAPController.class.php`** | Added `instanceof ZAPHandler` guard inside `fetch()` before calling `getContent()` | + +### How it works + +- Any class named `ZAP{Mode}` in `lib/` is autoloaded and instantiated by `ZAPController` +- The controller now checks `$this->action instanceof ZAPHandler` **before** calling `getContent()` +- If a handler forgets to implement the interface, you get a clear `die()` message instead of a cryptic runtime error +- The interface file uses the `.interface.php` naming convention, which the existing autoloader already handles (it checks `.interface.php` first, then `.class.php`) \ No newline at end of file diff --git a/lib/ZAPController.class.php b/lib/ZAPController.class.php index 27214f1..115b416 100755 --- a/lib/ZAPController.class.php +++ b/lib/ZAPController.class.php @@ -35,6 +35,9 @@ class ZAPController { * @return string content */ public function fetch() { + if (!($this->action instanceof ZAPHandler)) { + die('Handler does not implement ZAPHandler interface'); + } return $this->action->getContent(); } } diff --git a/lib/ZAPDisplay.class.php b/lib/ZAPDisplay.class.php index a1398ab..2d1ef71 100755 --- a/lib/ZAPDisplay.class.php +++ b/lib/ZAPDisplay.class.php @@ -2,7 +2,7 @@ /** * Class is responsible for starting up ZAP machine */ -class ZAPDisplay { +class ZAPDisplay implements ZAPHandler { /** * Template file */ diff --git a/lib/ZAPHandler.interface.php b/lib/ZAPHandler.interface.php new file mode 100644 index 0000000..4f38e1a --- /dev/null +++ b/lib/ZAPHandler.interface.php @@ -0,0 +1,12 @@ + Date: Sun, 19 Jul 2026 20:17:37 +0200 Subject: [PATCH 04/10] session directory prefix; reset.php made saver. (isues #17 and #16) --- cli_scripts/reset.php | 100 +++++++++++++++++++++++++++++---------- cli_scripts/test.php | 5 +- lib/ZAPDisplay.class.php | 5 +- 3 files changed, 82 insertions(+), 28 deletions(-) diff --git a/cli_scripts/reset.php b/cli_scripts/reset.php index 6cd3f1f..2cc6f1c 100755 --- a/cli_scripts/reset.php +++ b/cli_scripts/reset.php @@ -1,28 +1,80 @@ +#!/usr/bin/env php session reset

'; - -if ($_GET['allcollages'] == 'clear') { - $sessions = glob('./Sessions/*'); - foreach($sessions as $folder){ - $files = glob($folder.'/*'); - foreach($files as $fileToDelete){ - echo "$fileToDelete deleted
"; - unlink($fileToDelete); - } - echo "$folder deleted
"; - rmdir($folder); - } +if (php_sapi_name() !== 'cli') { + die('This script can only be run from the command line.' . PHP_EOL); } -echo '

'; -session_destroy(); -?> \ No newline at end of file +// Make sure relative paths in init.php resolve against the project root +chdir(__DIR__); + +require_once __DIR__ . '/../conf/init.php'; + +// Start a session so reset/destroy have something to work with +if (session_status() === PHP_SESSION_NONE) { + session_start(); +} + +// --- Session reset --- +Session::reset(); + +echo 'Session reset.' . PHP_EOL; + +// --- Optional: purge all session folders --- +$purgeAll = in_array('--all', $argv); + +if ($purgeAll) { + $force = in_array('-f', $argv); + + if (!$force) { + echo 'WARNING: This will permanently delete ALL session folders.' . PHP_EOL; + echo 'Are you sure? (type "yes" to confirm): '; + $handle = fopen('php://stdin', 'r'); + $input = trim(fgets($handle)); + fclose($handle); + + if (strtolower($input) !== 'yes') { + echo 'Aborted.' . PHP_EOL; + exit(0); + } + } + + $sessionsDir = ZAP_APP_BASE_DIR . DIRECTORY_SEPARATOR . ZAP_SESSIONS_DIR; + $folders = glob($sessionsDir . DIRECTORY_SEPARATOR . '*'); + $count = 0; + + foreach ($folders as $folder) { + if (!is_dir($folder)) { + continue; + } + $files = glob($folder . DIRECTORY_SEPARATOR . '*'); + foreach ($files as $file) { + if (is_file($file)) { + unlink($file); + } + } + rmdir($folder); + $count++; + } + + echo "Purged $count session folder(s)." . PHP_EOL; +} + +// Only destroy if a session was actually started +if (session_status() === PHP_SESSION_ACTIVE) { + Session::destroy(); + echo 'Session destroyed.' . PHP_EOL; +} + +exit(0); \ No newline at end of file diff --git a/cli_scripts/test.php b/cli_scripts/test.php index 3a618cd..bb1b29b 100755 --- a/cli_scripts/test.php +++ b/cli_scripts/test.php @@ -85,9 +85,10 @@ function testImgSearch() { return; } - $folder = ZAP_APP_BASE_DIR . DIRECTORY_SEPARATOR . ZAP_SESSIONS_DIR . DIRECTORY_SEPARATOR . 'search_test_' . ZAP_MOMENT; + $suffix = bin2hex(random_bytes(4)); + $folder = ZAP_APP_BASE_DIR . DIRECTORY_SEPARATOR . ZAP_SESSIONS_DIR . DIRECTORY_SEPARATOR . 'search_test_' . ZAP_MOMENT . '_' . $suffix; if (!is_dir($folder)) { - @mkdir($folder, 0777, true); + mkdir($folder, 0777, true); } echo "\nQuery: '$word'\n\n"; diff --git a/lib/ZAPDisplay.class.php b/lib/ZAPDisplay.class.php index 2d1ef71..9cfe2c2 100755 --- a/lib/ZAPDisplay.class.php +++ b/lib/ZAPDisplay.class.php @@ -49,9 +49,10 @@ class ZAPDisplay implements ZAPHandler { */ private function createFolder() { try { + $suffix = bin2hex(random_bytes(4)); $folder = ZAP_APP_BASE_DIR . DIRECTORY_SEPARATOR - . ZAP_SESSIONS_DIR . DIRECTORY_SEPARATOR . 'zap_' . ZAP_MOMENT; - if (!@mkdir($folder, 0777)) { + . ZAP_SESSIONS_DIR . DIRECTORY_SEPARATOR . 'zap_' . ZAP_MOMENT . '_' . $suffix; + if (!mkdir($folder, 0777, true)) { throw new Exception('

' . ERROR_PREFIX . 'Unable to create base dir: ' . $folder . '

'); } return $folder; From ab0ac454151798bdd09d77df43e2088cd4d10f27 Mon Sep 17 00:00:00 2001 From: Fabian de Boer Date: Sun, 19 Jul 2026 21:27:13 +0200 Subject: [PATCH 05/10] wrong mode in index.php now gives a 404 error (#15); anti cross-site-request token --- lib/ZAPController.class.php | 4 +++- lib/ZAPDisplay.class.php | 12 ++++++++++++ tpl/404.tpl | 29 +++++++++++++++++++++++++++++ tpl/index.tpl | 4 ++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tpl/404.tpl diff --git a/lib/ZAPController.class.php b/lib/ZAPController.class.php index 115b416..29df4c8 100755 --- a/lib/ZAPController.class.php +++ b/lib/ZAPController.class.php @@ -26,7 +26,9 @@ class ZAPController { $this->mode = ucfirst($mode); $clazz = $this->prefix . $this->mode; if (!class_exists($clazz)) { - die('Wrong parameter'); + http_response_code(404); + include getZAPTemplate('404'); + die(); } $this->action = new $clazz(); } diff --git a/lib/ZAPDisplay.class.php b/lib/ZAPDisplay.class.php index 9cfe2c2..65656e9 100755 --- a/lib/ZAPDisplay.class.php +++ b/lib/ZAPDisplay.class.php @@ -28,6 +28,18 @@ class ZAPDisplay implements ZAPHandler { */ public function __construct() { ob_start(); + + // Validate CSRF token + if ( + empty($_POST['csrf_token']) + || empty($_SESSION['csrf_token']) + || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']) + ) { + http_response_code(400); + echo '

Invalid or missing security token. Please go back and try again.

'; + die(); + } + $this->folder = $this->createFolder(); $this->words = $this->handleWords(); diff --git a/tpl/404.tpl b/tpl/404.tpl new file mode 100644 index 0000000..68649d5 --- /dev/null +++ b/tpl/404.tpl @@ -0,0 +1,29 @@ + + + + + 404 - ZapMachine + + + + +
error 404
+ + diff --git a/tpl/index.tpl b/tpl/index.tpl index a5fc6f4..249b112 100755 --- a/tpl/index.tpl +++ b/tpl/index.tpl @@ -12,7 +12,11 @@

Input words

+
+
From 9196bf07a732432089787fe970ec2833c30126bb Mon Sep 17 00:00:00 2001 From: Fabian de Boer Date: Sun, 19 Jul 2026 21:46:08 +0200 Subject: [PATCH 06/10] removed log.xml (txt remain) (#22); updated README (#24); achor ZapBase (#23) --- README.md | 4 ++-- conf/init.php | 4 ++-- docs/framework_guide.md | 2 +- lib/ImgTools.class.php | 6 ++---- utils/Log.class.php | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e45046a..a1d4e21 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,12 @@ www.apfab.com Zap stands for 'Zapped Artificial Picture', a collage made from Internet images. A Zap translates a set of words or a text into an image which reflects the visual representation of the words on the Internet. -This web application, Zap Machine, needs a number of words as input. It then takes the most relevant image that search engine Yahoo comes up with. After having collected all images the Zap Machine creates a given number of different collages from those images. +This web application, Zap Machine, needs a number of words as input. It then takes the most relevant image that search engine Brave comes up with. After having collected all images the Zap Machine creates a given number of different collages from those images. ## installation -First you need to run a server with PHP. Please do not use a Windows machine for this purpose, while Windows suck. ApFab recommends Linux or Mac;) +First you need to run a server with PHP. The application is developed and tested on Linux and macOS; Windows is not recommended. ApFab Zap Machine needs some PHP extensions: - JSON (default installed) diff --git a/conf/init.php b/conf/init.php index 717c6f8..f2a6078 100755 --- a/conf/init.php +++ b/conf/init.php @@ -15,7 +15,7 @@ if (!defined('BRAVE_API_KEY')) { /** * Define some app-wide constants */ -define('ZAP_APP_BASE_DIR', realpath('.' . DIRECTORY_SEPARATOR . '..')); +define('ZAP_APP_BASE_DIR', realpath(__DIR__ . DIRECTORY_SEPARATOR . '..')); define('ZAP_WEB_DIR', 'www'); define('ZAP_RESOURCE_DIR', 'resource'); define('ZAP_UTILS_DIR', 'utils'); @@ -104,7 +104,7 @@ function getZAPTemplate($tplname) { if (file_exists($tplfilepath)) { return $tplfilepath; } else { - return ERROR_PREFIX . 'No template'; + throw new Exception(ERROR_PREFIX . 'No template'); } } ?> \ No newline at end of file diff --git a/docs/framework_guide.md b/docs/framework_guide.md index 4bff8d8..4fb7aa6 100644 --- a/docs/framework_guide.md +++ b/docs/framework_guide.md @@ -212,7 +212,7 @@ Key differences from the web entry point: ### Logging -The `Log` class (`utils/Log.class.php`) writes timestamped messages to a session folder. It is used by `ImgSearch` (controlled by the `ZAP_LOG` constant in `conf/conf.php`). Each log call appends to both `log.txt` (plain text) and `log.xml` (XML-friendly) in the session's output directory. +The `Log` class (`utils/Log.class.php`) writes timestamped messages to a session folder. It is used by `ImgSearch` (controlled by the `ZAP_LOG` constant in `conf/conf.php`). Each log call appends to both `log.txt` (plain text) and `log.html` (HTML-friendly) in the session's output directory. ```php if (ZAP_LOG == true) { diff --git a/lib/ImgTools.class.php b/lib/ImgTools.class.php index 995ad9f..e743235 100755 --- a/lib/ImgTools.class.php +++ b/lib/ImgTools.class.php @@ -112,10 +112,8 @@ class ImgTools { $mimeType = $imgInfo['mime']; //find gif if ($mimeType == "image/gif") { - $imagetype = 'png'; - //full image name - $Dot = '.'; - $newFilename = "$filename$Dot$imagetype"; + // Replace .gif extension with .png + $newFilename = preg_replace('/\.gif$/i', '.png', $filename); //convert $img = new Imagick($filename); diff --git a/utils/Log.class.php b/utils/Log.class.php index db162db..3ff120f 100755 --- a/utils/Log.class.php +++ b/utils/Log.class.php @@ -13,7 +13,7 @@ class Log { $this->folder = $imgFolder; $this->logFileName = $imgFolder . DIRECTORY_SEPARATOR . 'log.txt'; - $this->logFileXmlName = $imgFolder . DIRECTORY_SEPARATOR . 'log.xml'; + $this->logFileXmlName = $imgFolder . DIRECTORY_SEPARATOR . 'log.html'; } public function addToLog($type, $head, $addString) { From e96b8e5563ada19623877a1042f9365ff39b034b Mon Sep 17 00:00:00 2001 From: Fabian de Boer Date: Sun, 19 Jul 2026 22:47:19 +0200 Subject: [PATCH 07/10] zapWidth and zapHeight are now in conf.php (#21) --- conf/conf.php | 3 +++ lib/ImgIOTools.class.php | 10 +++------- lib/ImgTools.class.php | 6 +----- tpl/index.tpl | 4 ++-- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/conf/conf.php b/conf/conf.php index 801c3e6..67f380a 100755 --- a/conf/conf.php +++ b/conf/conf.php @@ -6,6 +6,9 @@ define('MAX_WORDS', 4); define('MAX_ZAPS', 9); +define('ZAP_DEFAULT_WIDTH', 1067); +define('ZAP_DEFAULT_HEIGHT', 600); + define('ZAP_CURL_TIMEOUT', 12); define('ZAP_IMG_TYPE', 'jpg'); define('ZAP_CANVAS_NAME', 'collage'); diff --git a/lib/ImgIOTools.class.php b/lib/ImgIOTools.class.php index a41127f..724d091 100755 --- a/lib/ImgIOTools.class.php +++ b/lib/ImgIOTools.class.php @@ -3,9 +3,6 @@ * Image Input Output Tools */ class ImgIOTools { - public static $maxScreenWidth = 1067; //screen dimensions - - public static $maxScreenHeight = 600; /** * Flushes image to screen @@ -13,9 +10,9 @@ class ImgIOTools { * @return void */ public static function onScreen(Imagick $img) { - $img->scaleImage(self::$maxScreenWidth, self::$maxScreenHeight, true); + $img->scaleImage(ZAP_DEFAULT_WIDTH, ZAP_DEFAULT_HEIGHT, true); - //$img = self::scaleByLength($img, $this->maxScreenWidth, $this->maxScreenHeight); + //$img = self::scaleByLength($img, ZAP_DEFAULT_WIDTH, ZAP_DEFAULT_HEIGHT); header('Content-type: image/' . ZAP_IMG_TYPE); echo $img->getImageBlob(); } @@ -46,5 +43,4 @@ class ImgIOTools { $path = $folder . $filename . '.' . ZAP_IMG_TYPE; return $img->writeImage($path); } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/lib/ImgTools.class.php b/lib/ImgTools.class.php index e743235..2cd68b0 100755 --- a/lib/ImgTools.class.php +++ b/lib/ImgTools.class.php @@ -3,10 +3,6 @@ * Library of tool functions to manipulate an image */ class ImgTools { - public static $maxScreenWidth = 1067; //screen dimensions - - public static $maxScreenHeight = 600; - public static $minFuzzFactor = 0.08; //minimal factor for $fuzz public static $maxFuzzFactor = 0.64; //maximal factor for $fuzz @@ -161,7 +157,7 @@ class ImgTools { public static function scaleByLength(Imagick $img, $maxW, $maxH) { $width = $img->getImageWidth(); $height = $img->getImageHeight(); - if(($width <= self::$maxScreenWidth) && ($height <= self::$maxScreenHeight)) { + if(($width <= ZAP_DEFAULT_WIDTH) && ($height <= ZAP_DEFAULT_HEIGHT)) { return $img; } else { if ($width >= $height) { //horizontal image diff --git a/tpl/index.tpl b/tpl/index.tpl index 249b112..68354e2 100755 --- a/tpl/index.tpl +++ b/tpl/index.tpl @@ -32,10 +32,10 @@
- width px + width px
- height px + height px
From ffab9dc4da541c8579738db8a1b8b1bad4e38c4d Mon Sep 17 00:00:00 2001 From: Fabian de Boer Date: Sun, 19 Jul 2026 22:49:00 +0200 Subject: [PATCH 08/10] removed kanban from gitignore --- .gitignore | 1 - kanban/.lock | 0 kanban/activity.jsonl | 87 +++++++++++++++++++ kanban/config.yml | 47 ++++++++++ kanban/tasks/001-pixabay-api.md | 13 +++ kanban/tasks/002-pexels-api.md | 13 +++ kanban/tasks/003-random-words.md | 13 +++ .../tasks/004-search-for-original-images.md | 13 +++ kanban/tasks/005-image-processing.md | 14 +++ kanban/tasks/006-full-screen-gui.md | 14 +++ .../007-ai-generated-images-for-input.md | 14 +++ kanban/tasks/008-automatic-mode.md | 14 +++ kanban/tasks/009-framework-guide.md | 13 +++ kanban/tasks/010-check-framework.md | 16 ++++ ...request-building-lazy-url-single-encode.md | 22 +++++ ...utoloader-use-path-separator-instead-of.md | 20 +++++ ...-hygiene-zap-debug-false-secrets-in-git.md | 19 ++++ ...-add-csrf-protection-to-word-input-form.md | 17 ++++ ...e-wrong-parameter-with-proper-404-error.md | 17 ++++ ...16-fix-and-secure-cli-scripts-reset-php.md | 23 +++++ ...on-folder-names-unique-stop-suppressing.md | 17 ++++ ...uce-zaphandler-interface-for-getcontent.md | 18 ++++ ...-source-of-truth-for-word-count-cleanup.md | 13 +++ ...t-template-rendering-pattern-across-all.md | 13 +++ ...lidate-duplicated-screen-size-constants.md | 17 ++++ ...tgifstopng-extension-rename-fix-log-xml.md | 19 ++++ ...app-base-dir-to-dir-make-getzaptemplate.md | 19 ++++ ...eadme-yahoo-brave-drop-outdated-roadmap.md | 17 ++++ 28 files changed, 522 insertions(+), 1 deletion(-) create mode 100644 kanban/.lock create mode 100644 kanban/activity.jsonl create mode 100644 kanban/config.yml create mode 100644 kanban/tasks/001-pixabay-api.md create mode 100644 kanban/tasks/002-pexels-api.md create mode 100644 kanban/tasks/003-random-words.md create mode 100644 kanban/tasks/004-search-for-original-images.md create mode 100644 kanban/tasks/005-image-processing.md create mode 100644 kanban/tasks/006-full-screen-gui.md create mode 100644 kanban/tasks/007-ai-generated-images-for-input.md create mode 100644 kanban/tasks/008-automatic-mode.md create mode 100644 kanban/tasks/009-framework-guide.md create mode 100644 kanban/tasks/010-check-framework.md create mode 100644 kanban/tasks/011-fix-brave-request-building-lazy-url-single-encode.md create mode 100644 kanban/tasks/012-fix-autoloader-use-path-separator-instead-of.md create mode 100644 kanban/tasks/013-config-hygiene-zap-debug-false-secrets-in-git.md create mode 100644 kanban/tasks/014-add-csrf-protection-to-word-input-form.md create mode 100644 kanban/tasks/015-replace-die-wrong-parameter-with-proper-404-error.md create mode 100644 kanban/tasks/016-fix-and-secure-cli-scripts-reset-php.md create mode 100644 kanban/tasks/017-make-session-folder-names-unique-stop-suppressing.md create mode 100644 kanban/tasks/018-introduce-zaphandler-interface-for-getcontent.md create mode 100644 kanban/tasks/019-single-source-of-truth-for-word-count-cleanup.md create mode 100644 kanban/tasks/020-consistent-template-rendering-pattern-across-all.md create mode 100644 kanban/tasks/021-consolidate-duplicated-screen-size-constants.md create mode 100644 kanban/tasks/022-fix-convertgifstopng-extension-rename-fix-log-xml.md create mode 100644 kanban/tasks/023-anchor-zap-app-base-dir-to-dir-make-getzaptemplate.md create mode 100644 kanban/tasks/024-refresh-readme-yahoo-brave-drop-outdated-roadmap.md diff --git a/.gitignore b/.gitignore index 4dfc547..b9c4024 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ sessions/* conf/conf.local.php ignore -kanban/ .pi-tasks docs/framework_review.md diff --git a/kanban/.lock b/kanban/.lock new file mode 100644 index 0000000..e69de29 diff --git a/kanban/activity.jsonl b/kanban/activity.jsonl new file mode 100644 index 0000000..1453eab --- /dev/null +++ b/kanban/activity.jsonl @@ -0,0 +1,87 @@ +{"timestamp":"2026-07-15T14:55:51.244024579+02:00","action":"create","task_id":1,"detail":"pixabay api"} +{"timestamp":"2026-07-15T14:56:56.736534944+02:00","action":"edit","task_id":1,"detail":"pixabay api"} +{"timestamp":"2026-07-15T14:58:10.598825995+02:00","action":"create","task_id":2,"detail":"Pexels api"} +{"timestamp":"2026-07-15T14:58:27.253134741+02:00","action":"edit","task_id":2,"detail":"Pexels api"} +{"timestamp":"2026-07-15T15:00:10.532523584+02:00","action":"create","task_id":3,"detail":"random words"} +{"timestamp":"2026-07-15T15:02:31.109387111+02:00","action":"create","task_id":4,"detail":"search for original images"} +{"timestamp":"2026-07-15T15:02:43.403448994+02:00","action":"edit","task_id":4,"detail":"search for original images"} +{"timestamp":"2026-07-15T15:13:21.132728219+02:00","action":"create","task_id":5,"detail":"image processing"} +{"timestamp":"2026-07-15T15:15:54.327404411+02:00","action":"create","task_id":6,"detail":"full screen gui"} +{"timestamp":"2026-07-15T15:17:54.955029144+02:00","action":"create","task_id":7,"detail":"ai generated images for input"} +{"timestamp":"2026-07-15T15:20:29.077954887+02:00","action":"create","task_id":8,"detail":"automatic mode"} +{"timestamp":"2026-07-15T15:22:18.569491561+02:00","action":"create","task_id":9,"detail":"library guide"} +{"timestamp":"2026-07-15T15:22:33.368044405+02:00","action":"move","task_id":9,"detail":"backlog -\u003e todo"} +{"timestamp":"2026-07-15T15:23:07.895052923+02:00","action":"move","task_id":6,"detail":"backlog -\u003e todo"} +{"timestamp":"2026-07-15T15:23:14.910744836+02:00","action":"move","task_id":5,"detail":"backlog -\u003e todo"} +{"timestamp":"2026-07-17T00:46:24.790499349+02:00","action":"edit","task_id":9,"detail":"framework guide"} +{"timestamp":"2026-07-17T01:14:23.984206626+02:00","action":"move","task_id":9,"detail":"todo -\u003e in-progress"} +{"timestamp":"2026-07-17T02:16:23.061670813+02:00","action":"create","task_id":10,"detail":"check framework"} +{"timestamp":"2026-07-17T02:16:52.676988996+02:00","action":"move","task_id":9,"detail":"in-progress -\u003e done"} +{"timestamp":"2026-07-17T02:54:59.922251442+02:00","action":"move","task_id":10,"detail":"todo -\u003e in-progress"} +{"timestamp":"2026-07-17T03:04:14.08873263+02:00","action":"priority","task_id":10,"detail":"high -\u003e medium"} +{"timestamp":"2026-07-17T03:04:15.072297537+02:00","action":"priority","task_id":10,"detail":"medium -\u003e low"} +{"timestamp":"2026-07-17T03:04:23.967707693+02:00","action":"priority","task_id":10,"detail":"low -\u003e medium"} +{"timestamp":"2026-07-17T03:04:24.575872692+02:00","action":"priority","task_id":10,"detail":"medium -\u003e high"} +{"timestamp":"2026-07-17T03:25:58.441011923+02:00","action":"edit","task_id":10,"detail":"check framework"} +{"timestamp":"2026-07-17T03:26:01.636755342+02:00","action":"move","task_id":10,"detail":"in-progress -\u003e review"} +{"timestamp":"2026-07-17T03:26:18.979779542+02:00","action":"edit","task_id":10,"detail":"check framework"} +{"timestamp":"2026-07-17T03:26:18.979863107+02:00","action":"release","task_id":10,"detail":"tui@mcBookje"} +{"timestamp":"2026-07-17T03:50:56.347742215+02:00","action":"move","task_id":6,"detail":"todo -\u003e backlog"} +{"timestamp":"2026-07-17T03:51:00.178032766+02:00","action":"move","task_id":5,"detail":"todo -\u003e backlog"} +{"timestamp":"2026-07-17T03:57:00.446052295+02:00","action":"create","task_id":11,"detail":"Fix Brave request building: lazy URL + single encode"} +{"timestamp":"2026-07-17T03:57:06.830184988+02:00","action":"create","task_id":12,"detail":"Fix autoloader: use PATH_SEPARATOR instead of hardcoded ':'"} +{"timestamp":"2026-07-17T03:57:13.889026673+02:00","action":"create","task_id":13,"detail":"Config hygiene: ZAP_DEBUG=false, secrets in git-ignored local file, sessions perms"} +{"timestamp":"2026-07-17T03:57:20.700148188+02:00","action":"create","task_id":14,"detail":"Add CSRF protection to word-input form"} +{"timestamp":"2026-07-17T03:57:28.29860719+02:00","action":"create","task_id":15,"detail":"Replace die('Wrong parameter') with proper 404/error handler"} +{"timestamp":"2026-07-17T03:57:35.273661605+02:00","action":"create","task_id":16,"detail":"Fix and secure cli_scripts/reset.php"} +{"timestamp":"2026-07-17T03:57:50.885872288+02:00","action":"create","task_id":17,"detail":"Make session folder names unique, stop suppressing mkdir errors"} +{"timestamp":"2026-07-17T03:57:59.05551049+02:00","action":"create","task_id":18,"detail":"Introduce ZAPHandler interface for getContent()"} +{"timestamp":"2026-07-17T03:58:05.367581371+02:00","action":"create","task_id":19,"detail":"Single source of truth for word count + cleanup handleWords"} +{"timestamp":"2026-07-17T03:58:11.989024488+02:00","action":"create","task_id":20,"detail":"Consistent template rendering pattern across all handlers"} +{"timestamp":"2026-07-17T03:58:20.432255313+02:00","action":"create","task_id":21,"detail":"Consolidate duplicated screen-size constants"} +{"timestamp":"2026-07-17T03:58:30.405465201+02:00","action":"create","task_id":22,"detail":"Fix convertGifsToPng extension + rename/fix log.xml"} +{"timestamp":"2026-07-17T03:58:37.159090865+02:00","action":"create","task_id":23,"detail":"Anchor ZAP_APP_BASE_DIR to __DIR__, make getZAPTemplate throw"} +{"timestamp":"2026-07-17T03:58:43.664420067+02:00","action":"create","task_id":24,"detail":"Refresh README (Yahoo→Brave, drop outdated roadmap, fix tone)"} +{"timestamp":"2026-07-17T12:26:07.265732515+02:00","action":"move","task_id":10,"detail":"review -\u003e done"} +{"timestamp":"2026-07-17T12:26:29.348995723+02:00","action":"move","task_id":11,"detail":"todo -\u003e in-progress"} +{"timestamp":"2026-07-17T12:26:34.350031444+02:00","action":"edit","task_id":11,"detail":"Fix Brave request building: lazy URL + single encode"} +{"timestamp":"2026-07-17T12:26:38.760178641+02:00","action":"move","task_id":11,"detail":"in-progress -\u003e review"} +{"timestamp":"2026-07-17T12:26:38.760294679+02:00","action":"handoff","task_id":11,"detail":"Fix Brave request building: lazy URL + single encode"} +{"timestamp":"2026-07-17T12:26:38.760385873+02:00","action":"release","task_id":11,"detail":"Fix Brave request building: lazy URL + single encode"} +{"timestamp":"2026-07-17T12:27:40.64656083+02:00","action":"move","task_id":12,"detail":"todo -\u003e in-progress"} +{"timestamp":"2026-07-17T12:27:53.052811022+02:00","action":"edit","task_id":12,"detail":"Fix autoloader: use PATH_SEPARATOR instead of hardcoded ':'"} +{"timestamp":"2026-07-17T12:27:53.056394858+02:00","action":"move","task_id":12,"detail":"in-progress -\u003e review"} +{"timestamp":"2026-07-17T12:27:53.056484271+02:00","action":"handoff","task_id":12,"detail":"Fix autoloader: use PATH_SEPARATOR instead of hardcoded ':'"} +{"timestamp":"2026-07-17T12:27:53.056530205+02:00","action":"release","task_id":12,"detail":"Fix autoloader: use PATH_SEPARATOR instead of hardcoded ':'"} +{"timestamp":"2026-07-17T21:40:38.025770941+02:00","action":"move","task_id":13,"detail":"todo -\u003e in-progress"} +{"timestamp":"2026-07-17T21:54:34.996015797+02:00","action":"move","task_id":13,"detail":"in-progress -\u003e review"} +{"timestamp":"2026-07-17T22:27:10.995630482+02:00","action":"move","task_id":13,"detail":"review -\u003e done"} +{"timestamp":"2026-07-18T11:42:51.254205784+02:00","action":"move","task_id":18,"detail":"todo -\u003e in-progress"} +{"timestamp":"2026-07-18T11:50:59.773873861+02:00","action":"edit","task_id":18,"detail":"Introduce ZAPHandler interface for getContent()"} +{"timestamp":"2026-07-18T11:51:03.257951319+02:00","action":"edit","task_id":18,"detail":"Introduce ZAPHandler interface for getContent()"} +{"timestamp":"2026-07-18T11:51:03.258056745+02:00","action":"release","task_id":18,"detail":"tui@mcBookje"} +{"timestamp":"2026-07-18T11:51:03.28154522+02:00","action":"move","task_id":18,"detail":"in-progress -\u003e done"} +{"timestamp":"2026-07-19T19:06:12.923187222+02:00","action":"edit","task_id":17,"detail":"Make session folder names unique, stop suppressing mkdir errors"} +{"timestamp":"2026-07-19T19:06:12.930146038+02:00","action":"claim","task_id":17,"detail":"rain-raven"} +{"timestamp":"2026-07-19T19:06:58.839482716+02:00","action":"move","task_id":17,"detail":"todo -\u003e done"} +{"timestamp":"2026-07-19T20:09:41.967705512+02:00","action":"edit","task_id":16,"detail":"Fix and secure cli_scripts/reset.php"} +{"timestamp":"2026-07-19T20:09:41.967823882+02:00","action":"claim","task_id":16,"detail":"rain-raven"} +{"timestamp":"2026-07-19T20:09:41.988956545+02:00","action":"move","task_id":16,"detail":"todo -\u003e done"} +{"timestamp":"2026-07-19T20:20:19.991872348+02:00","action":"edit","task_id":15,"detail":"Replace die('Wrong parameter') with proper 404/error handler"} +{"timestamp":"2026-07-19T20:20:19.991950864+02:00","action":"claim","task_id":15,"detail":"rain-raven"} +{"timestamp":"2026-07-19T20:20:20.013428274+02:00","action":"move","task_id":15,"detail":"todo -\u003e done"} +{"timestamp":"2026-07-19T21:23:27.794040982+02:00","action":"edit","task_id":14,"detail":"Add CSRF protection to word-input form"} +{"timestamp":"2026-07-19T21:23:27.794120976+02:00","action":"claim","task_id":14,"detail":"rain-raven"} +{"timestamp":"2026-07-19T21:23:27.819566153+02:00","action":"move","task_id":14,"detail":"todo -\u003e done"} +{"timestamp":"2026-07-19T21:31:14.49434419+02:00","action":"edit","task_id":24,"detail":"Refresh README (Yahoo→Brave, drop outdated roadmap, fix tone)"} +{"timestamp":"2026-07-19T21:31:14.494474852+02:00","action":"claim","task_id":24,"detail":"rain-raven"} +{"timestamp":"2026-07-19T21:31:14.518300878+02:00","action":"move","task_id":24,"detail":"todo -\u003e done"} +{"timestamp":"2026-07-19T21:39:02.761552753+02:00","action":"edit","task_id":23,"detail":"Anchor ZAP_APP_BASE_DIR to __DIR__, make getZAPTemplate throw"} +{"timestamp":"2026-07-19T21:39:02.761712873+02:00","action":"claim","task_id":23,"detail":"rain-raven"} +{"timestamp":"2026-07-19T21:39:02.783567342+02:00","action":"move","task_id":23,"detail":"todo -\u003e done"} +{"timestamp":"2026-07-19T21:42:54.751029245+02:00","action":"edit","task_id":22,"detail":"Fix convertGifsToPng extension + rename/fix log.xml"} +{"timestamp":"2026-07-19T21:42:54.751140546+02:00","action":"claim","task_id":22,"detail":"rain-raven"} +{"timestamp":"2026-07-19T21:42:54.773029003+02:00","action":"move","task_id":22,"detail":"todo -\u003e done"} +{"timestamp":"2026-07-19T21:49:54.312195218+02:00","action":"edit","task_id":21,"detail":"Consolidate duplicated screen-size constants"} +{"timestamp":"2026-07-19T21:49:54.312297605+02:00","action":"claim","task_id":21,"detail":"rain-raven"} +{"timestamp":"2026-07-19T21:49:54.334491216+02:00","action":"move","task_id":21,"detail":"todo -\u003e done"} diff --git a/kanban/config.yml b/kanban/config.yml new file mode 100644 index 0000000..eb9fd78 --- /dev/null +++ b/kanban/config.yml @@ -0,0 +1,47 @@ +version: 10 +board: + name: ZapMachine +tasks_dir: tasks +statuses: + - name: backlog + show_duration: false + - name: todo + - name: in-progress + require_claim: true + - name: review + require_claim: true + - name: done + show_duration: false + - name: archived + show_duration: false +priorities: + - low + - medium + - high + - critical +defaults: + status: backlog + priority: medium + class: standard +claim_timeout: 1m +classes: + - name: expedite + wip_limit: 1 + bypass_column_wip: true + - name: fixed-date + - name: standard + - name: intangible +tui: + title_lines: 2 + age_thresholds: + - after: 0s + color: "242" + - after: 1h + color: "34" + - after: 24h + color: "226" + - after: 72h + color: "208" + - after: 168h + color: "196" +next_id: 25 diff --git a/kanban/tasks/001-pixabay-api.md b/kanban/tasks/001-pixabay-api.md new file mode 100644 index 0000000..85e9e35 --- /dev/null +++ b/kanban/tasks/001-pixabay-api.md @@ -0,0 +1,13 @@ +--- +id: 1 +title: pixabay api +status: backlog +priority: high +created: 2026-07-15T14:55:51.243108541+02:00 +updated: 2026-07-15T14:56:56.735673124+02:00 +tags: + - search +class: standard +--- + +Add Pixabay api as search engine diff --git a/kanban/tasks/002-pexels-api.md b/kanban/tasks/002-pexels-api.md new file mode 100644 index 0000000..3d8f41b --- /dev/null +++ b/kanban/tasks/002-pexels-api.md @@ -0,0 +1,13 @@ +--- +id: 2 +title: Pexels api +status: backlog +priority: high +created: 2026-07-15T14:58:10.597616484+02:00 +updated: 2026-07-15T14:58:27.252246225+02:00 +tags: + - search +class: standard +--- + +Use pexels api as a seach engine diff --git a/kanban/tasks/003-random-words.md b/kanban/tasks/003-random-words.md new file mode 100644 index 0000000..28991e1 --- /dev/null +++ b/kanban/tasks/003-random-words.md @@ -0,0 +1,13 @@ +--- +id: 3 +title: random words +status: backlog +priority: high +created: 2026-07-15T15:00:10.530100379+02:00 +updated: 2026-07-15T15:00:10.530100379+02:00 +tags: + - search +class: standard +--- + +random word version (https://random-words-api.kushcreates.com/) diff --git a/kanban/tasks/004-search-for-original-images.md b/kanban/tasks/004-search-for-original-images.md new file mode 100644 index 0000000..0cb420b --- /dev/null +++ b/kanban/tasks/004-search-for-original-images.md @@ -0,0 +1,13 @@ +--- +id: 4 +title: search for original images +status: backlog +priority: low +created: 2026-07-15T15:02:31.107843015+02:00 +updated: 2026-07-15T15:02:43.402550141+02:00 +tags: + - search +class: standard +--- + +input number and search for a fresh photo with a camera filename like 'img####.jpg' diff --git a/kanban/tasks/005-image-processing.md b/kanban/tasks/005-image-processing.md new file mode 100644 index 0000000..c472770 --- /dev/null +++ b/kanban/tasks/005-image-processing.md @@ -0,0 +1,14 @@ +--- +id: 5 +title: image processing +status: backlog +priority: critical +created: 2026-07-15T15:13:21.131166647+02:00 +updated: 2026-07-17T03:51:00.175266589+02:00 +started: 2026-07-15T15:23:14.91034394+02:00 +tags: + - image_processing +class: standard +--- + +Put back the original image processing from the old Zap Machine diff --git a/kanban/tasks/006-full-screen-gui.md b/kanban/tasks/006-full-screen-gui.md new file mode 100644 index 0000000..4c107a0 --- /dev/null +++ b/kanban/tasks/006-full-screen-gui.md @@ -0,0 +1,14 @@ +--- +id: 6 +title: full screen gui +status: backlog +priority: critical +created: 2026-07-15T15:15:54.325695132+02:00 +updated: 2026-07-17T03:50:56.343336322+02:00 +started: 2026-07-15T15:23:07.894599552+02:00 +tags: + - gui +class: standard +--- + +Full screen GUI with the collage covering the background and a partly transparen control panel and feedback screen on the right diff --git a/kanban/tasks/007-ai-generated-images-for-input.md b/kanban/tasks/007-ai-generated-images-for-input.md new file mode 100644 index 0000000..0680553 --- /dev/null +++ b/kanban/tasks/007-ai-generated-images-for-input.md @@ -0,0 +1,14 @@ +--- +id: 7 +title: ai generated images for input +status: backlog +priority: medium +created: 2026-07-15T15:17:54.953039352+02:00 +updated: 2026-07-15T15:17:54.953039352+02:00 +tags: + - search + - source +class: standard +--- + +use a scraper (brightdata) or write one ourselves to scrape for ai genarated images as sources diff --git a/kanban/tasks/008-automatic-mode.md b/kanban/tasks/008-automatic-mode.md new file mode 100644 index 0000000..b71b059 --- /dev/null +++ b/kanban/tasks/008-automatic-mode.md @@ -0,0 +1,14 @@ +--- +id: 8 +title: automatic mode +status: backlog +priority: medium +created: 2026-07-15T15:20:29.075465548+02:00 +updated: 2026-07-15T15:20:29.075465548+02:00 +tags: + - gui + - mode +class: standard +--- + +if one or more people are watching automatically generate a zap layer every 21 seconds diff --git a/kanban/tasks/009-framework-guide.md b/kanban/tasks/009-framework-guide.md new file mode 100644 index 0000000..7b5abd3 --- /dev/null +++ b/kanban/tasks/009-framework-guide.md @@ -0,0 +1,13 @@ +--- +id: 9 +title: framework guide +status: done +priority: critical +created: 2026-07-15T15:22:18.567355625+02:00 +updated: 2026-07-17T02:16:52.6730631+02:00 +started: 2026-07-15T15:22:33.367755161+02:00 +completed: 2026-07-17T02:16:52.676581649+02:00 +class: standard +--- + +add the usage of the framework in the README. How does this framework work, how to add templates with interactive elements diff --git a/kanban/tasks/010-check-framework.md b/kanban/tasks/010-check-framework.md new file mode 100644 index 0000000..f2fe51f --- /dev/null +++ b/kanban/tasks/010-check-framework.md @@ -0,0 +1,16 @@ +--- +id: 10 +title: check framework +status: done +priority: high +created: 2026-07-17T02:16:23.059189291+02:00 +updated: 2026-07-17T12:26:07.258170416+02:00 +started: 2026-07-17T12:26:07.265125164+02:00 +completed: 2026-07-17T12:26:07.265125164+02:00 +class: standard +--- + +Check if the framework is the optimal, most simple and flexible way to go. If not: suggest some changes. + +[[2026-07-17]] Fri 03:25 +Framework review committed: docs/framework_review.md (commit 2f61b07). Verdict: architecture is simple/flexible (zero-config routing, autoloader, Search plugin interface, native templates) — keep as-is. Not optimal due to 3 P0 correctness bugs (Brave size/offset params never sent because setQuery builds URL before setParam; query double-URL-encoded; autoloader hardcodes ':' vs PATH_SEPARATOR), P1 config/security (ZAP_DEBUG=true & placeholder key committed, no CSRF, die() routing, unsafe reset.php, second-resolution session folder collisions), P2 consistency. NO source edits — deliverable is docs-only. Includes recommended-task table for follow-up fixes (each independently approvable). Handing to review for Fabian's approval before any code changes. diff --git a/kanban/tasks/011-fix-brave-request-building-lazy-url-single-encode.md b/kanban/tasks/011-fix-brave-request-building-lazy-url-single-encode.md new file mode 100644 index 0000000..5f2c2cc --- /dev/null +++ b/kanban/tasks/011-fix-brave-request-building-lazy-url-single-encode.md @@ -0,0 +1,22 @@ +--- +id: 11 +title: 'Fix Brave request building: lazy URL + single encode' +status: review +priority: high +created: 2026-07-17T03:57:00.444816503+02:00 +updated: 2026-07-17T12:26:38.759875442+02:00 +tags: + - search + - bug +class: standard +--- + +From framework review (docs/framework_review.md). Two-part fix in resource/Brave.class.php + lib/ImgSearch.class.php: +1. Move URL construction from setQuery() to getData() so setParam('start'/'size') values are applied (currently size defaults to 3 instead of 16, offset is never sent). +2. Remove double urlencode: ImgSearch passes urlencode(word) into Brave::setQuery which urlencodes again → %2520 for spaces. Fix: pass raw word from ImgSearch; engine owns encoding. + +[[2026-07-17]] Fri 12:26 +P0-1: Moved URL construction from setQuery() to new buildRequest() called from getData() — size/offset now use setParam() values (resource/Brave.class.php). P0-2: Removed urlencode() call from ImgSearch::getImageData() — raw word passed to engine, single encode in Brave (lib/ImgSearch.class.php). Both pass php -l. + +[[2026-07-17]] Fri 12:26 +P0-1 + P0-2 implemented. Changes to resource/Brave.class.php and lib/ImgSearch.class.php. Both pass php -l. Ready for your commit. diff --git a/kanban/tasks/012-fix-autoloader-use-path-separator-instead-of.md b/kanban/tasks/012-fix-autoloader-use-path-separator-instead-of.md new file mode 100644 index 0000000..211a40d --- /dev/null +++ b/kanban/tasks/012-fix-autoloader-use-path-separator-instead-of.md @@ -0,0 +1,20 @@ +--- +id: 12 +title: 'Fix autoloader: use PATH_SEPARATOR instead of hardcoded '':''' +status: review +priority: high +created: 2026-07-17T03:57:06.82901804+02:00 +updated: 2026-07-17T12:27:53.056263524+02:00 +tags: + - framework + - bug +class: standard +--- + +From framework review (docs/framework_review.md). conf/init.php uses explode(':', get_include_path()) but the include path separator is ':' on Unix and ';' on Windows. Makes the autoloader fail on Windows. Fix: explode(PATH_SEPARATOR, get_include_path()). + +[[2026-07-17]] Fri 12:27 +Changed explode(':', ...) to explode(PATH_SEPARATOR, ...) in conf/init.php. PHP passes php -l. + +[[2026-07-17]] Fri 12:27 +P0-3 fixed. One-line change in conf/init.php. Ready for your commit. diff --git a/kanban/tasks/013-config-hygiene-zap-debug-false-secrets-in-git.md b/kanban/tasks/013-config-hygiene-zap-debug-false-secrets-in-git.md new file mode 100644 index 0000000..0d6ba07 --- /dev/null +++ b/kanban/tasks/013-config-hygiene-zap-debug-false-secrets-in-git.md @@ -0,0 +1,19 @@ +--- +id: 13 +title: 'Config hygiene: ZAP_DEBUG=false, secrets in git-ignored local file, sessions perms' +status: done +priority: medium +created: 2026-07-17T03:57:13.88777683+02:00 +updated: 2026-07-17T22:27:10.989415474+02:00 +started: 2026-07-17T22:27:10.994979516+02:00 +completed: 2026-07-17T22:27:10.994979516+02:00 +tags: + - security + - config +class: standard +--- + +From framework review (docs/framework_review.md), P1-1. conf/conf.php is git-tracked and ships with ZAP_DEBUG=true, BRAVE_API_KEY='your api key' placeholder. Fixes: +- Default ZAP_DEBUG=false in committed conf.php +- Add git-ignored conf/conf.local.php loaded by init.php if present, for secrets +- Tighten sessions/ permissions from 777 to 770 (or document webserver-user group) diff --git a/kanban/tasks/014-add-csrf-protection-to-word-input-form.md b/kanban/tasks/014-add-csrf-protection-to-word-input-form.md new file mode 100644 index 0000000..1dc212c --- /dev/null +++ b/kanban/tasks/014-add-csrf-protection-to-word-input-form.md @@ -0,0 +1,17 @@ +--- +id: 14 +title: Add CSRF protection to word-input form +status: done +priority: medium +created: 2026-07-17T03:57:20.699057952+02:00 +updated: 2026-07-19T21:23:27.817959707+02:00 +started: 2026-07-19T21:23:27.819406405+02:00 +completed: 2026-07-19T21:23:27.819406405+02:00 +tags: + - security +claimed_by: rain-raven +claimed_at: 2026-07-19T21:23:27.817959707+02:00 +class: standard +--- + +From framework review (docs/framework_review.md), P1-2. tpl/index.tpl POSTs to mode=display which writes session state and triggers image downloads. No CSRF token means a cross-site request can drive the machine. Fix: add session-bound CSRF token to the form, validate in ZAPDisplay. diff --git a/kanban/tasks/015-replace-die-wrong-parameter-with-proper-404-error.md b/kanban/tasks/015-replace-die-wrong-parameter-with-proper-404-error.md new file mode 100644 index 0000000..0119a46 --- /dev/null +++ b/kanban/tasks/015-replace-die-wrong-parameter-with-proper-404-error.md @@ -0,0 +1,17 @@ +--- +id: 15 +title: Replace die('Wrong parameter') with proper 404/error handler +status: done +priority: medium +created: 2026-07-17T03:57:28.297533467+02:00 +updated: 2026-07-19T20:20:20.011896223+02:00 +started: 2026-07-19T20:20:20.013282064+02:00 +completed: 2026-07-19T20:20:20.013282064+02:00 +tags: + - framework +claimed_by: rain-raven +claimed_at: 2026-07-19T20:20:20.011896223+02:00 +class: standard +--- + +From framework review (docs/framework_review.md), P1-3. ZAPController::__construct() calls die('Wrong parameter') when an unknown mode is given — no HTTP status, no HTML, and the polling onerror contract expects ERROR_PREFIX HTML. Fix: return a proper 404 response (or brand error page via ZAPError handler). Also consider mode allowlist. diff --git a/kanban/tasks/016-fix-and-secure-cli-scripts-reset-php.md b/kanban/tasks/016-fix-and-secure-cli-scripts-reset-php.md new file mode 100644 index 0000000..1932343 --- /dev/null +++ b/kanban/tasks/016-fix-and-secure-cli-scripts-reset-php.md @@ -0,0 +1,23 @@ +--- +id: 16 +title: Fix and secure cli_scripts/reset.php +status: done +priority: medium +created: 2026-07-17T03:57:35.272402311+02:00 +updated: 2026-07-19T20:09:41.987393047+02:00 +started: 2026-07-19T20:09:41.988806323+02:00 +completed: 2026-07-19T20:09:41.988806323+02:00 +tags: + - cli + - security +claimed_by: rain-raven +claimed_at: 2026-07-19T20:09:41.987393047+02:00 +class: standard +--- + +From framework review (docs/framework_review.md), P1-4. cli_scripts/reset.php has multiple issues: +- Case mismatch: glob('./Sessions/*') vs actual 'sessions/' directory → no-op on case-sensitive FS +- No auth/CSRF: bare $_GET['allcollages'] == 'clear' triggers destructive unlink/rmdir +- If web-accessible it's an unauthenticated destructive endpoint +- Misplaced in cli_scripts/ (uses $_GET like a web script) +Fix: correct path, add CLI guard (php_sapi_name()), gate destructive branch behind confirmation. diff --git a/kanban/tasks/017-make-session-folder-names-unique-stop-suppressing.md b/kanban/tasks/017-make-session-folder-names-unique-stop-suppressing.md new file mode 100644 index 0000000..746f354 --- /dev/null +++ b/kanban/tasks/017-make-session-folder-names-unique-stop-suppressing.md @@ -0,0 +1,17 @@ +--- +id: 17 +title: Make session folder names unique, stop suppressing mkdir errors +status: done +priority: medium +created: 2026-07-17T03:57:50.884126062+02:00 +updated: 2026-07-19T19:06:58.837663313+02:00 +started: 2026-07-19T19:06:58.839287746+02:00 +completed: 2026-07-19T19:06:58.839287746+02:00 +tags: + - session +claimed_by: rain-raven +claimed_at: 2026-07-19T19:06:58.837663313+02:00 +class: standard +--- + +From framework review (docs/framework_review.md), P1-5. ZAP_MOMENT = date('Y-m-d-H_i_s') has second resolution. Two submissions in the same second collide on mkdir — suppressed with @, returns null folder, silently breaks all downstream operations. Fix: append random suffix to folder name, drop @, add recursive flag. diff --git a/kanban/tasks/018-introduce-zaphandler-interface-for-getcontent.md b/kanban/tasks/018-introduce-zaphandler-interface-for-getcontent.md new file mode 100644 index 0000000..16cde7e --- /dev/null +++ b/kanban/tasks/018-introduce-zaphandler-interface-for-getcontent.md @@ -0,0 +1,18 @@ +--- +id: 18 +title: Introduce ZAPHandler interface for getContent() +status: done +priority: medium +created: 2026-07-17T03:57:59.054256365+02:00 +updated: 2026-07-18T11:51:03.280085523+02:00 +started: 2026-07-18T11:51:03.281402582+02:00 +completed: 2026-07-18T11:51:03.281402582+02:00 +tags: + - framework +class: standard +--- + +From framework review (docs/framework_review.md), P1-7. Currently getContent() is convention-only — no interface enforces it. A handler missing the method fails at runtime, not definition time. Fix: create a ZAPHandler (or ModeHandler) interface with public function getContent(), implement it on all existing handlers, add instanceof check in ZAPController. + +[[2026-07-18]] Sat 11:50 +Created ZAPHandler interface with getContent(), implemented on ZAPHome/ZAPDisplay/ZAPZap, added instanceof check in ZAPController::fetch(). diff --git a/kanban/tasks/019-single-source-of-truth-for-word-count-cleanup.md b/kanban/tasks/019-single-source-of-truth-for-word-count-cleanup.md new file mode 100644 index 0000000..5d4982b --- /dev/null +++ b/kanban/tasks/019-single-source-of-truth-for-word-count-cleanup.md @@ -0,0 +1,13 @@ +--- +id: 19 +title: Single source of truth for word count + cleanup handleWords +status: todo +priority: low +created: 2026-07-17T03:58:05.365933671+02:00 +updated: 2026-07-17T03:58:05.365933671+02:00 +tags: + - cleanup +class: standard +--- + +From framework review (docs/framework_review.md), P2-1/2. MAX_WORDS drives the template loop but handleWords() independently re-scans POST. Also has / typo. Fix: iterate 1..MAX_WORDS in handleWords, clean up variable naming. diff --git a/kanban/tasks/020-consistent-template-rendering-pattern-across-all.md b/kanban/tasks/020-consistent-template-rendering-pattern-across-all.md new file mode 100644 index 0000000..0d22f76 --- /dev/null +++ b/kanban/tasks/020-consistent-template-rendering-pattern-across-all.md @@ -0,0 +1,13 @@ +--- +id: 20 +title: Consistent template rendering pattern across all handlers +status: todo +priority: low +created: 2026-07-17T03:58:11.987251314+02:00 +updated: 2026-07-17T03:58:11.987251314+02:00 +tags: + - cleanup +class: standard +--- + +From framework review (docs/framework_review.md), P2-3. ZAPHome uses extract(get_object_vars()) (the documented pattern); ZAPDisplay does not — it manually declares local vars and accesses directly in the template. Pick one pattern (extract or explicit) and apply consistently. diff --git a/kanban/tasks/021-consolidate-duplicated-screen-size-constants.md b/kanban/tasks/021-consolidate-duplicated-screen-size-constants.md new file mode 100644 index 0000000..91ccbdd --- /dev/null +++ b/kanban/tasks/021-consolidate-duplicated-screen-size-constants.md @@ -0,0 +1,17 @@ +--- +id: 21 +title: Consolidate duplicated screen-size constants +status: done +priority: low +created: 2026-07-17T03:58:20.43055577+02:00 +updated: 2026-07-19T21:49:54.332882291+02:00 +started: 2026-07-19T21:49:54.33435439+02:00 +completed: 2026-07-19T21:49:54.33435439+02:00 +tags: + - cleanup +claimed_by: rain-raven +claimed_at: 2026-07-19T21:49:54.332882291+02:00 +class: standard +--- + +From framework review (docs/framework_review.md), P2-5. ImgIOTools and ImgTools both define $maxScreenWidth/$maxScreenHeight to the same values (1067/600). Drift risk. Consolidate into one place. diff --git a/kanban/tasks/022-fix-convertgifstopng-extension-rename-fix-log-xml.md b/kanban/tasks/022-fix-convertgifstopng-extension-rename-fix-log-xml.md new file mode 100644 index 0000000..a0bcc07 --- /dev/null +++ b/kanban/tasks/022-fix-convertgifstopng-extension-rename-fix-log-xml.md @@ -0,0 +1,19 @@ +--- +id: 22 +title: Fix convertGifsToPng extension + rename/fix log.xml +status: done +priority: low +created: 2026-07-17T03:58:30.403935395+02:00 +updated: 2026-07-19T21:42:54.771387687+02:00 +started: 2026-07-19T21:42:54.772886896+02:00 +completed: 2026-07-19T21:42:54.772886896+02:00 +tags: + - cleanup +claimed_by: rain-raven +claimed_at: 2026-07-19T21:42:54.771387687+02:00 +class: standard +--- + +From framework review (docs/framework_review.md), P2-6/7. Two small fixes: +1. convertGifsToPng produces cat.gif.png (appends .png instead of replacing .gif) — confusing filenames. +2. Log writes HTML fragments (
) into log.xml — not valid XML. Rename to log.html or emit proper XML. diff --git a/kanban/tasks/023-anchor-zap-app-base-dir-to-dir-make-getzaptemplate.md b/kanban/tasks/023-anchor-zap-app-base-dir-to-dir-make-getzaptemplate.md new file mode 100644 index 0000000..8bb0a12 --- /dev/null +++ b/kanban/tasks/023-anchor-zap-app-base-dir-to-dir-make-getzaptemplate.md @@ -0,0 +1,19 @@ +--- +id: 23 +title: Anchor ZAP_APP_BASE_DIR to __DIR__, make getZAPTemplate throw +status: done +priority: low +created: 2026-07-17T03:58:37.157613187+02:00 +updated: 2026-07-19T21:39:02.781954542+02:00 +started: 2026-07-19T21:39:02.783424942+02:00 +completed: 2026-07-19T21:39:02.783424942+02:00 +tags: + - cleanup +claimed_by: rain-raven +claimed_at: 2026-07-19T21:39:02.781954542+02:00 +class: standard +--- + +From framework review (docs/framework_review.md), P2-8/9. Two fixes in conf/init.php: +1. ZAP_APP_BASE_DIR = realpath('.'.DIRECTORY_SEPARATOR.'..') is cwd-dependent — breaks if entry point is not www/ or cli_scripts/. Use realpath(__DIR__ . '/..') instead. +2. getZAPTemplate returns ERROR_PREFIX . 'No template' string on missing file instead of throwing — inconsistent with other error paths. Make it throw. diff --git a/kanban/tasks/024-refresh-readme-yahoo-brave-drop-outdated-roadmap.md b/kanban/tasks/024-refresh-readme-yahoo-brave-drop-outdated-roadmap.md new file mode 100644 index 0000000..2f8f118 --- /dev/null +++ b/kanban/tasks/024-refresh-readme-yahoo-brave-drop-outdated-roadmap.md @@ -0,0 +1,17 @@ +--- +id: 24 +title: Refresh README (Yahoo→Brave, drop outdated roadmap, fix tone) +status: done +priority: low +created: 2026-07-17T03:58:43.662424712+02:00 +updated: 2026-07-19T21:31:14.516637296+02:00 +started: 2026-07-19T21:31:14.51815456+02:00 +completed: 2026-07-19T21:31:14.51815456+02:00 +tags: + - docs +claimed_by: rain-raven +claimed_at: 2026-07-19T21:31:14.516637296+02:00 +class: standard +--- + +From framework review (docs/framework_review.md), P2-11. README.md still says 'the most relevant image that search engine Yahoo comes up with', tells Windows users 'Windows suck', and lists a roadmap that doesn't map to kanban. Refresh prose to match current state (Brave search, no Yahoo). From 3edb156749844de83627711f22c95d2b8b232f4b Mon Sep 17 00:00:00 2001 From: Fabian de Boer Date: Mon, 20 Jul 2026 01:14:00 +0200 Subject: [PATCH 09/10] clean up code in ZapHome and ZapDisplay (#19 and #20) --- kanban/activity.jsonl | 6 ++++++ ...ingle-source-of-truth-for-word-count-cleanup.md | 8 ++++++-- ...istent-template-rendering-pattern-across-all.md | 8 ++++++-- lib/ZAPDisplay.class.php | 14 +++++--------- lib/ZAPHome.class.php | 9 +-------- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/kanban/activity.jsonl b/kanban/activity.jsonl index 1453eab..0aab3a4 100644 --- a/kanban/activity.jsonl +++ b/kanban/activity.jsonl @@ -85,3 +85,9 @@ {"timestamp":"2026-07-19T21:49:54.312195218+02:00","action":"edit","task_id":21,"detail":"Consolidate duplicated screen-size constants"} {"timestamp":"2026-07-19T21:49:54.312297605+02:00","action":"claim","task_id":21,"detail":"rain-raven"} {"timestamp":"2026-07-19T21:49:54.334491216+02:00","action":"move","task_id":21,"detail":"todo -\u003e done"} +{"timestamp":"2026-07-19T22:54:27.983080764+02:00","action":"edit","task_id":20,"detail":"Consistent template rendering pattern across all handlers"} +{"timestamp":"2026-07-19T22:54:27.98319541+02:00","action":"claim","task_id":20,"detail":"rain-raven"} +{"timestamp":"2026-07-19T22:54:28.007952955+02:00","action":"move","task_id":20,"detail":"todo -\u003e done"} +{"timestamp":"2026-07-20T01:09:23.947300407+02:00","action":"edit","task_id":19,"detail":"Single source of truth for word count + cleanup handleWords"} +{"timestamp":"2026-07-20T01:09:23.947430566+02:00","action":"claim","task_id":19,"detail":"rain-raven"} +{"timestamp":"2026-07-20T01:09:23.97076796+02:00","action":"move","task_id":19,"detail":"todo -\u003e done"} diff --git a/kanban/tasks/019-single-source-of-truth-for-word-count-cleanup.md b/kanban/tasks/019-single-source-of-truth-for-word-count-cleanup.md index 5d4982b..c0685a6 100644 --- a/kanban/tasks/019-single-source-of-truth-for-word-count-cleanup.md +++ b/kanban/tasks/019-single-source-of-truth-for-word-count-cleanup.md @@ -1,12 +1,16 @@ --- id: 19 title: Single source of truth for word count + cleanup handleWords -status: todo +status: done priority: low created: 2026-07-17T03:58:05.365933671+02:00 -updated: 2026-07-17T03:58:05.365933671+02:00 +updated: 2026-07-20T01:09:23.968637294+02:00 +started: 2026-07-20T01:09:23.970603129+02:00 +completed: 2026-07-20T01:09:23.970603129+02:00 tags: - cleanup +claimed_by: rain-raven +claimed_at: 2026-07-20T01:09:23.968637294+02:00 class: standard --- diff --git a/kanban/tasks/020-consistent-template-rendering-pattern-across-all.md b/kanban/tasks/020-consistent-template-rendering-pattern-across-all.md index 0d22f76..9dd7b1d 100644 --- a/kanban/tasks/020-consistent-template-rendering-pattern-across-all.md +++ b/kanban/tasks/020-consistent-template-rendering-pattern-across-all.md @@ -1,12 +1,16 @@ --- id: 20 title: Consistent template rendering pattern across all handlers -status: todo +status: done priority: low created: 2026-07-17T03:58:11.987251314+02:00 -updated: 2026-07-17T03:58:11.987251314+02:00 +updated: 2026-07-19T22:54:28.006158702+02:00 +started: 2026-07-19T22:54:28.007766996+02:00 +completed: 2026-07-19T22:54:28.007766996+02:00 tags: - cleanup +claimed_by: rain-raven +claimed_at: 2026-07-19T22:54:28.006158702+02:00 class: standard --- diff --git a/lib/ZAPDisplay.class.php b/lib/ZAPDisplay.class.php index 65656e9..3be56b0 100755 --- a/lib/ZAPDisplay.class.php +++ b/lib/ZAPDisplay.class.php @@ -80,15 +80,11 @@ class ZAPDisplay implements ZAPHandler { */ private function handleWords() { $words = array(); - $ix = 1; - $postix = sprintf("%02d", $ix); - $wd = 'word' . $postix; - - while(!empty($_POST[$wd])) { - array_push($words, $_POST[$wd]); - $ix++; - $postfix = sprintf("%02d", $ix); - $wd = 'word' . $postfix; + for ($i = 1; $i <= MAX_WORDS; $i++) { + $key = 'word' . sprintf("%02d", $i); + if (!empty($_POST[$key])) { + $words[] = $_POST[$key]; + } } return $words; } diff --git a/lib/ZAPHome.class.php b/lib/ZAPHome.class.php index 21de450..207b0f8 100755 --- a/lib/ZAPHome.class.php +++ b/lib/ZAPHome.class.php @@ -8,11 +8,6 @@ class ZAPHome implements ZAPHandler { */ private $template = 'index'; - /** - * ZAP version - */ - private $ver; - /** * Parsable content */ @@ -22,10 +17,8 @@ class ZAPHome implements ZAPHandler { * Constructor */ public function __construct() { - $this->ver = ZAP_VERSION; - extract(get_object_vars($this)); - ob_start(); + $ver = ZAP_VERSION; include getZAPTemplate($this->template); $this->tpl .= ob_get_clean(); } From 42cfff87d030189e45569973d2bf14fe2c09bd24 Mon Sep 17 00:00:00 2001 From: Fabian de Boer Date: Mon, 20 Jul 2026 14:35:35 +0200 Subject: [PATCH 10/10] optimize framework: ready to merge and release 0.9.0 --- .gitignore | 1 - docs/framework_review.md | 39 +++++++++++++++++++ kanban/activity.jsonl | 2 + ...request-building-lazy-url-single-encode.md | 6 ++- ...utoloader-use-path-separator-instead-of.md | 6 ++- 5 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 docs/framework_review.md diff --git a/.gitignore b/.gitignore index b9c4024..532dbc8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ sessions/* conf/conf.local.php ignore .pi-tasks -docs/framework_review.md diff --git a/docs/framework_review.md b/docs/framework_review.md new file mode 100644 index 0000000..2865e02 --- /dev/null +++ b/docs/framework_review.md @@ -0,0 +1,39 @@ +# ZAP Framework Review + +Task #10 — *“Check if the framework is the optimal, most simple and flexible way to go. If not: suggest some changes.”* + +This review audits the internal ZapMachine PHP framework (documented in `docs/framework_guide.md`) against three criteria — **optimal**, **simple**, and **flexible** — based on a full read of the current source on branch `optimize-framework` (commit `d971512`). + +**Scope:** audit + recommendations only. **No source files are modified in this deliverable.** Every recommendation below is a proposal that should get its own kanban task and separate approval before implementation. + +--- + +## Verdict + +The framework’s **architecture is genuinely simple and flexible** for its scope. Adding a new page (`?mode=`) is a one-class + one-template operation with zero registration. Adding a new search engine is a one-class operation behind a clean interface. PHP-native templating means no build step and no engine dependency. These are real strengths and should be preserved. + +It is **not optimal**, however, because: + +1. **Three P0 correctness bugs** silently degrade the core search pipeline (Brave `size`/`offset` params never sent; query double-URL-encoded; autoloader path split hardcodes `':'`). +2. **Unsafe / committed config defaults** (`ZAP_DEBUG=true`, placeholder API key in git-tracked `conf/conf.php`, `chmod 777`). +3. **Several loose abstraction boundaries** make the “framework contract” described in the guide looser than the code actually enforces (no `ZAPHandler` interface, inconsistent `extract()` template pattern, raw `$_SESSION` access beside a `Session` wrapper). + +None of these require replacing the architecture. They are bug fixes, a config-hygiene pass, and a few small interface tightenings. Recommended effort: a handful of small, well-scoped tasks — **not** a rewrite. + +--- + + +## Strengths worth preserving + +| Aspect | Why it’s good | +|---|---| +| Front-controller + naming convention (`ZAP` + ucfirst(mode)) | Zero-config routing. A new mode = one class in `lib/` + one `.tpl`. No route table, no registration. Very flexible, very simple. (`lib/ZAPController.class.php`) | +| `spl_autoload_register` over the include path | No manual `require_once` for library classes; drop a `.class.php` in the right dir and it loads. (`conf/init.php`) | +| `Search` interface plugin system | Engines are interchangeable behind a 3-method contract and a universal result-array shape. Adding `Pixabay`/`Pexels` (tasks #1/#2) is a clean drop-in. (`resource/Search.interface.php`, `resource/Brave.class.php`) | +| PHP-native templates (`ob_start`/`include`/`extract`/`ob_get_clean`) | No template engine, no compilation step, no extra dependency. Simplest possible rendering. | +| `Config` singleton | Decouples runtime config (e.g. registered engines) from individual classes. (`conf/Config.class.php`) | +| Clear layering | routing / handlers / search / imaging / utils are in separate dirs with single responsibilities. | + +These answer the “simple and flexible” part of the brief affirmatively. The rest of this document is about getting to “optimal.” + +--- diff --git a/kanban/activity.jsonl b/kanban/activity.jsonl index 0aab3a4..3352de4 100644 --- a/kanban/activity.jsonl +++ b/kanban/activity.jsonl @@ -91,3 +91,5 @@ {"timestamp":"2026-07-20T01:09:23.947300407+02:00","action":"edit","task_id":19,"detail":"Single source of truth for word count + cleanup handleWords"} {"timestamp":"2026-07-20T01:09:23.947430566+02:00","action":"claim","task_id":19,"detail":"rain-raven"} {"timestamp":"2026-07-20T01:09:23.97076796+02:00","action":"move","task_id":19,"detail":"todo -\u003e done"} +{"timestamp":"2026-07-20T14:15:55.814485137+02:00","action":"move","task_id":12,"detail":"review -\u003e done"} +{"timestamp":"2026-07-20T14:15:59.830512648+02:00","action":"move","task_id":11,"detail":"review -\u003e done"} diff --git a/kanban/tasks/011-fix-brave-request-building-lazy-url-single-encode.md b/kanban/tasks/011-fix-brave-request-building-lazy-url-single-encode.md index 5f2c2cc..88f61cc 100644 --- a/kanban/tasks/011-fix-brave-request-building-lazy-url-single-encode.md +++ b/kanban/tasks/011-fix-brave-request-building-lazy-url-single-encode.md @@ -1,10 +1,12 @@ --- id: 11 title: 'Fix Brave request building: lazy URL + single encode' -status: review +status: done priority: high created: 2026-07-17T03:57:00.444816503+02:00 -updated: 2026-07-17T12:26:38.759875442+02:00 +updated: 2026-07-20T14:15:59.825243496+02:00 +started: 2026-07-20T14:15:59.830054903+02:00 +completed: 2026-07-20T14:15:59.830054903+02:00 tags: - search - bug diff --git a/kanban/tasks/012-fix-autoloader-use-path-separator-instead-of.md b/kanban/tasks/012-fix-autoloader-use-path-separator-instead-of.md index 211a40d..54fd574 100644 --- a/kanban/tasks/012-fix-autoloader-use-path-separator-instead-of.md +++ b/kanban/tasks/012-fix-autoloader-use-path-separator-instead-of.md @@ -1,10 +1,12 @@ --- id: 12 title: 'Fix autoloader: use PATH_SEPARATOR instead of hardcoded '':''' -status: review +status: done priority: high created: 2026-07-17T03:57:06.82901804+02:00 -updated: 2026-07-17T12:27:53.056263524+02:00 +updated: 2026-07-20T14:15:55.809367918+02:00 +started: 2026-07-20T14:15:55.814059562+02:00 +completed: 2026-07-20T14:15:55.814059562+02:00 tags: - framework - bug