diff --git a/README.md b/README.md new file mode 100644 index 0000000..9b68413 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# picoblog +picoblog is a super simple microblog which was written to be a part of [Saisho](https://0xff.nu/saisho). +It was converted to be standalone after I figured people might want to use it without using Saisho. + +See `blog.php` for example usage, or use it as is (note: it's VERY basic). + +## What it has +- Markdown support (using modified Slimdown): Bold, Italic, Marked, Removed, Link, Image, Inline Code. +- Support for tags and IDs. +- Using either twtxt format or picoblog format (which is the same as twtxt except it also has unique IDs). + +## What it does't have +- Literally everything else. The goal here (aligned with Saisho), is to keep everything simple. + +[Home](https://0xff.nu/picoblog) + +[Bug Reports](https://todo.sr.ht/~hxii/picoblog) \ No newline at end of file diff --git a/Slimdown.php b/Slimdown.php new file mode 100644 index 0000000..063b648 --- /dev/null +++ b/Slimdown.php @@ -0,0 +1,42 @@ + + * Website: https://gist.github.com/jbroadway/2836900 + * License: MIT + */ +class Slimdown { + public static $rules = array ( + '/(? '\1', // links + '/(\*\*|__)(.*?)\1/' => '\2', // bold + '/(\*|_)(.*?)\1/' => '\2', // emphasis + '/\~\~(.*?)\~\~/' => '\1', // del + '/\:\"(.*?)\"\:/' => '\1', // quote + '/`(.*?)`/' => '\1', // inline code + '/(?:!\[([^\[]+)\]\(([^\)]+)\))/' => '\'\1\'', + '/==(.*?)==/' => '\1', + ); + + /** + * Add a rule. + */ + public static function add_rule ($regex, $replacement) { + self::$rules[$regex] = $replacement; + } + + /** + * Render some Markdown into HTML. + */ + public static function render ($text) { + $text = "\n" . $text . "\n"; + foreach (self::$rules as $regex => $replacement) { + if (is_callable ( $replacement)) { + $text = preg_replace_callback ($regex, $replacement, $text); + } else { + $text = preg_replace ($regex, $replacement, $text); + } + } + return trim ($text); + } +} diff --git a/blog.php b/blog.php new file mode 100644 index 0000000..3c91450 --- /dev/null +++ b/blog.php @@ -0,0 +1,38 @@ +parseQuery(); +$entries = ($query) ? $mb->getEntries($query) : $mb->getEntries('all'); + +?> + + + + + + + + picoblog @ <?= $_SERVER['HTTP_HOST'] ?> + + + +

picoblog

+ Currently viewing ' . implode('', $query) . '. Back to list?'; + } + ?> +
    + + renderEntries($entries, '
  1. {entry}
  2. '); ?> +
+ + + \ No newline at end of file diff --git a/blog.txt b/blog.txt new file mode 100644 index 0000000..a890835 --- /dev/null +++ b/blog.txt @@ -0,0 +1 @@ +2020-10-01T08:05:33Z ac7c64 Welcome to picoblog. For updates and such, visit [https://0xff.nu/picoblog](https://0xff.nu/picoblog). \ No newline at end of file diff --git a/picoblog.php b/picoblog.php new file mode 100644 index 0000000..dc17da4 --- /dev/null +++ b/picoblog.php @@ -0,0 +1,154 @@ +sourcefile = $sourcefile; + $this->format = $format; + $this->readSource(); + } + + /** + * Check for and parse query string from $_SERVER['QUERY_STRING']). + * Used to get entries by ID or tag. + * + * @return array|boolean + */ + public function parseQuery() + { + if (isset($_SERVER['QUERY_STRING'])) { + parse_str($_SERVER['QUERY_STRING'], $return); + return $return; + } + return false; + } + + /** + * Read source file + * + * @return boolean true if successful, false if not + */ + private function readSource() + { + if (is_file($this->sourcefile) && is_readable($this->sourcefile)) { + $this->rawentries = file($this->sourcefile); + if (!empty($this->rawentries)) { + return true; + } + } + throw new \Exception("{$this->sourcefile} is empty! Aborting."); + return false; + } + + /** + * Parse entries from source file and replace tags with links + * + * @param array $entries array of raw entries + * @return void + */ + private function parseEntries(array $entries, bool $parseTags = true) + { + switch ($this->format) { + case 'twtxt': + $pattern = '/^(?[0-9-T:Z]+)\t(?.*)/'; + break; + case 'picoblog': + $pattern = '/^(?[0-9-T:Z]+)\t(?[a-zA-Z0-9]{6})\t(?.*)/'; + break; + } + foreach ($entries as $i => $entry) { + preg_match($pattern, $entry, $matches); + if (!$matches) continue; + $id = (!empty($matches['id'])) ? $matches['id'] : $i; + $parsedEntries[$id] = [ + 'date' => $matches['date'], + 'entry' => ($parseTags) ? preg_replace('/#(\w+)?/', '#${1}', $matches['entry']) : $matches['entry'], + ]; + } + return $parsedEntries; + } + + /** + * Returns a filtered list of raw entries + * + * @param string|array $search entry filter. can be 'all', 'newest', 'oldest', 'random' or an ID/Tag. + * For ID, we're looking for ['id'=>'IDHERE']. For tag, we're looking for ['tag'=>'tagname'] + * @return boolean|array + */ + public function getEntries($search) + { + switch ($search) { + case '': + return false; + case 'all': + return $this->rawentries; + case 'newest': + return [reset($this->rawentries)]; + case 'oldest': + return [end($this->rawentries)]; + case 'random': + return [$this->rawentries[array_rand($this->rawentries, 1)]]; + default: + if (isset($search['id'])) { + $filter = array_filter($this->rawentries, function ($entry) use ($search) { + preg_match("/\b$search[id]\b/i", $entry, $match); + return $match; + }); + return $filter; + } elseif (isset($search['tag'])) { + $filter = array_filter($this->rawentries, function ($entry) use ($search) { + preg_match("/#\b$search[tag]\b/i", $entry, $match); + return $match; + }); + return $filter; + } + return false; + } + } + + /** + * Render Markdown in given entries and output as HTML + * + * @param array $entries array of parsed entries to render + * @param string $entryWrap tne entry wrapper, e.g.
  • {entry}
  • + * @param bool $parseTags should #tags be parsed to links? + * @return string entries in HTML + */ + public function renderEntries(array $entries, string $entryWrap = '
  • {entry}
  • ', bool $parseTags = true) + { + if (!$entries) return false; + $entries = $this->parseEntries($entries, $parseTags); + require_once('Slimdown.php'); + $html = ''; + foreach ($entries as $id => $entry) { + $text = \Slimdown::render($entry['entry']); + $date = $entry['date']; + $text = "[{$id}] " . $text; + $html .= str_replace('{entry}', $text, $entryWrap); + } + return $html; + } +} \ No newline at end of file