From: Ben Sherratt Date: Sat, 27 Dec 2025 01:38:48 +0000 (+0000) Subject: Initial commit X-Git-Url: https://git.bts.cx/garden.git/commitdiff_plain?ds=sidebyside Initial commit --- 9b85fe10f66386c9af92a9a2d2bd79838ff8f883 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..841e07a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "garden/third_party/parsedown"] + path = garden/third_party/parsedown + url = https://github.com/erusev/parsedown.git diff --git a/config.php b/config.php new file mode 100644 index 0000000..36a3931 --- /dev/null +++ b/config.php @@ -0,0 +1,14 @@ +path = $path; + $this->type = $type; + $this->id = $id; + $this->title = $title; + $this->url = $url; + $this->date = $date; + $this->source_filename = $source_filename; + $this->target_filename = $target_filename; + } +} + +function garden_make_process_items($output_directory, $content_paths) { + $output_items = []; + + foreach ($content_paths as $item) { + $id = garden_slug($item->filename); + + $target_extension = $item->extension; + + $ignore_file = false; + $type = GardenItemType::Raw; + switch ($item->extension) { + case 'php': + $ignore_file = true; + break; + + case 'md': + $type = GardenItemType::Article; + $target_extension = 'html'; + break; + + case 'png': + case 'jpg': + case 'jpeg': + case 'gif': + $type = GardenItemType::Image; + break; + } + + if ($ignore_file == true) { + continue; + } + + $target_basename = $item->filename; + if ($target_extension != '') { + $target_basename .= '.' . $target_extension; + } + + $url = GARDEN_SITE_BASE_URL . garden_url($item->path, $target_basename); + $target_path = garden_path($output_directory, garden_url($item->path, $target_basename)); + + $date = filemtime($item->full_path); + + $output_items[] = new GardenItem($item, $type, $id, $item->filename, $url, $date, $item->full_path, $target_path); + } + + return $output_items; +} + +/////////////////////////////////////////////////////////////////////////////// +// Paths +/////////////////////////////////////////////////////////////////////////////// + +class GardenContentPath { + public $full_path; + public $root; + public $path; + public $basename; + public $filename; + public $extension; + + public function __construct($full_path, $root, $path, $path_parts) { + $this->full_path = $full_path; + $this->root = $root; + $this->path = $path; + $this->basename = $path_parts['basename']; + $this->filename = $path_parts['filename']; + $this->extension = $path_parts['extension']; + } +} + +function garden_find_content_files($content_dir) { + $content_dir = realpath($content_dir); + + $scan_paths = ['']; + $output_paths = []; + + while (count($scan_paths) > 0) { + $scan_path = array_shift($scan_paths); + $path_contents = scandir(garden_path($content_dir, $scan_path)); + foreach ($path_contents as $item) { + if (str_starts_with($item, '.')) { + continue; + } + + $full_path = garden_path($content_dir, $scan_path, $item); + if (is_dir($full_path)) { + $scan_paths[] = garden_path($scan_path, $item); + continue; + } + + $path_parts = pathinfo($full_path); + $output_paths[] = new GardenContentPath($full_path, $content_dir, $scan_path, $path_parts); + } + } + + return $output_paths; +} + +/////////////////////////////////////////////////////////////////////////////// +// Directories +/////////////////////////////////////////////////////////////////////////////// + +function garden_make_directories($content_items) { + foreach ($content_items as $item) { + $path_parts = pathinfo($item->target_filename); + + $directory = $path_parts['dirname']; + if (is_dir($directory ) == false) { + mkdir($directory , 0777, true); // FIXME, permissions... + } + } +} + +function garden_move_raw($content_items) { + foreach ($content_items as $item) { + if ($item->type != GardenItemType::Raw && $item->type != GardenItemType::Image) { + continue; + } + + $success = copy($item->source_filename, $item->target_filename); + if ($success != true) { + error('Failed to copy file: filename="', $item->source_filename, '"'); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Template +/////////////////////////////////////////////////////////////////////////////// + +function garden_template_render($name, $variables = null) { + global $garden_template_base, $garden_template_content; + + $base_template = null; + + $output = ''; + while ($name != null) { + $path = garden_path(GARDEN_TEMPLATE_DIR, $name . '.php'); + + $base_template = null; + $base_template_variables = null; + + $garden_template_base_previous = $garden_template_base; + $garden_template_base = function($name, $base_variables) use (&$base_template, &$base_template_variables) { + $base_template = $name; + $base_template_variables = $base_variables; + }; + + $garden_template_content_previous = $garden_template_content; + $garden_template_content = function() use ($output) { + return $output; + }; + + if ($variables != null) { + extract($variables); + } + + ob_start(); + include($path); + $output = ob_get_contents(); + ob_end_clean(); + + $garden_template_base = $garden_template_base_previous; + $garden_template_content = $garden_template_content_previous; + + $name = $base_template; + $variables = $base_template_variables; + } + + return $output; +} + +/////////////////////////////////////////////////////////////////////////////// +// Template helpers +/////////////////////////////////////////////////////////////////////////////// + +function garden_template_base($name, $variables = null) { + global $garden_template_base; + $garden_template_base($name, $variables); +} + +function garden_template_content() { + global $garden_template_content; + return $garden_template_content(); +} + +/////////////////////////////////////////////////////////////////////////////// +// HTML +/////////////////////////////////////////////////////////////////////////////// + +require_once(garden_path(__DIR__, 'third_party', 'parsedown', 'Parsedown.php')); + +class GardenExtendedParsedown extends Parsedown { + private $content_items; + + public function __construct($content_items) { + $this->content_items = $content_items; + $this->InlineTypes['!'][] = 'Youtube'; + $this->InlineTypes['!'][] = 'Image'; + $this->InlineTypes['['][] = 'WikiLinks'; + $this->inlineMarkerList .= '!'; + $this->inlineMarkerList .= '['; + } + + protected function inlineImage($excerpt) { + if (preg_match('/^!\[\[([^\|\]]+)(\|([0-9]+)x([0-9]+))?\]\]/', $excerpt['text'], $matches)) { + $image_name = $matches[1]; + $image_width = count($matches) == 5 ? $matches[3] : null; + $image_height = count($matches) == 5 ? $matches[4] : null; + + if ($image_name == null) { + return; + } + + $target_url = null; + foreach ($this->content_items as $content_item) { + if ($content_item->path->basename == $image_name) { + $target_url = $content_item->url; + break; + } + } + + if ($target_url == null) { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'img', + 'text' => '', + 'attributes' => array( + 'src' => $target_url, + ), + ), + ); + } + } + + protected function inlineYoutube($excerpt) { + if (preg_match('/^!\[\[yt:([^\]]+)\]\]/', $excerpt['text'], $matches)) { + $video_id = $matches[1]; + + if ($video_id == null) { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'iframe', + 'text' => '', + 'attributes' => array( + 'class' => "video", + 'type' => "text/html", + //'width' => "640", + //'height' => "360", + 'src' => "https://www.youtube.com/embed/" . $video_id, + 'frameborder' => "0", + 'loading' => "lazy", + 'referrerpolicy' => "no-referrer", + 'sandbox' => "allow-same-origin allow-scripts", + ), + ), + ); + } + } + + protected function inlineWikiLinks($excerpt) { + if (preg_match('/^\[\[(.+)\]\]/', $excerpt['text'], $matches)) { + $target_title = $matches[1]; + + if ($target_title == null) { + return; + } + + $target_url = null; + foreach ($this->content_items as $content_item) { + if ($content_item->title == $target_title) { + $target_url = $content_item->url; + break; + } + } + + if ($target_url == null) { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $target_title, + 'attributes' => array( + 'href' => $target_url, + ), + ), + ); + } + } +} + +function garden_generate_html($content_items) { + $parsedown = new GardenExtendedParsedown($content_items); + + foreach ($content_items as $item) { + if ($item->type != GardenItemType::Article) { + continue; + } + + $markdown_data = garden_read_file($item->source_filename); + $markdown_html = $parsedown->text($markdown_data); + + $html_data = garden_template_render('article', [ 'article' => $item, 'article_content' => $markdown_html ]); + + garden_write_file($item->target_filename, $html_data); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main +/////////////////////////////////////////////////////////////////////////////// + +function garden() { + $content_files = garden_find_content_files(GARDEN_CONTENT_DIR); + array_push($content_files, ...garden_find_content_files(GARDEN_TEMPLATE_DIR)); + $process_items = garden_make_process_items(GARDEN_OUTPUT_DIR, $content_files); + garden_make_directories($process_items); + garden_move_raw($process_items); + garden_generate_html($process_items); +} + +/////////////////////////////////////////////////////////////////////////////// +// Main code! +/////////////////////////////////////////////////////////////////////////////// + +assert($argc >= 2); + +// First parameter needs to be the configuration php +$config_file = $argv[1]; +output_debug("Will use configuration: file='", $config_file, "'"); +require_once($config_file); + +garden(); diff --git a/garden/third_party/parsedown b/garden/third_party/parsedown new file mode 160000 index 0000000..0b274ac --- /dev/null +++ b/garden/third_party/parsedown @@ -0,0 +1 @@ +Subproject commit 0b274ac959624e6c6d647e9c9b6c2d20da242004 diff --git a/templates/article.php b/templates/article.php new file mode 100644 index 0000000..9c5d098 --- /dev/null +++ b/templates/article.php @@ -0,0 +1,9 @@ + + +
+ +
diff --git a/templates/base.php b/templates/base.php new file mode 100644 index 0000000..83217f2 --- /dev/null +++ b/templates/base.php @@ -0,0 +1,24 @@ + + + + + + + + + <?= site_name ?> - <?= $article->title; ?> + + + +
+

Find me @btsherratt or on itch.io

+
+ + + + + +