From 4f3557a8174ae7a492ca91f2de89b50813bc09f8 Mon Sep 17 00:00:00 2001 From: Victor Westerlund Date: Thu, 28 Nov 2024 22:03:52 +0100 Subject: [PATCH] wip: 2024-11-28T17:44:40+0100 (1732812280) --- api/.env.example.ini | 5 +- api/composer.lock | 20 +--- api/endpoints/notes/GET.php | 46 ++++++++ api/src/Endpoints.php | 2 + public/notes/blog.php | 12 +++ public/notes/index.php | 0 src/helpers/Notes.php | 209 ++++++++++++++++++++++++++++++++++++ 7 files changed, 278 insertions(+), 16 deletions(-) create mode 100644 api/endpoints/notes/GET.php create mode 100644 public/notes/blog.php create mode 100644 public/notes/index.php create mode 100644 src/helpers/Notes.php diff --git a/api/.env.example.ini b/api/.env.example.ini index 163a056..ed10a11 100755 --- a/api/.env.example.ini +++ b/api/.env.example.ini @@ -5,4 +5,7 @@ pass = "" [databases] vlw = "" -battlestation = "" \ No newline at end of file +battlestation = "" + +[notes] +md_file_dir = "" \ No newline at end of file diff --git a/api/composer.lock b/api/composer.lock index d46e22b..6ab197d 100755 --- a/api/composer.lock +++ b/api/composer.lock @@ -8,17 +8,11 @@ "packages": [ { "name": "reflect/plugin-rules", - "version": "1.5.0", + "version": "1.5.2", "source": { "type": "git", - "url": "https://github.com/VictorWesterlund/reflect-rules-plugin.git", - "reference": "9c837fd1944133edfed70a63ce8b32bf67f0d94b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/VictorWesterlund/reflect-rules-plugin/zipball/9c837fd1944133edfed70a63ce8b32bf67f0d94b", - "reference": "9c837fd1944133edfed70a63ce8b32bf67f0d94b", - "shasum": "" + "url": "https://codeberg.org/reflect/reflect-rules-plugin", + "reference": "aa7d969350f50d00d7dce01b948276946fcc0e81" }, "type": "library", "autoload": { @@ -37,11 +31,7 @@ } ], "description": "Add request search paramter and request body constraints to an API built with Reflect", - "support": { - "issues": "https://github.com/VictorWesterlund/reflect-rules-plugin/issues", - "source": "https://github.com/VictorWesterlund/reflect-rules-plugin/tree/1.5.0" - }, - "time": "2024-01-17T11:07:44+00:00" + "time": "2024-11-28T17:05:16+00:00" }, { "name": "victorwesterlund/xenum", @@ -121,5 +111,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.3.0" } diff --git a/api/endpoints/notes/GET.php b/api/endpoints/notes/GET.php new file mode 100644 index 0000000..3d89822 --- /dev/null +++ b/api/endpoints/notes/GET.php @@ -0,0 +1,46 @@ +ruleset = new Ruleset(strict: true); + + $this->ruleset->GET([ + (new Rules(GET_PARAM_NOTE)) + ->required() + ->type(Type::STRING) + ->min(1) + ]); + + $this->ruleset->validate_or_exit(); + } + + private function get_path(): string { + $base_path = substr($_ENV["notes"]["md_file_dir"], -1, 1) === "/" ? $_ENV["notes"]["md_file_dir"] : $_ENV["notes"]["md_file_dir"] . "/"; + + return "{$base_path}{$_GET[GET_PARAM_NOTE]}.md"; + } + + public function exists(): bool { + return is_readable($this->get_path()); + } + + public function read(): string { + return file_get_contents($this->get_path()); + } + + public function main(): Response { + return $this->exists() + ? new Response($this->read(), 200, "text/plain") + : new Response("Note not found", 404); + } + } \ No newline at end of file diff --git a/api/src/Endpoints.php b/api/src/Endpoints.php index 7ce9a17..00dfe16 100644 --- a/api/src/Endpoints.php +++ b/api/src/Endpoints.php @@ -11,6 +11,8 @@ case MESSAGES = "/messages"; + case NOTES = "/notes"; + case WORK = "/work"; case WORK_TAGS = "/work/tags"; case WORK_ACTIONS = "/work/actions"; diff --git a/public/notes/blog.php b/public/notes/blog.php new file mode 100644 index 0000000..086c5cf --- /dev/null +++ b/public/notes/blog.php @@ -0,0 +1,12 @@ + +lex() as $line): ?> + + \ No newline at end of file diff --git a/public/notes/index.php b/public/notes/index.php new file mode 100644 index 0000000..e69de29 diff --git a/src/helpers/Notes.php b/src/helpers/Notes.php new file mode 100644 index 0000000..f799306 --- /dev/null +++ b/src/helpers/Notes.php @@ -0,0 +1,209 @@ + HTML::NOOP, + "--" => HTML::NOOP, + "`" => HTML::CODE, + "**" => HTML::BOLD, + ">" => HTML::QUOTE, + "*" => HTML::ITALIC, + "![" => HTML::ANCHOR, + "[" => HTML::ANCHOR, + "]" => HTML::ANCHOR, + "(" => HTML::ANCHOR, + ")" => HTML::ANCHOR, + "#" => HTML::HEADING_1, + "##" => HTML::HEADING_2, + "###" => HTML::HEADING_3, + "- " => HTML::UNORDERED_LIST, + "---" => HTML::THEMATIC_BREAK + ]; + + class Notes extends API { + private string $html; + private ?HTML $token; + + private readonly SplObjectStorage $open; + private readonly Response $resp; + + public function __construct(string $note_id) { + parent::__construct(); + + $this->open = new SplObjectStorage(); + $this->resp = $this->call(Endpoints::NOTES->value)->params([ + "id" => $note_id + ])->get(); + + $this->init(); + } + + private function init(): object { + $this->html = ""; + $this->token = null; + + return $this->tokens(); + } + + private function push(string $text): self { + $this->html .= $text; + + return $this; + } + + private function open(HTML $tag): self { + $this->push("<{$tag->value}>"); + + $this->token = null; + $this->open[$tag] = true; + + return $this; + } + + private function close(HTML $tag): self { + unset($this->open[$tag]); + $this->token = null; + + $this->push("value}>"); + return $this; + } + + private function tokens(): object { + return new class { + private array $lexed = []; + + public function __construct() {} + + public function push(string $token): void { + $this->lexed[] = $token; + } + + // Return last lexed token by offset + public function offset(int $offset): ?string { + $len = count($this->lexed); + + return $offset <= $len ? $this->lexed[$len - $offset] : null; + } + + public function combine(int $offset): string { + $output = ""; + + for ($i = 1; $i <= $offset; $i++) { + if (!$this->offset($i)) { + continue; + } + + $output .= $this->offset($i); + } + + return $output; + } + }; + } + + public function line(): Generator|array { + if (!$this->resp->ok) { + return []; + } + + foreach (explode(PHP_EOL, $this->resp->output()) as $line) { + yield $line; + } + } + + public function lex(): Generator { + foreach ($this->line() as $line) { + $lexed = $this->init(); + + // Create + foreach (str_split($line) as $idx => $token) { + $lexed->push($token); + + if ($this->token === HTML::NOOP) { + $this->push($token); + } + + if (!array_key_exists($token, LEX_TOKENS)) { + // Open a new tag + if ($this->token) { + // Close an already open tag + if (isset($this->open[$this->token])) { + $this->close($this->token); + + // Push current literal and continue with next char + $this->push($token); + continue; + } + + $this->open($this->token); + } + + // Open a paragraph if first char of line is not a token + if ($idx === 0) { + $this->open(HTML::PARAGRAPH); + } + + // Push literal to output + $this->push($token); + continue; + } + + $this->token = LEX_TOKENS[$token]; + + // Backtrack in lex history until we find the first literal + $offset = 1; + while (array_key_exists($lexed->offset($offset), LEX_TOKENS)) { + // Update current token if we're still matching a keyword + if (array_key_exists($lexed->combine($offset), LEX_TOKENS)) { + $this->token = LEX_TOKENS[$lexed->combine($offset)]; + } + + $offset++; + } + } + + // Create a paragraph for empty lines + if (empty($this->html)) { + $this->open(HTML::PARAGRAPH)->push(HTML::EMPTY_LINE); + } + + // Close all remaining tags + foreach ($this->open as $tag) { + $this->close($tag); + } + + yield $this->html; + } + } + } \ No newline at end of file