Compare commits
10 commits
d971512a31
...
42cfff87d0
| Author | SHA1 | Date | |
|---|---|---|---|
| 42cfff87d0 | |||
| 3edb156749 | |||
| ffab9dc4da | |||
| e96b8e5563 | |||
| 9196bf07a7 | |||
| ab0ac45415 | |||
| 186d770741 | |||
| b276ac7f6a | |||
| d185a26f87 | |||
| 6a122e3bed |
49 changed files with 818 additions and 85 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
sessions/*
|
sessions/*
|
||||||
|
conf/conf.local.php
|
||||||
ignore
|
ignore
|
||||||
kanban/
|
|
||||||
.pi-tasks
|
.pi-tasks
|
||||||
|
|
|
||||||
19
README.md
19
README.md
|
|
@ -10,25 +10,34 @@ 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.
|
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
|
## 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:
|
ApFab Zap Machine needs some PHP extensions:
|
||||||
- JSON (default installed)
|
- JSON (default installed)
|
||||||
- CUrl (default installed)
|
- CUrl (default installed)
|
||||||
- Imagick (Image Magick image processing Class)
|
- 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/
|
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
|
# yet to come
|
||||||
- Logfile also generated in imgProcess Class (0.9.2)
|
- Logfile also generated in imgProcess Class (0.9.2)
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,80 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
<?php
|
<?php
|
||||||
session_start();
|
/**
|
||||||
|
* ZapMachine Reset Script
|
||||||
|
*
|
||||||
|
* CLI only. Resets the current session and optionally purges all session
|
||||||
|
* folders. Confirmation is required before any destructive action.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* php cli_scripts/reset.php Reset current session only
|
||||||
|
* php cli_scripts/reset.php --all Purge all session folders (with prompt)
|
||||||
|
* php cli_scripts/reset.php --all -f Purge all session folders, no prompt
|
||||||
|
*/
|
||||||
|
|
||||||
unset($_SESSION['imgOriginal']);
|
if (php_sapi_name() !== 'cli') {
|
||||||
unset($_SESSION['words']);
|
die('This script can only be run from the command line.' . PHP_EOL);
|
||||||
unset($_SESSION['folder']);
|
|
||||||
unset($_SESSION['collages']);
|
|
||||||
unset($_SESSION['width']);
|
|
||||||
unset($_SESSION['height']);
|
|
||||||
|
|
||||||
echo '<p>session reset</p><p>';
|
|
||||||
|
|
||||||
if ($_GET['allcollages'] == 'clear') {
|
|
||||||
$sessions = glob('./Sessions/*');
|
|
||||||
foreach($sessions as $folder){
|
|
||||||
$files = glob($folder.'/*');
|
|
||||||
foreach($files as $fileToDelete){
|
|
||||||
echo "$fileToDelete deleted<br />";
|
|
||||||
unlink($fileToDelete);
|
|
||||||
}
|
|
||||||
echo "$folder deleted<br />";
|
|
||||||
rmdir($folder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
echo '</p>';
|
|
||||||
|
|
||||||
session_destroy();
|
// 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);
|
||||||
|
|
@ -85,9 +85,10 @@ function testImgSearch() {
|
||||||
return;
|
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)) {
|
if (!is_dir($folder)) {
|
||||||
@mkdir($folder, 0777, true);
|
mkdir($folder, 0777, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "\nQuery: '$word'\n\n";
|
echo "\nQuery: '$word'\n\n";
|
||||||
|
|
|
||||||
10
conf/conf.local.php.dist
Normal file
10
conf/conf.local.php.dist
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Local configuration overrides — gitignored.
|
||||||
|
*
|
||||||
|
* Copy this file to conf/conf.local.php (without the .dist suffix)
|
||||||
|
* and fill in your real API keys.
|
||||||
|
*
|
||||||
|
* Priority: conf.local.php > environment variable > placeholder
|
||||||
|
*/
|
||||||
|
define('BRAVE_API_KEY', 'your-real-api-key-here');
|
||||||
|
|
@ -6,6 +6,9 @@
|
||||||
define('MAX_WORDS', 4);
|
define('MAX_WORDS', 4);
|
||||||
define('MAX_ZAPS', 9);
|
define('MAX_ZAPS', 9);
|
||||||
|
|
||||||
|
define('ZAP_DEFAULT_WIDTH', 1067);
|
||||||
|
define('ZAP_DEFAULT_HEIGHT', 600);
|
||||||
|
|
||||||
define('ZAP_CURL_TIMEOUT', 12);
|
define('ZAP_CURL_TIMEOUT', 12);
|
||||||
define('ZAP_IMG_TYPE', 'jpg');
|
define('ZAP_IMG_TYPE', 'jpg');
|
||||||
define('ZAP_CANVAS_NAME', 'collage');
|
define('ZAP_CANVAS_NAME', 'collage');
|
||||||
|
|
@ -13,12 +16,13 @@ define('ZAP_CANVAS_NAME', 'collage');
|
||||||
define('ZAP_DELETE_SOURCE', false);
|
define('ZAP_DELETE_SOURCE', false);
|
||||||
define('ZAP_ADULT', false);
|
define('ZAP_ADULT', false);
|
||||||
define('ZAP_LOG', true);
|
define('ZAP_LOG', true);
|
||||||
define('ZAP_DEBUG', true);
|
define('ZAP_DEBUG', false);
|
||||||
|
|
||||||
|
|
||||||
// Brave Search API
|
// Brave Search API
|
||||||
// Get your API key: https://api.search.brave.com/
|
// 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');
|
define('BRAVE_BASE', 'https://api.search.brave.com/res/v1/images/search');
|
||||||
|
|
||||||
$search_engines = array('brave');
|
$search_engines = array('brave');
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,21 @@
|
||||||
<?php
|
<?php
|
||||||
include_once 'conf.php';
|
include_once 'conf.php';
|
||||||
|
|
||||||
|
// Load gitignored local overrides (API keys, debug toggle) if present
|
||||||
|
$localConf = __DIR__ . '/conf.local.php';
|
||||||
|
if (file_exists($localConf)) {
|
||||||
|
require_once $localConf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: try environment variable if still not defined
|
||||||
|
if (!defined('BRAVE_API_KEY')) {
|
||||||
|
define('BRAVE_API_KEY', getenv('BRAVE_API_KEY') ?: 'your api key');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define some app-wide constants
|
* 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_WEB_DIR', 'www');
|
||||||
define('ZAP_RESOURCE_DIR', 'resource');
|
define('ZAP_RESOURCE_DIR', 'resource');
|
||||||
define('ZAP_UTILS_DIR', 'utils');
|
define('ZAP_UTILS_DIR', 'utils');
|
||||||
|
|
@ -52,7 +63,7 @@ spl_autoload_register(function ($class_name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$include_path = get_include_path();
|
$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) {
|
foreach ($include_path_tokens as $prefix) {
|
||||||
$path[0] = $prefix . DIRECTORY_SEPARATOR . $class_name . '.interface.php';
|
$path[0] = $prefix . DIRECTORY_SEPARATOR . $class_name . '.interface.php';
|
||||||
|
|
@ -93,7 +104,7 @@ function getZAPTemplate($tplname) {
|
||||||
if (file_exists($tplfilepath)) {
|
if (file_exists($tplfilepath)) {
|
||||||
return $tplfilepath;
|
return $tplfilepath;
|
||||||
} else {
|
} else {
|
||||||
return ERROR_PREFIX . 'No template';
|
throw new Exception(ERROR_PREFIX . 'No template');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
16
docs/ZapHandler.md
Normal file
16
docs/ZapHandler.md
Normal file
|
|
@ -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`)
|
||||||
|
|
@ -212,7 +212,7 @@ Key differences from the web entry point:
|
||||||
|
|
||||||
### Logging
|
### 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
|
```php
|
||||||
if (ZAP_LOG == true) {
|
if (ZAP_LOG == true) {
|
||||||
|
|
|
||||||
39
docs/framework_review.md
Normal file
39
docs/framework_review.md
Normal file
|
|
@ -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.”
|
||||||
|
|
||||||
|
---
|
||||||
0
kanban/.lock
Normal file
0
kanban/.lock
Normal file
95
kanban/activity.jsonl
Normal file
95
kanban/activity.jsonl
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
{"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"}
|
||||||
|
{"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"}
|
||||||
|
{"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"}
|
||||||
47
kanban/config.yml
Normal file
47
kanban/config.yml
Normal file
|
|
@ -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
|
||||||
13
kanban/tasks/001-pixabay-api.md
Normal file
13
kanban/tasks/001-pixabay-api.md
Normal file
|
|
@ -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
|
||||||
13
kanban/tasks/002-pexels-api.md
Normal file
13
kanban/tasks/002-pexels-api.md
Normal file
|
|
@ -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
|
||||||
13
kanban/tasks/003-random-words.md
Normal file
13
kanban/tasks/003-random-words.md
Normal file
|
|
@ -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/)
|
||||||
13
kanban/tasks/004-search-for-original-images.md
Normal file
13
kanban/tasks/004-search-for-original-images.md
Normal file
|
|
@ -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'
|
||||||
14
kanban/tasks/005-image-processing.md
Normal file
14
kanban/tasks/005-image-processing.md
Normal file
|
|
@ -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
|
||||||
14
kanban/tasks/006-full-screen-gui.md
Normal file
14
kanban/tasks/006-full-screen-gui.md
Normal file
|
|
@ -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
|
||||||
14
kanban/tasks/007-ai-generated-images-for-input.md
Normal file
14
kanban/tasks/007-ai-generated-images-for-input.md
Normal file
|
|
@ -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
|
||||||
14
kanban/tasks/008-automatic-mode.md
Normal file
14
kanban/tasks/008-automatic-mode.md
Normal file
|
|
@ -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
|
||||||
13
kanban/tasks/009-framework-guide.md
Normal file
13
kanban/tasks/009-framework-guide.md
Normal file
|
|
@ -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
|
||||||
16
kanban/tasks/010-check-framework.md
Normal file
16
kanban/tasks/010-check-framework.md
Normal file
|
|
@ -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.
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
id: 11
|
||||||
|
title: 'Fix Brave request building: lazy URL + single encode'
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
created: 2026-07-17T03:57:00.444816503+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
|
||||||
|
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.
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
id: 12
|
||||||
|
title: 'Fix autoloader: use PATH_SEPARATOR instead of hardcoded '':'''
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
created: 2026-07-17T03:57:06.82901804+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
|
||||||
|
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.
|
||||||
|
|
@ -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)
|
||||||
17
kanban/tasks/014-add-csrf-protection-to-word-input-form.md
Normal file
17
kanban/tasks/014-add-csrf-protection-to-word-input-form.md
Normal file
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
23
kanban/tasks/016-fix-and-secure-cli-scripts-reset-php.md
Normal file
23
kanban/tasks/016-fix-and-secure-cli-scripts-reset-php.md
Normal file
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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().
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
id: 19
|
||||||
|
title: Single source of truth for word count + cleanup handleWords
|
||||||
|
status: done
|
||||||
|
priority: low
|
||||||
|
created: 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
|
||||||
|
---
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
id: 20
|
||||||
|
title: Consistent template rendering pattern across all handlers
|
||||||
|
status: done
|
||||||
|
priority: low
|
||||||
|
created: 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
|
||||||
|
---
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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 (<div class="type">) into log.xml — not valid XML. Rename to log.html or emit proper XML.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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).
|
||||||
|
|
@ -3,9 +3,6 @@
|
||||||
* Image Input Output Tools
|
* Image Input Output Tools
|
||||||
*/
|
*/
|
||||||
class ImgIOTools {
|
class ImgIOTools {
|
||||||
public static $maxScreenWidth = 1067; //screen dimensions
|
|
||||||
|
|
||||||
public static $maxScreenHeight = 600;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flushes image to screen
|
* Flushes image to screen
|
||||||
|
|
@ -13,9 +10,9 @@ class ImgIOTools {
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function onScreen(Imagick $img) {
|
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);
|
header('Content-type: image/' . ZAP_IMG_TYPE);
|
||||||
echo $img->getImageBlob();
|
echo $img->getImageBlob();
|
||||||
}
|
}
|
||||||
|
|
@ -47,4 +44,3 @@ class ImgIOTools {
|
||||||
return $img->writeImage($path);
|
return $img->writeImage($path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
|
|
@ -62,7 +62,7 @@ class ImgSearch {
|
||||||
public function getImageData() {
|
public function getImageData() {
|
||||||
$clazz = ucfirst($this->engine);
|
$clazz = ucfirst($this->engine);
|
||||||
$search = new $clazz();
|
$search = new $clazz();
|
||||||
$search->setQuery(urlencode($this->word));
|
$search->setQuery($this->word);
|
||||||
$search->setParam('start', $this->hitPosition);
|
$search->setParam('start', $this->hitPosition);
|
||||||
$search->setParam('size', $this->numberResults);
|
$search->setParam('size', $this->numberResults);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,6 @@
|
||||||
* Library of tool functions to manipulate an image
|
* Library of tool functions to manipulate an image
|
||||||
*/
|
*/
|
||||||
class ImgTools {
|
class ImgTools {
|
||||||
public static $maxScreenWidth = 1067; //screen dimensions
|
|
||||||
|
|
||||||
public static $maxScreenHeight = 600;
|
|
||||||
|
|
||||||
public static $minFuzzFactor = 0.08; //minimal factor for $fuzz
|
public static $minFuzzFactor = 0.08; //minimal factor for $fuzz
|
||||||
|
|
||||||
public static $maxFuzzFactor = 0.64; //maximal factor for $fuzz
|
public static $maxFuzzFactor = 0.64; //maximal factor for $fuzz
|
||||||
|
|
@ -112,10 +108,8 @@ class ImgTools {
|
||||||
$mimeType = $imgInfo['mime'];
|
$mimeType = $imgInfo['mime'];
|
||||||
//find gif
|
//find gif
|
||||||
if ($mimeType == "image/gif") {
|
if ($mimeType == "image/gif") {
|
||||||
$imagetype = 'png';
|
// Replace .gif extension with .png
|
||||||
//full image name
|
$newFilename = preg_replace('/\.gif$/i', '.png', $filename);
|
||||||
$Dot = '.';
|
|
||||||
$newFilename = "$filename$Dot$imagetype";
|
|
||||||
//convert
|
//convert
|
||||||
$img = new Imagick($filename);
|
$img = new Imagick($filename);
|
||||||
|
|
||||||
|
|
@ -163,7 +157,7 @@ class ImgTools {
|
||||||
public static function scaleByLength(Imagick $img, $maxW, $maxH) {
|
public static function scaleByLength(Imagick $img, $maxW, $maxH) {
|
||||||
$width = $img->getImageWidth();
|
$width = $img->getImageWidth();
|
||||||
$height = $img->getImageHeight();
|
$height = $img->getImageHeight();
|
||||||
if(($width <= self::$maxScreenWidth) && ($height <= self::$maxScreenHeight)) {
|
if(($width <= ZAP_DEFAULT_WIDTH) && ($height <= ZAP_DEFAULT_HEIGHT)) {
|
||||||
return $img;
|
return $img;
|
||||||
} else {
|
} else {
|
||||||
if ($width >= $height) { //horizontal image
|
if ($width >= $height) { //horizontal image
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ class ZAPController {
|
||||||
$this->mode = ucfirst($mode);
|
$this->mode = ucfirst($mode);
|
||||||
$clazz = $this->prefix . $this->mode;
|
$clazz = $this->prefix . $this->mode;
|
||||||
if (!class_exists($clazz)) {
|
if (!class_exists($clazz)) {
|
||||||
die('Wrong parameter');
|
http_response_code(404);
|
||||||
|
include getZAPTemplate('404');
|
||||||
|
die();
|
||||||
}
|
}
|
||||||
$this->action = new $clazz();
|
$this->action = new $clazz();
|
||||||
}
|
}
|
||||||
|
|
@ -35,6 +37,9 @@ class ZAPController {
|
||||||
* @return string content
|
* @return string content
|
||||||
*/
|
*/
|
||||||
public function fetch() {
|
public function fetch() {
|
||||||
|
if (!($this->action instanceof ZAPHandler)) {
|
||||||
|
die('Handler does not implement ZAPHandler interface');
|
||||||
|
}
|
||||||
return $this->action->getContent();
|
return $this->action->getContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* Class is responsible for starting up ZAP machine
|
* Class is responsible for starting up ZAP machine
|
||||||
*/
|
*/
|
||||||
class ZAPDisplay {
|
class ZAPDisplay implements ZAPHandler {
|
||||||
/**
|
/**
|
||||||
* Template file
|
* Template file
|
||||||
*/
|
*/
|
||||||
|
|
@ -28,6 +28,18 @@ class ZAPDisplay {
|
||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
ob_start();
|
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 '<p>Invalid or missing security token. Please go back and try again.</p>';
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
$this->folder = $this->createFolder();
|
$this->folder = $this->createFolder();
|
||||||
$this->words = $this->handleWords();
|
$this->words = $this->handleWords();
|
||||||
|
|
||||||
|
|
@ -49,9 +61,10 @@ class ZAPDisplay {
|
||||||
*/
|
*/
|
||||||
private function createFolder() {
|
private function createFolder() {
|
||||||
try {
|
try {
|
||||||
|
$suffix = bin2hex(random_bytes(4));
|
||||||
$folder = ZAP_APP_BASE_DIR . DIRECTORY_SEPARATOR
|
$folder = ZAP_APP_BASE_DIR . DIRECTORY_SEPARATOR
|
||||||
. ZAP_SESSIONS_DIR . DIRECTORY_SEPARATOR . 'zap_' . ZAP_MOMENT;
|
. ZAP_SESSIONS_DIR . DIRECTORY_SEPARATOR . 'zap_' . ZAP_MOMENT . '_' . $suffix;
|
||||||
if (!@mkdir($folder, 0777)) {
|
if (!mkdir($folder, 0777, true)) {
|
||||||
throw new Exception('<p>' . ERROR_PREFIX . 'Unable to create base dir: ' . $folder . '</p>');
|
throw new Exception('<p>' . ERROR_PREFIX . 'Unable to create base dir: ' . $folder . '</p>');
|
||||||
}
|
}
|
||||||
return $folder;
|
return $folder;
|
||||||
|
|
@ -67,15 +80,11 @@ class ZAPDisplay {
|
||||||
*/
|
*/
|
||||||
private function handleWords() {
|
private function handleWords() {
|
||||||
$words = array();
|
$words = array();
|
||||||
$ix = 1;
|
for ($i = 1; $i <= MAX_WORDS; $i++) {
|
||||||
$postix = sprintf("%02d", $ix);
|
$key = 'word' . sprintf("%02d", $i);
|
||||||
$wd = 'word' . $postix;
|
if (!empty($_POST[$key])) {
|
||||||
|
$words[] = $_POST[$key];
|
||||||
while(!empty($_POST[$wd])) {
|
}
|
||||||
array_push($words, $_POST[$wd]);
|
|
||||||
$ix++;
|
|
||||||
$postfix = sprintf("%02d", $ix);
|
|
||||||
$wd = 'word' . $postfix;
|
|
||||||
}
|
}
|
||||||
return $words;
|
return $words;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
lib/ZAPHandler.interface.php
Normal file
12
lib/ZAPHandler.interface.php
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Interface for ZAP mode handlers.
|
||||||
|
* All ZAP handler classes must implement this interface.
|
||||||
|
*/
|
||||||
|
interface ZAPHandler {
|
||||||
|
/**
|
||||||
|
* Renders and returns the content for this mode.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContent();
|
||||||
|
}
|
||||||
|
|
@ -2,17 +2,12 @@
|
||||||
/**
|
/**
|
||||||
* Index class
|
* Index class
|
||||||
*/
|
*/
|
||||||
class ZAPHome {
|
class ZAPHome implements ZAPHandler {
|
||||||
/**
|
/**
|
||||||
* Template file prefix
|
* Template file prefix
|
||||||
*/
|
*/
|
||||||
private $template = 'index';
|
private $template = 'index';
|
||||||
|
|
||||||
/**
|
|
||||||
* ZAP version
|
|
||||||
*/
|
|
||||||
private $ver;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parsable content
|
* Parsable content
|
||||||
*/
|
*/
|
||||||
|
|
@ -22,10 +17,8 @@ class ZAPHome {
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->ver = ZAP_VERSION;
|
|
||||||
extract(get_object_vars($this));
|
|
||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
|
$ver = ZAP_VERSION;
|
||||||
include getZAPTemplate($this->template);
|
include getZAPTemplate($this->template);
|
||||||
$this->tpl .= ob_get_clean();
|
$this->tpl .= ob_get_clean();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* ZAP factory
|
* ZAP factory
|
||||||
*/
|
*/
|
||||||
class ZAPZap {
|
class ZAPZap implements ZAPHandler {
|
||||||
private $tpl = '';
|
private $tpl = '';
|
||||||
private $folder;
|
private $folder;
|
||||||
private $height;
|
private $height;
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,20 @@ class Brave implements Search {
|
||||||
|
|
||||||
public function setQuery($word) {
|
public function setQuery($word) {
|
||||||
$this->word = $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)) {
|
if (isset($this->start)) {
|
||||||
$request .= '&offset=' . $this->start;
|
$request .= '&offset=' . $this->start;
|
||||||
}
|
}
|
||||||
|
|
@ -38,11 +50,6 @@ class Brave implements Search {
|
||||||
$this->$key = $value;
|
$this->$key = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getData() {
|
|
||||||
$return_array = $this->prepareData();
|
|
||||||
return $return_array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare a project wide universal array with results
|
* Prepare a project wide universal array with results
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
29
tpl/404.tpl
Normal file
29
tpl/404.tpl
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>404 - ZapMachine</title>
|
||||||
|
<link rel="stylesheet" media="screen" href="css/style.css">
|
||||||
|
<style>
|
||||||
|
.error-404 {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 50vw;
|
||||||
|
height: 50vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: Georgia, serif;
|
||||||
|
font-size: clamp(2rem, 8vw, 6rem);
|
||||||
|
color: #555;
|
||||||
|
border: 0px;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="error-404">error 404</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -12,7 +12,11 @@
|
||||||
<!--input modus: words-->
|
<!--input modus: words-->
|
||||||
<div id="inpWords" class="inpSection">
|
<div id="inpWords" class="inpSection">
|
||||||
<h2>Input words</h2>
|
<h2>Input words</h2>
|
||||||
|
<?php if (empty($_SESSION['csrf_token'])) {
|
||||||
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(16));
|
||||||
|
} ?>
|
||||||
<form id="form1" action="index.php?mode=display" method="post">
|
<form id="form1" action="index.php?mode=display" method="post">
|
||||||
|
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
||||||
<?php for ($i = 1; $i <= MAX_WORDS; $i++) : ?>
|
<?php for ($i = 1; $i <= MAX_WORDS; $i++) : ?>
|
||||||
<?php $nr = sprintf("%02d", $i); ?>
|
<?php $nr = sprintf("%02d", $i); ?>
|
||||||
<div class="inp">
|
<div class="inp">
|
||||||
|
|
@ -28,10 +32,10 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="inp">
|
<div class="inp">
|
||||||
width <input type="text" name="width" id="width" size="5" maxlength="4" value="1067"> px
|
width <input type="text" name="width" id="width" size="5" maxlength="4" value="<?= ZAP_DEFAULT_WIDTH ?>"> px
|
||||||
</div>
|
</div>
|
||||||
<div class="inp">
|
<div class="inp">
|
||||||
height <input type="text" name="height" id="height" size="5" maxlength="4" value="600"> px
|
height <input type="text" name="height" id="height" size="5" maxlength="4" value="<?= ZAP_DEFAULT_HEIGHT ?>"> px
|
||||||
</div>
|
</div>
|
||||||
<div class="inp">
|
<div class="inp">
|
||||||
<input type="button" class="btn" onclick="form1Submit()" value="Go & Generate Some Zaps">
|
<input type="button" class="btn" onclick="form1Submit()" value="Go & Generate Some Zaps">
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class Log {
|
||||||
$this->folder = $imgFolder;
|
$this->folder = $imgFolder;
|
||||||
|
|
||||||
$this->logFileName = $imgFolder . DIRECTORY_SEPARATOR . 'log.txt';
|
$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) {
|
public function addToLog($type, $head, $addString) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue