Init commit
This commit is contained in:
parent
3930438c10
commit
d63c3a2cef
|
@ -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)
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Slimdown - A very basic regex-based Markdown parser.
|
||||
* Author: Johnny Broadway <johnny@johnnybroadway.com>
|
||||
* Website: https://gist.github.com/jbroadway/2836900
|
||||
* License: MIT
|
||||
*/
|
||||
class Slimdown {
|
||||
public static $rules = array (
|
||||
'/(?<!!)\[([^\[]+)\]\(([^\)]+)\)/' => '<a href=\'\2\'>\1</a>', // links
|
||||
'/(\*\*|__)(.*?)\1/' => '<strong>\2</strong>', // bold
|
||||
'/(\*|_)(.*?)\1/' => '<em>\2</em>', // emphasis
|
||||
'/\~\~(.*?)\~\~/' => '<del>\1</del>', // del
|
||||
'/\:\"(.*?)\"\:/' => '<q>\1</q>', // quote
|
||||
'/`(.*?)`/' => '<code>\1</code>', // inline code
|
||||
'/(?:!\[([^\[]+)\]\(([^\)]+)\))/' => '<img src=\'\2\' alt=\'\1\' loading=\'lazy\'>',
|
||||
'/==(.*?)==/' => '<mark>\1</mark>',
|
||||
);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
// Inlcude the file
|
||||
include_once 'picoblog.php';
|
||||
|
||||
// Instantiate the class with the source file
|
||||
$mb = new \hxii\PicoBlog('blog.txt');
|
||||
|
||||
// Parse query string and get blog entries
|
||||
$query = $mb->parseQuery();
|
||||
$entries = ($query) ? $mb->getEntries($query) : $mb->getEntries('all');
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>picoblog @ <?= $_SERVER['HTTP_HOST'] ?></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>picoblog</h1>
|
||||
<?php
|
||||
// Display message and link to main list if viewing a filtered entry list
|
||||
if ($query) {
|
||||
echo '<div>Currently viewing ' . implode('', $query) . '. Back to <a href="' . $_SERVER['PHP_SELF'] . '">list?</a></div>';
|
||||
}
|
||||
?>
|
||||
<ol>
|
||||
<!-- Render entries -->
|
||||
<?= $mb->renderEntries($entries, '<li class="e">{entry}</li>'); ?>
|
||||
</ol>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -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).
|
|
@ -0,0 +1,154 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* picoblog
|
||||
*
|
||||
* made by hxii (https://0xff.nu/picoblog)
|
||||
*
|
||||
* Picoblog is a very simple front-end for a twtxt (https://github.com/prologic/twtxt) format microblog with support for:
|
||||
* - Limited Markdown (strong, em, marked, deleted, links, images, inline code).
|
||||
* - Tags (#tags are automatically converted to links).
|
||||
* - Unique IDs (I use them, but they are optional).
|
||||
*/
|
||||
|
||||
namespace hxii;
|
||||
|
||||
class PicoBlog
|
||||
{
|
||||
|
||||
private $sourcefile, $format;
|
||||
public $rawentries, $blog;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $sourcefile Source file in twtxt format (or PicoBlog format).
|
||||
*/
|
||||
public function __construct(string $sourcefile, string $format = 'picoblog')
|
||||
{
|
||||
$this->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 = '/^(?<date>[0-9-T:Z]+)\t(?<entry>.*)/';
|
||||
break;
|
||||
case 'picoblog':
|
||||
$pattern = '/^(?<date>[0-9-T:Z]+)\t(?<id>[a-zA-Z0-9]{6})\t(?<entry>.*)/';
|
||||
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+)?/', '<a href="?tag=$1">#${1}</a>', $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. <li>{entry}</li>
|
||||
* @param bool $parseTags should #tags be parsed to links?
|
||||
* @return string entries in HTML
|
||||
*/
|
||||
public function renderEntries(array $entries, string $entryWrap = '<li>{entry}</li>', 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 = "<a href='?id={$id}' title='{$date}'>[{$id}]</a> " . $text;
|
||||
$html .= str_replace('{entry}', $text, $entryWrap);
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue