From f3a8d2cc41051d74dba0acf739091adb40845b40 Mon Sep 17 00:00:00 2001 From: xt Date: Sun, 17 Jan 2021 21:15:23 +0800 Subject: [PATCH 01/28] pre-release ` --- Cmd/BCmd.php | 10 ++++++ Cmd/BuildCmd.php | 10 ++++++ Cmd/CCmd.php | 10 ++++++ Cmd/CleanCmd.php | 10 ++++++ Cmd/HCmd.php | 10 ++++++ Cmd/HelpCmd.php | 10 ++++++ Cmd/NewCmd.php | 10 ++++++ Cmd/SCmd.php | 10 ++++++ Cmd/ServeCmd.php | 10 ++++++ Lib/Base/BaseCmd.php | 10 ++++++ Lib/Base/BaseTemplate.php | 10 ++++++ Lib/Parser.php | 7 ++++ Lib/main.php | 1 + Templates/ArchiveTemplate.php | 10 ++++++ Templates/PageTemplate.php | 63 +++++++++++++++++++++++++++++++++++ Templates/PostTemplate.php | 10 ++++++ Templates/_config.php | 1 + 17 files changed, 202 insertions(+) create mode 100644 Cmd/BCmd.php create mode 100644 Cmd/BuildCmd.php create mode 100644 Cmd/CCmd.php create mode 100644 Cmd/CleanCmd.php create mode 100644 Cmd/HCmd.php create mode 100644 Cmd/HelpCmd.php create mode 100644 Cmd/NewCmd.php create mode 100644 Cmd/SCmd.php create mode 100644 Cmd/ServeCmd.php create mode 100644 Lib/Base/BaseCmd.php create mode 100644 Lib/Base/BaseTemplate.php create mode 100644 Lib/Parser.php create mode 100644 Lib/main.php create mode 100644 Templates/ArchiveTemplate.php create mode 100644 Templates/PageTemplate.php create mode 100644 Templates/PostTemplate.php create mode 100644 Templates/_config.php diff --git a/Cmd/BCmd.php b/Cmd/BCmd.php new file mode 100644 index 0000000..83337c2 --- /dev/null +++ b/Cmd/BCmd.php @@ -0,0 +1,10 @@ + + + + + + {$postSplit['title']} + + +

{$postSplit['title']}

+

{$postSplit['datetime']}

+

+
+EOF + .(new Parser())->makeHtml($postSplit['body']). + << + + +EOF; + + return $html; + } + + private static function splitPost($mdFileContent) + { + $aStr = ""; + $b = stripos($mdFileContent, $bStr); + $comments = substr($mdFileContent, $a, $b - $a); + $commentArr = explode("\n", $comments); + foreach ($commentArr as $commentLine) { + if (!stripos($commentLine, "|")) continue; + list($_k, $_v) = explode("|", $commentLine); + switch (trim($_k)) { + case 'title': $_title = trim($_v); break; + case 'datetime': $_datetime = trim($_v); break; + } + } + return [ + 'title' => $_title ?? '', + 'datetime' => $_datetime ?? '', + 'body' => substr($mdFileContent, stripos($mdFileContent, "-->") + 3) + ]; + } +} diff --git a/Templates/PostTemplate.php b/Templates/PostTemplate.php new file mode 100644 index 0000000..8fc4f95 --- /dev/null +++ b/Templates/PostTemplate.php @@ -0,0 +1,10 @@ + Date: Sun, 17 Jan 2021 21:15:30 +0800 Subject: [PATCH 02/28] pre-release 1 --- .gitignore | 5 + Cmd/BCmd.php | 15 +- Cmd/BuildCmd.php | 105 +- Cmd/CCmd.php | 15 +- Cmd/CleanCmd.php | 26 +- Cmd/HCmd.php | 15 +- Cmd/HelpCmd.php | 30 +- Cmd/NewCmd.php | 76 +- Cmd/SCmd.php | 15 +- Cmd/ServeCmd.php | 10 +- Lib/Base/BaseCmd.php | 8 +- Lib/Base/BaseTemplate.php | 2 +- Lib/Parser.php | 1791 +++++++++++++++++++++++++++++++ Lib/main.php | 13 + README.en.md | 36 - README.md | 248 ++++- Sources/Pages/about/index.md | 10 + Sources/Pages/doc/index.md | 236 ++++ Sources/Pages/page1/index.md | 4 + Sources/Posts/template/index.md | 4 + Templates/ArchiveTemplate.php | 76 +- Templates/IndexTemplate.php | 80 ++ Templates/PageTemplate.php | 2 +- Templates/PostTemplate.php | 55 +- Templates/_config.php | 3 + Templates/page/archives.php | 35 + Templates/page/index.php | 53 + Templates/page/page.php | 58 + Templates/page/post.php | 25 + composer.json | 19 + meo | 16 + 31 files changed, 3006 insertions(+), 80 deletions(-) create mode 100644 .gitignore delete mode 100644 README.en.md create mode 100644 Sources/Pages/about/index.md create mode 100644 Sources/Pages/doc/index.md create mode 100644 Sources/Pages/page1/index.md create mode 100644 Sources/Posts/template/index.md create mode 100644 Templates/IndexTemplate.php create mode 100644 Templates/page/archives.php create mode 100644 Templates/page/index.php create mode 100644 Templates/page/page.php create mode 100644 Templates/page/post.php create mode 100644 composer.json create mode 100755 meo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..172891f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/vendor/ +env.php +.idea +/Dist/* +.DS_Store diff --git a/Cmd/BCmd.php b/Cmd/BCmd.php index 83337c2..7b3f425 100644 --- a/Cmd/BCmd.php +++ b/Cmd/BCmd.php @@ -4,7 +4,18 @@ namespace Cmd; -class BCmd -{ +use Lib\Base\BaseCmd; +/** + * Class BCmd + * @package Cmd + * + * Alias of Build + */ +class BCmd implements BaseCmd +{ + public static function handle($argv) + { + BuildCmd::handle($argv); + } } diff --git a/Cmd/BuildCmd.php b/Cmd/BuildCmd.php index 5f78577..5d01325 100644 --- a/Cmd/BuildCmd.php +++ b/Cmd/BuildCmd.php @@ -4,7 +4,110 @@ namespace Cmd; -class BuildCmd +use Lib\Base\BaseCmd; +use Lib\Parser; +use Templates\ArchiveTemplate; +use Templates\IndexTemplate; +use Templates\PageTemplate; +use Templates\PostTemplate; + +class BuildCmd implements BaseCmd { + public static function handle($argv) + { + CleanCmd::handle($argv); + + if(!is_dir(DIST_DIR)) { + mkdir(DIST_DIR); + echo "Mkdir success! " . DIST_DIR; + } + $themePath = ROOT_DIR . '/Templates/'; + echo "Load theme from path: " . $themePath . "\n"; + if (!is_dir($themePath)) { + die("{$themePath} not exist!\n"); + } + + echo "Load theme success! \n Build index page...\n"; + self::buildIndex(); + echo "Build archives...\n"; + self::buildArchives(); + echo "Build posts...\n"; + self::buildPosts(POST_DIR); + echo "Build pages...\n"; + self::buildPages(PAGE_DIR); + + echo "Build finish!\nSee at " . DIST_DIR; + } + + public static function buildIndex() + { + $html = IndexTemplate::render([]); + file_put_contents(DIST_DIR . '/' . 'index.html', $html); + } + + public static function buildPosts($postDir) + { + $posts = scandir($postDir); + foreach ($posts as $v) { + $filePath = $postDir . '/' . $v; + if (is_dir($filePath)) { + echo "{$filePath} is dir \n"; + if (in_array($v, ['.', '..'])) continue; + if (!is_dir(DIST_DIR . '/_posts/' . $v)) { + if (!is_dir(DIST_DIR . '/_posts')) mkdir (DIST_DIR . '/_posts'); + mkdir(DIST_DIR . '/_posts/' . $v); + } + self::buildPosts($filePath); + } else if(is_file($filePath) && stripos($filePath, '.md')) { + echo "{$filePath} is file \n"; + $htmlFilePath = str_replace('.md', '.html', $filePath); + $htmlFilePath = str_replace('Sources/Posts', 'Dist/_posts', $htmlFilePath); + if (!file_exists($htmlFilePath)) { + touch($htmlFilePath); + } + $html = PostTemplate::render([ + 'filePath' => $filePath + ]); + file_put_contents($htmlFilePath, $html); + } + } + } + + public static function buildPages($pageDir) + { + $posts = scandir($pageDir); + foreach ($posts as $v) { + $filePath = $pageDir . '/' . $v; + if (is_dir($filePath)) { + echo "{$filePath} is dir \n"; + if (in_array($v, ['.', '..'])) continue; + if (!is_dir(DIST_DIR . '/' . $v)) { + if (!is_dir(DIST_DIR)) mkdir (DIST_DIR . '/_posts'); + mkdir(DIST_DIR . '/' . $v); + } + self::buildPages($filePath); + } else if(is_file($filePath) && stripos($filePath, '.md')) { + echo "{$filePath} is file \n"; + $htmlFilePath = str_replace('.md', '.html', $filePath); + $htmlFilePath = str_replace('Sources/Pages', 'Dist', $htmlFilePath); + if (!file_exists($htmlFilePath)) { + touch($htmlFilePath); + } + $html = PageTemplate::render([ + 'filePath' => $filePath + ]); + file_put_contents($htmlFilePath, $html); + } + } + } + public static function buildArchives() + { + $html = ArchiveTemplate::render([]); + $archivesFilePath = DIST_DIR . '/archives.html'; + if (!file_exists($archivesFilePath)) { + touch($archivesFilePath); + } + file_put_contents($archivesFilePath, $html); + } } diff --git a/Cmd/CCmd.php b/Cmd/CCmd.php index 7b7eed4..3b39127 100644 --- a/Cmd/CCmd.php +++ b/Cmd/CCmd.php @@ -4,7 +4,18 @@ namespace Cmd; -class CCmd -{ +use Lib\Base\BaseCmd; +/** + * Class CCmd + * @package Cmd + * + * Alias of Clean + */ +class CCmd implements BaseCmd +{ + public static function handle($argv) + { + CleanCmd::handle($argv); + } } diff --git a/Cmd/CleanCmd.php b/Cmd/CleanCmd.php index 70a3897..67a57f6 100644 --- a/Cmd/CleanCmd.php +++ b/Cmd/CleanCmd.php @@ -4,7 +4,31 @@ namespace Cmd; -class CleanCmd +use Lib\Base\BaseCmd; + +class CleanCmd implements BaseCmd { + public static function handle($argv) + { + self::handleRmFilesOfDir(DIST_DIR); + echo "Clean dist path finish\n"; + } + public static function handleRmFilesOfDir($path) + { + $files = scandir($path); + foreach ($files as $v) { + $_filePath = $path . '/' . $v; + if (in_array($v, ['.', '..'])) continue; + if (is_dir($_filePath)) { + echo "{$_filePath} is a path \n"; + self::handleRmFilesOfDir($_filePath); + rmdir($_filePath); + } + if (is_file($_filePath)) { + echo "{$_filePath} is a file \n"; + unlink($_filePath); + } + } + } } diff --git a/Cmd/HCmd.php b/Cmd/HCmd.php index c33f4c8..748a713 100644 --- a/Cmd/HCmd.php +++ b/Cmd/HCmd.php @@ -4,7 +4,18 @@ namespace Cmd; -class HCmd -{ +use Lib\Base\BaseCmd; +/** + * Class HCmd + * @package Cmd + * + * Alias of Help + */ +class HCmd implements BaseCmd +{ + public static function handle($argv) + { + return HelpCmd::handle($argv); + } } diff --git a/Cmd/HelpCmd.php b/Cmd/HelpCmd.php index 6a8e56f..831c1ef 100644 --- a/Cmd/HelpCmd.php +++ b/Cmd/HelpCmd.php @@ -4,7 +4,35 @@ namespace Cmd; -class HelpCmd +use Lib\Base\BaseCmd; + +class HelpCmd implements BaseCmd { + public static function handle($argv) + { + echo " + _ _ + _ __ ___ ___ ___ | |__ | | ___ __ _ +| '_ ` _ \ / _ \/ _ \ | '_ \| |/ _ \ / _` | +| | | | | | __/ (_) | | |_) | | (_) | (_| | +|_| |_| |_|\___|\___/ |_.__/|_|\___/ \__, | + |___/ + +Welcome to use meo blog! +This is a blog system written by a white cat's father! + +Usage: php meo \ +Here are all of commands: + +- php meo help | h: Get help(just like you now!) +- php meo new \ \: Create a new post +- php meo build | b: Build static files +- php meo serve | s: Start develop server, and check page render when file change +- php meo clean | c: Clean static files and db file + +If you want join us, visit https://gitee.com/xtzero/meoBlog. +Enjoy yourself~ +"; + } } diff --git a/Cmd/NewCmd.php b/Cmd/NewCmd.php index 3e3fa1d..df28b26 100644 --- a/Cmd/NewCmd.php +++ b/Cmd/NewCmd.php @@ -4,7 +4,81 @@ namespace Cmd; -class NewCmd +use Lib\Base\BaseCmd; + +class NewCmd implements BaseCmd { + public static function handle($argv) + { + self::init(); + $type = $argv[2] ?? ""; + $filename = $argv[3] ?? ""; + if (empty($filename)) { + fwrite(STDOUT, "Please input file name: [". time() .".md]"); + $stdin = fgets(STDIN); + if (!$stdin || $stdin == "\n") { + $filename = time() . '.md'; + } else { + $filename = $stdin; + } + } + + beginSwitch: + if (!in_array($type, ['post', 'page'])) { + fwrite(STDOUT, "Please input [post|page|others for cancel]:"); + $type = substr(fgets(STDIN), 0, -1); + if (in_array($type, ['post', 'page'])) { + goto beginSwitch; + } else { + die('Bye~'); + } + } + $filename = str_replace(["\n", " ", "\t", "\r"], "", $filename); + switch ($type) { + case 'post': self::handleNewPost($filename, $argv); break; + case 'page': self::handleNewPage($filename, $argv); break; + } + } + + public static function handleNewPost($filename, $argv) + { + self::handleNewFile($filename, 'Posts'); + } + + public static function handleNewPage($filename, $argv) + { + self::handleNewFile($filename, 'Pages'); + } + + private static function handleNewFile($filename, $category) + { + $mdFolder = ROOT_DIR . "/Sources/{$category}/{$filename}"; + if (is_dir($mdFolder)) { + die("Folder exist! {$mdFolder}"); + } + $mdFilePath = "{$mdFolder}/index.md"; + if (file_exists($mdFilePath)) { + die("File already exist: {$mdFilePath}"); + } + mkdir($mdFolder); + touch($mdFilePath); + $defaultContent = ""; + file_put_contents($mdFilePath, $defaultContent); + die("Create file success: {$mdFilePath}"); + } + public static function init() + { + $postPath = POST_DIR; + $pagePath = PAGE_DIR; + if (!is_dir($postPath)) { + mkdir($postPath); + } + if (!is_dir($pagePath)) { + mkdir($pagePath); + } + } } diff --git a/Cmd/SCmd.php b/Cmd/SCmd.php index ab264ab..aafde5c 100644 --- a/Cmd/SCmd.php +++ b/Cmd/SCmd.php @@ -4,7 +4,18 @@ namespace Cmd; -class SCmd -{ +use Lib\Base\BaseCmd; +/** + * Class SCmd + * @package Cmd + * + * Alias of Serve + */ +class SCmd implements BaseCmd +{ + public static function handle($argv) + { + ServeCmd::handle($argv); + } } diff --git a/Cmd/ServeCmd.php b/Cmd/ServeCmd.php index f9a1506..0269c68 100644 --- a/Cmd/ServeCmd.php +++ b/Cmd/ServeCmd.php @@ -4,7 +4,13 @@ namespace Cmd; -class ServeCmd -{ +use Lib\Base\BaseCmd; +class ServeCmd implements BaseCmd +{ + public static function handle($argv) + { + BuildCmd::handle($argv); + system("cd ". DIST_DIR ." && php -S ". ENV['develop_server_host'] .":".ENV['develop_server_port']); + } } diff --git a/Lib/Base/BaseCmd.php b/Lib/Base/BaseCmd.php index 3a3498d..7964be3 100644 --- a/Lib/Base/BaseCmd.php +++ b/Lib/Base/BaseCmd.php @@ -4,7 +4,11 @@ namespace Lib\Base; -class BaseCmd +interface BaseCmd { - + /** + * A command line command must have a `handle` function which is public and static! + * @return mixed + */ + public static function handle($argv); } diff --git a/Lib/Base/BaseTemplate.php b/Lib/Base/BaseTemplate.php index c8d683b..57edade 100644 --- a/Lib/Base/BaseTemplate.php +++ b/Lib/Base/BaseTemplate.php @@ -6,5 +6,5 @@ namespace Lib\Base; interface BaseTemplate { - + public static function render($data); } diff --git a/Lib/Parser.php b/Lib/Parser.php index 801cb39..bfcd703 100644 --- a/Lib/Parser.php +++ b/Lib/Parser.php @@ -1,7 +1,1798 @@ + * @license BSD License + */ class Parser { + /** + * _whiteList + * + * @var string + */ + public $_commonWhiteList = 'kbd|b|i|strong|em|sup|sub|br|code|del|a|hr|small'; + /** + * html tags + * + * @var string + */ + public $_blockHtmlTags = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption|svg|script|noscript'; + + /** + * _specialWhiteList + * + * @var mixed + * @access private + */ + public $_specialWhiteList = array( + 'table' => 'table|tbody|thead|tfoot|tr|td|th' + ); + + /** + * _footnotes + * + * @var array + */ + public $_footnotes; + + /** + * @var bool + */ + public $_html = false; + + /** + * @var bool + */ + public $_line = false; + + /** + * @var array + */ + public $blockParsers = array( + array('code', 10), + array('shtml', 20), + array('pre', 30), + array('ahtml', 40), + array('shr', 50), + array('list', 60), + array('math', 70), + array('html', 80), + array('footnote', 90), + array('definition', 100), + array('quote', 110), + array('table', 120), + array('sh', 130), + array('mh', 140), + array('dhr', 150), + array('default', 9999) + ); + + /** + * _blocks + * + * @var array + */ + private $_blocks; + + /** + * _current + * + * @var string + */ + private $_current; + + /** + * _pos + * + * @var int + */ + private $_pos; + + /** + * _definitions + * + * @var array + */ + public $_definitions; + + /** + * @var array + */ + private $_hooks = array(); + + /** + * @var array + */ + private $_holders; + + /** + * @var string + */ + private $_uniqid; + + /** + * @var int + */ + private $_id; + + /** + * @var array + */ + private $_parsers = array(); + + /** + * makeHtml + * + * @param mixed $text + * @return string + */ + public function makeHtml($text) + { + $this->_footnotes = array(); + $this->_definitions = array(); + $this->_holders = array(); + $this->_uniqid = md5(uniqid()); + $this->_id = 0; + + usort($this->blockParsers, function ($a, $b) { + return $a[1] < $b[1] ? -1 : 1; + }); + + foreach ($this->blockParsers as $parser) { + list ($name) = $parser; + + if (isset($parser[2])) { + $this->_parsers[$name] = $parser[2]; + } else { + $this->_parsers[$name] = array($this, 'parseBlock' . ucfirst($name)); + } + } + + $text = $this->initText($text); + $html = $this->parse($text); + $html = $this->makeFootnotes($html); + $html = $this->optimizeLines($html); + + return $this->call('makeHtml', $html); + } + + /** + * @param $html + */ + public function enableHtml($html = true) + { + $this->_html = $html; + } + + /** + * @param bool $line + */ + public function enableLine($line = true) + { + $this->_line = $line; + } + + /** + * @param $type + * @param $callback + */ + public function hook($type, $callback) + { + $this->_hooks[$type][] = $callback; + } + + /** + * @param $str + * @return string + */ + public function makeHolder($str) + { + $key = "\r" . $this->_uniqid . $this->_id . "\r"; + $this->_id ++; + $this->_holders[$key] = $str; + + return $key; + } + + /** + * @param $text + * @return mixed + */ + private function initText($text) + { + $text = str_replace(array("\t", "\r"), array(' ', ''), $text); + return $text; + } + + /** + * @param $html + * @return string + */ + private function makeFootnotes($html) + { + if (count($this->_footnotes) > 0) { + $html .= '

    '; + $index = 1; + + while ($val = array_shift($this->_footnotes)) { + if (is_string($val)) { + $val .= " "; + } else { + $val[count($val) - 1] .= " "; + $val = count($val) > 1 ? $this->parse(implode("\n", $val)) : $this->parseInline($val[0]); + } + + $html .= "
  1. {$val}
  2. "; + $index ++; + } + + $html .= '
'; + } + + return $html; + } + + /** + * parse + * + * @param string $text + * @param bool $inline + * @param int $offset + * @return string + */ + private function parse($text, $inline = false, $offset = 0) + { + $blocks = $this->parseBlock($text, $lines); + $html = ''; + + // inline mode for single normal block + if ($inline && count($blocks) == 1 && $blocks[0][0] == 'normal') { + $blocks[0][3] = true; + } + + foreach ($blocks as $block) { + list ($type, $start, $end, $value) = $block; + $extract = array_slice($lines, $start, $end - $start + 1); + $method = 'parse' . ucfirst($type); + + $extract = $this->call('before' . ucfirst($method), $extract, $value); + $result = $this->{$method}($extract, $value, $start + $offset, $end + $offset); + $result = $this->call('after' . ucfirst($method), $result, $value); + + $html .= $result; + } + + return $html; + } + + /** + * @param $text + * @param $clearHolders + * @return string + */ + private function releaseHolder($text, $clearHolders = true) + { + $deep = 0; + while (strpos($text, "\r") !== false && $deep < 10) { + $text = str_replace(array_keys($this->_holders), array_values($this->_holders), $text); + $deep ++; + } + + if ($clearHolders) { + $this->_holders = array(); + } + + return $text; + } + + /** + * @param $start + * @param int $end + * @return string + */ + public function markLine($start, $end = -1) + { + if ($this->_line) { + $end = $end < 0 ? $start : $end; + return ''; + } + + return ''; + } + + /** + * @param array $lines + * @param $start + * @return string + */ + public function markLines(array $lines, $start) + { + $i = -1; + $self = $this; + + return $this->_line ? array_map(function ($line) use ($self, $start, &$i) { + $i ++; + return $self->markLine($start + $i) . $line; + }, $lines) : $lines; + } + + /** + * @param $html + * @return string + */ + public function optimizeLines($html) + { + $last = 0; + + return $this->_line ? + preg_replace_callback("/class=\"line\" data\-start=\"([0-9]+)\" data\-end=\"([0-9]+)\" (data\-id=\"{$this->_uniqid}\")/", + function ($matches) use (&$last) { + if ($matches[1] != $last) { + $replace = 'class="line" data-start="' . $last . '" data-start-original="' . $matches[1] . '" data-end="' . $matches[2] . '" ' . $matches[3]; + } else { + $replace = $matches[0]; + } + + $last = $matches[2] + 1; + return $replace; + }, $html) : $html; + } + + /** + * @param $type + * @param $value + * @return mixed + */ + public function call($type, $value) + { + if (empty($this->_hooks[$type])) { + return $value; + } + + $args = func_get_args(); + $args = array_slice($args, 1); + + foreach ($this->_hooks[$type] as $callback) { + $value = call_user_func_array($callback, $args); + $args[0] = $value; + } + + return $value; + } + + /** + * parseInline + * + * @param string $text + * @param string $whiteList + * @param bool $clearHolders + * @param bool $enableAutoLink + * @return string + */ + public function parseInline($text, $whiteList = '', $clearHolders = true, $enableAutoLink = true) + { + $self = $this; + $text = $this->call('beforeParseInline', $text); + + // code + $text = preg_replace_callback( + "/(^|[^\\\])(`+)(.+?)\\2/", + function ($matches) use ($self) { + return $matches[1] . $self->makeHolder( + '' . htmlspecialchars($matches[3]) . '' + ); + }, + $text + ); + + // mathjax + $text = preg_replace_callback( + "/(^|[^\\\])(\\$+)(.+?)\\2/", + function ($matches) use ($self) { + return $matches[1] . $self->makeHolder( + $matches[2] . htmlspecialchars($matches[3]) . $matches[2] + ); + }, + $text + ); + + // escape + $text = preg_replace_callback( + "/\\\(.)/u", + function ($matches) use ($self) { + $prefix = preg_match("/^[-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]$/", $matches[1]) ? '' : '\\'; + $escaped = htmlspecialchars($matches[1]); + $escaped = str_replace('$', '$', $escaped); + return $self->makeHolder($prefix . $escaped); + }, + $text + ); + + // link + $text = preg_replace_callback( + "/<(https?:\/\/.+)>/i", + function ($matches) use ($self) { + $url = $self->cleanUrl($matches[1]); + $link = $self->call('parseLink', $matches[1]); + + return $self->makeHolder( + "{$link}" + ); + }, + $text + ); + + // encode unsafe tags + $text = preg_replace_callback( + "/<(\/?)([a-z0-9-]+)(\s+[^>]*)?>/i", + function ($matches) use ($self, $whiteList) { + if ($self->_html || false !== stripos( + '|' . $self->_commonWhiteList . '|' . $whiteList . '|', '|' . $matches[2] . '|' + )) { + return $self->makeHolder($matches[0]); + } else { + return $self->makeHolder(htmlspecialchars($matches[0])); + } + }, + $text + ); + + if ($this->_html) { + $text = preg_replace_callback("//", function ($matches) use ($self) { + return $self->makeHolder($matches[0]); + }, $text); + } + + $text = str_replace(array('<', '>'), array('<', '>'), $text); + + // footnote + $text = preg_replace_callback( + "/\[\^((?:[^\]]|\\\\\]|\\\\\[)+?)\]/", + function ($matches) use ($self) { + $id = array_search($matches[1], $self->_footnotes); + + if (false === $id) { + $id = count($self->_footnotes) + 1; + $self->_footnotes[$id] = $self->parseInline($matches[1], '', false); + } + + return $self->makeHolder( + "{$id}" + ); + }, + $text + ); + + // image + $text = preg_replace_callback( + "/!\[((?:[^\]]|\\\\\]|\\\\\[)*?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/", + function ($matches) use ($self) { + $escaped = htmlspecialchars($self->escapeBracket($matches[1])); + $url = $self->escapeBracket($matches[2]); + $url = $self->cleanUrl($url); + return $self->makeHolder( + "\"{$escaped}\"" + ); + }, + $text + ); + + $text = preg_replace_callback( + "/!\[((?:[^\]]|\\\\\]|\\\\\[)*?)\]\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]/", + function ($matches) use ($self) { + $escaped = htmlspecialchars($self->escapeBracket($matches[1])); + + $result = isset( $self->_definitions[$matches[2]] ) ? + "_definitions[$matches[2]]}\" alt=\"{$escaped}\" title=\"{$escaped}\">" + : $escaped; + + return $self->makeHolder($result); + }, + $text + ); + + // link + $text = preg_replace_callback( + "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/", + function ($matches) use ($self) { + $escaped = $self->parseInline( + $self->escapeBracket($matches[1]), '', false, false + ); + $url = $self->escapeBracket($matches[2]); + $url = $self->cleanUrl($url); + return $self->makeHolder("{$escaped}"); + }, + $text + ); + + $text = preg_replace_callback( + "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]/", + function ($matches) use ($self) { + $escaped = $self->parseInline( + $self->escapeBracket($matches[1]), '', false + ); + $result = isset( $self->_definitions[$matches[2]] ) ? + "_definitions[$matches[2]]}\">{$escaped}" + : $escaped; + + return $self->makeHolder($result); + }, + $text + ); + + // strong and em and some fuck + $text = $this->parseInlineCallback($text); + $text = preg_replace( + "/<([_a-z0-9-\.\+]+@[^@]+\.[a-z]{2,})>/i", + "\\1", + $text + ); + + // autolink url + if ($enableAutoLink) { + $text = preg_replace_callback( + "/(^|[^\"])((https?):[\p{L}_0-9-\.\/%#!@\?\+=~\|\,&\(\)]+)($|[^\"])/iu", + function ($matches) use ($self) { + $link = $self->call('parseLink', $matches[2]); + return "{$matches[1]}{$link}{$matches[4]}"; + }, + $text + ); + } + + $text = $this->call('afterParseInlineBeforeRelease', $text); + $text = $this->releaseHolder($text, $clearHolders); + + $text = $this->call('afterParseInline', $text); + + return $text; + } + + /** + * @param $text + * @return mixed + */ + public function parseInlineCallback($text) + { + $self = $this; + + $text = preg_replace_callback( + "/(\*{3})(.+?)\\1/", + function ($matches) use ($self) { + return '' . + $self->parseInlineCallback($matches[2]) . + ''; + }, + $text + ); + + $text = preg_replace_callback( + "/(\*{2})(.+?)\\1/", + function ($matches) use ($self) { + return '' . + $self->parseInlineCallback($matches[2]) . + ''; + }, + $text + ); + + $text = preg_replace_callback( + "/(\*)(.+?)\\1/", + function ($matches) use ($self) { + return '' . + $self->parseInlineCallback($matches[2]) . + ''; + }, + $text + ); + + $text = preg_replace_callback( + "/(\s+|^)(_{3})(.+?)\\2(\s+|$)/", + function ($matches) use ($self) { + return $matches[1] . '' . + $self->parseInlineCallback($matches[3]) . + '' . $matches[4]; + }, + $text + ); + + $text = preg_replace_callback( + "/(\s+|^)(_{2})(.+?)\\2(\s+|$)/", + function ($matches) use ($self) { + return $matches[1] . '' . + $self->parseInlineCallback($matches[3]) . + '' . $matches[4]; + }, + $text + ); + + $text = preg_replace_callback( + "/(\s+|^)(_)(.+?)\\2(\s+|$)/", + function ($matches) use ($self) { + return $matches[1] . '' . + $self->parseInlineCallback($matches[3]) . + '' . $matches[4]; + }, + $text + ); + + $text = preg_replace_callback( + "/(~{2})(.+?)\\1/", + function ($matches) use ($self) { + return '' . + $self->parseInlineCallback($matches[2]) . + ''; + }, + $text + ); + + return $text; + } + + /** + * parseBlock + * + * @param string $text + * @param array $lines + * @return array + */ + private function parseBlock($text, &$lines) + { + $lines = explode("\n", $text); + $this->_blocks = array(); + $this->_current = 'normal'; + $this->_pos = -1; + + $state = array( + 'special' => implode("|", array_keys($this->_specialWhiteList)), + 'empty' => 0, + 'html' => false + ); + + // analyze by line + foreach ($lines as $key => $line) { + $block = $this->getBlock(); + $args = array($block, $key, $line, &$state, $lines); + + if ($this->_current != 'normal') { + $pass = call_user_func_array($this->_parsers[$this->_current], $args); + + if (!$pass) { + continue; + } + } + + foreach ($this->_parsers as $name => $parser) { + if ($name != $this->_current) { + $pass = call_user_func_array($parser, $args); + + if (!$pass) { + break; + } + } + } + } + + return $this->optimizeBlocks($this->_blocks, $lines); + } + + /** + * @param $block + * @param $key + * @param $line + * @param $state + * @return bool + */ + private function parseBlockList($block, $key, $line, &$state) + { + if ($this->isBlock('list') && !preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line)) { + if ($state['empty'] <= 1 + && preg_match("/^(\s+)/", $line, $matches) + && strlen($matches[1]) > $block[3]) { + + $state['empty'] = 0; + $this->setBlock($key); + return false; + } else if (preg_match("/^(\s*)$/", $line) && $state['empty'] == 0) { + $state['empty'] ++; + $this->setBlock($key); + return false; + } + } + + if (preg_match("/^(\s*)((?:[0-9]+\.)|\-|\+|\*)\s+/i", $line, $matches)) { + $space = strlen($matches[1]); + $state['empty'] = 0; + + // opened + if ($this->isBlock('list')) { + $this->setBlock($key, $space); + } else { + $this->startBlock('list', $key, $space); + } + + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @return bool + */ + private function parseBlockCode($block, $key, $line) + { + if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line, $matches)) { + if ($this->isBlock('code')) { + $isAfterList = $block[3][2]; + + if ($isAfterList) { + $this->combineBlock() + ->setBlock($key); + } else { + $this->setBlock($key) + ->endBlock(); + } + } else { + $isAfterList = false; + + if ($this->isBlock('list')) { + $space = $block[3]; + + $isAfterList = ($space > 0 && strlen($matches[1]) >= $space) + || strlen($matches[1]) > $space; + } + + $this->startBlock('code', $key, array( + $matches[1], $matches[3], $isAfterList + )); + } + + return false; + } else if ($this->isBlock('code')) { + $this->setBlock($key); + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @param $state + * @return bool + */ + private function parseBlockShtml($block, $key, $line, &$state) + { + if ($this->_html) { + if (preg_match("/^(\s*)!!!(\s*)$/", $line, $matches)) { + if ($this->isBlock('shtml')) { + $this->setBlock($key)->endBlock(); + } else { + $this->startBlock('shtml', $key); + } + + return false; + } else if ($this->isBlock('shtml')) { + $this->setBlock($key); + return false; + } + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @param $state + * @return bool + */ + private function parseBlockAhtml($block, $key, $line, &$state) + { + if ($this->_html) { + if (preg_match("/^\s*<({$this->_blockHtmlTags})(\s+[^>]*)?>/i", $line, $matches)) { + if ($this->isBlock('ahtml')) { + $this->setBlock($key); + return false; + } else if (empty($matches[2]) || $matches[2] != '/') { + $this->startBlock('ahtml', $key); + preg_match_all("/<({$this->_blockHtmlTags})(\s+[^>]*)?>/i", $line, $allMatches); + $lastMatch = $allMatches[1][count($allMatches[0]) - 1]; + + if (strpos($line, "") !== false) { + $this->endBlock(); + } else { + $state['html'] = $lastMatch; + } + return false; + } + } else if (!!$state['html'] && strpos($line, "") !== false) { + $this->setBlock($key)->endBlock(); + $state['html'] = false; + return false; + } else if ($this->isBlock('ahtml')) { + $this->setBlock($key); + return false; + } else if (preg_match("/^\s*\s*$/", $line, $matches)) { + $this->startBlock('ahtml', $key)->endBlock(); + return false; + } + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @return bool + */ + private function parseBlockMath($block, $key, $line) + { + if (preg_match("/^(\s*)\\$\\$(\s*)$/", $line, $matches)) { + if ($this->isBlock('math')) { + $this->setBlock($key)->endBlock(); + } else { + $this->startBlock('math', $key); + } + + return false; + } else if ($this->isBlock('math')) { + $this->setBlock($key); + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @param $state + * @return bool + */ + private function parseBlockPre($block, $key, $line, &$state) + { + if (preg_match("/^ {4}/", $line)) { + if ($this->isBlock('pre')) { + $this->setBlock($key); + } else { + $this->startBlock('pre', $key); + } + + return false; + } else if ($this->isBlock('pre') && preg_match("/^\s*$/", $line)) { + $this->setBlock($key); + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @param $state + * @return bool + */ + private function parseBlockHtml($block, $key, $line, &$state) + { + if (preg_match("/^\s*<({$state['special']})(\s+[^>]*)?>/i", $line, $matches)) { + $tag = strtolower($matches[1]); + if (!$this->isBlock('html', $tag) && !$this->isBlock('pre')) { + $this->startBlock('html', $key, $tag); + } + + return false; + } else if (preg_match("/<\/({$state['special']})>\s*$/i", $line, $matches)) { + $tag = strtolower($matches[1]); + + if ($this->isBlock('html', $tag)) { + $this->setBlock($key) + ->endBlock(); + } + + return false; + } else if ($this->isBlock('html')) { + $this->setBlock($key); + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @return bool + */ + private function parseBlockFootnote($block, $key, $line) + { + if (preg_match("/^\[\^((?:[^\]]|\\]|\\[)+?)\]:/", $line, $matches)) { + $space = strlen($matches[0]) - 1; + $this->startBlock('footnote', $key, array( + $space, $matches[1] + )); + + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @return bool + */ + private function parseBlockDefinition($block, $key, $line) + { + if (preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line, $matches)) { + $this->_definitions[$matches[1]] = $this->cleanUrl($matches[2]); + $this->startBlock('definition', $key) + ->endBlock(); + + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @return bool + */ + private function parseBlockQuote($block, $key, $line) + { + if (preg_match("/^(\s*)>/", $line, $matches)) { + if ($this->isBlock('list') && strlen($matches[1]) > 0) { + $this->setBlock($key); + } else if ($this->isBlock('quote')) { + $this->setBlock($key); + } else { + $this->startBlock('quote', $key); + } + + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @param $state + * @param $lines + * @return bool + */ + private function parseBlockTable($block, $key, $line, &$state, $lines) + { + if (preg_match("/^((?:(?:(?:\||\+)(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:[ :]*\-+[ :]*)(?:\||\+)(?:[ :]*\-+[ :]*))|(?:(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:\||\+)(?:[ :]*\-+[ :]*)))+)$/", $line, $matches)) { + if ($this->isBlock('table')) { + $block[3][0][] = $block[3][2]; + $block[3][2]++; + $this->setBlock($key, $block[3]); + } else { + $head = 0; + + if (empty($block) || + $block[0] != 'normal' || + preg_match("/^\s*$/", $lines[$block[2]])) { + $this->startBlock('table', $key); + } else { + $head = 1; + $this->backBlock(1, 'table'); + } + + if ($matches[1][0] == '|') { + $matches[1] = substr($matches[1], 1); + + if ($matches[1][strlen($matches[1]) - 1] == '|') { + $matches[1] = substr($matches[1], 0, -1); + } + } + + $rows = preg_split("/(\+|\|)/", $matches[1]); + $aligns = array(); + foreach ($rows as $row) { + $align = 'none'; + + if (preg_match("/^\s*(:?)\-+(:?)\s*$/", $row, $matches)) { + if (!empty($matches[1]) && !empty($matches[2])) { + $align = 'center'; + } else if (!empty($matches[1])) { + $align = 'left'; + } else if (!empty($matches[2])) { + $align = 'right'; + } + } + + $aligns[] = $align; + } + + $this->setBlock($key, array(array($head), $aligns, $head + 1)); + } + + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @return bool + */ + private function parseBlockSh($block, $key, $line) + { + if (preg_match("/^(#+)(.*)$/", $line, $matches)) { + $num = min(strlen($matches[1]), 6); + $this->startBlock('sh', $key, $num) + ->endBlock(); + + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @param $state + * @param $lines + * @return bool + */ + private function parseBlockMh($block, $key, $line, &$state, $lines) + { + if (preg_match("/^\s*((=|-){2,})\s*$/", $line, $matches) + && ($block && $block[0] == "normal" && !preg_match("/^\s*$/", $lines[$block[2]]))) { // check if last line isn't empty + if ($this->isBlock('normal')) { + $this->backBlock(1, 'mh', $matches[1][0] == '=' ? 1 : 2) + ->setBlock($key) + ->endBlock(); + } else { + $this->startBlock('normal', $key); + } + + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @return bool + */ + private function parseBlockShr($block, $key, $line) + { + if (preg_match("/^(\* *){3,}\s*$/", $line)) { + $this->startBlock('hr', $key) + ->endBlock(); + + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @return bool + */ + private function parseBlockDhr($block, $key, $line) + { + if (preg_match("/^(- *){3,}\s*$/", $line)) { + $this->startBlock('hr', $key) + ->endBlock(); + + return false; + } + + return true; + } + + /** + * @param $block + * @param $key + * @param $line + * @param $state + * @return bool + */ + private function parseBlockDefault($block, $key, $line, &$state) + { + if ($this->isBlock('footnote')) { + preg_match("/^(\s*)/", $line, $matches); + if (strlen($matches[1]) >= $block[3][0]) { + $this->setBlock($key); + } else { + $this->startBlock('normal', $key); + } + } else if ($this->isBlock('table')) { + if (false !== strpos($line, '|')) { + $block[3][2] ++; + $this->setBlock($key, $block[3]); + } else { + $this->startBlock('normal', $key); + } + } else if ($this->isBlock('quote')) { + if (!preg_match("/^(\s*)$/", $line)) { // empty line + $this->setBlock($key); + } else { + $this->startBlock('normal', $key); + } + } else { + if (empty($block) || $block[0] != 'normal') { + $this->startBlock('normal', $key); + } else { + $this->setBlock($key); + } + } + + return true; + } + + /** + * @param array $blocks + * @param array $lines + * @return array + */ + private function optimizeBlocks(array $blocks, array $lines) + { + $blocks = $this->call('beforeOptimizeBlocks', $blocks, $lines); + + $key = 0; + while (isset($blocks[$key])) { + $moved = false; + + $block = &$blocks[$key]; + $prevBlock = isset($blocks[$key - 1]) ? $blocks[$key - 1] : NULL; + $nextBlock = isset($blocks[$key + 1]) ? $blocks[$key + 1] : NULL; + + list ($type, $from, $to) = $block; + + if ('pre' == $type) { + $isEmpty = array_reduce( + array_slice($lines, $block[1], $block[2] - $block[1] + 1), + function ($result, $line) { + return preg_match("/^\s*$/", $line) && $result; + }, + true + ); + + if ($isEmpty) { + $block[0] = $type = 'normal'; + } + } + + if ('normal' == $type) { + // combine two blocks + $types = array('list', 'quote'); + + if ($from == $to && preg_match("/^\s*$/", $lines[$from]) + && !empty($prevBlock) && !empty($nextBlock)) { + if ($prevBlock[0] == $nextBlock[0] && in_array($prevBlock[0], $types)) { + // combine 3 blocks + $blocks[$key - 1] = array( + $prevBlock[0], $prevBlock[1], $nextBlock[2], NULL + ); + array_splice($blocks, $key, 2); + + // do not move + $moved = true; + } + } + } + + if (!$moved) { + $key ++; + } + } + + return $this->call('afterOptimizeBlocks', $blocks, $lines); + } + + /** + * parseCode + * + * @param array $lines + * @param array $parts + * @param int $start + * @return string + */ + private function parseCode(array $lines, array $parts, $start) + { + list ($blank, $lang) = $parts; + $lang = trim($lang); + $count = strlen($blank); + + if (!preg_match("/^[_a-z0-9-\+\#\:\.]+$/i", $lang)) { + $lang = NULL; + } else { + $parts = explode(':', $lang); + if (count($parts) > 1) { + list ($lang, $rel) = $parts; + $lang = trim($lang); + $rel = trim($rel); + } + } + + $isEmpty = true; + + $lines = array_map(function ($line) use ($count, &$isEmpty) { + $line = preg_replace("/^[ ]{{$count}}/", '', $line); + if ($isEmpty && !preg_match("/^\s*$/", $line)) { + $isEmpty = false; + } + + return htmlspecialchars($line); + }, array_slice($lines, 1, -1)); + $str = implode("\n", $this->markLines($lines, $start + 1)); + + return $isEmpty ? '' : + '
'
+            . $str . '
'; + } + + /** + * parsePre + * + * @param array $lines + * @param mixed $value + * @param int $start + * @return string + */ + private function parsePre(array $lines, $value, $start) + { + foreach ($lines as &$line) { + $line = htmlspecialchars(substr($line, 4)); + } + + $str = implode("\n", $this->markLines($lines, $start)); + return preg_match("/^\s*$/", $str) ? '' : '
' . $str . '
'; + } + + /** + * parseAhtml + * + * @param array $lines + * @param mixed $value + * @param int $start + * @return string + */ + private function parseAhtml(array $lines, $value, $start) + { + return trim(implode("\n", $this->markLines($lines, $start))); + } + + /** + * parseShtml + * + * @param array $lines + * @param mixed $value + * @param int $start + * @return string + */ + private function parseShtml(array $lines, $value, $start) + { + return trim(implode("\n", $this->markLines(array_slice($lines, 1, -1), $start + 1))); + } + + /** + * parseMath + * + * @param array $lines + * @param mixed $value + * @param int $start + * @param int $end + * @return string + */ + private function parseMath(array $lines, $value, $start, $end) + { + return '

' . $this->markLine($start, $end) . htmlspecialchars(implode("\n", $lines)) . '

'; + } + + /** + * parseSh + * + * @param array $lines + * @param int $num + * @param int $start + * @param int $end + * @return string + */ + private function parseSh(array $lines, $num, $start, $end) + { + $line = $this->markLine($start, $end) . $this->parseInline(trim($lines[0], '# ')); + return preg_match("/^\s*$/", $line) ? '' : "{$line}"; + } + + /** + * parseMh + * + * @param array $lines + * @param int $num + * @param int $start + * @param int $end + * @return string + */ + private function parseMh(array $lines, $num, $start, $end) + { + return $this->parseSh($lines, $num, $start, $end); + } + + /** + * parseQuote + * + * @param array $lines + * @param mixed $value + * @param int $start + * @return string + */ + private function parseQuote(array $lines, $value, $start) + { + foreach ($lines as &$line) { + $line = preg_replace("/^\s*> ?/", '', $line); + } + $str = implode("\n", $lines); + + return preg_match("/^\s*$/", $str) ? '' : '
' . $this->parse($str, true, $start) . '
'; + } + + /** + * parseList + * + * @param array $lines + * @param mixed $value + * @param int $start + * @return string + */ + private function parseList(array $lines, $value, $start) + { + $html = ''; + $minSpace = 99999; + $secondMinSpace = 99999; + $found = false; + $secondFound = false; + $rows = array(); + + // count levels + foreach ($lines as $key => $line) { + if (preg_match("/^(\s*)((?:[0-9]+\.?)|\-|\+|\*)(\s+)(.*)$/i", $line, $matches)) { + $space = strlen($matches[1]); + $type = false !== strpos('+-*', $matches[2]) ? 'ul' : 'ol'; + $minSpace = min($space, $minSpace); + $found = true; + + if ($space > 0) { + $secondMinSpace = min($space, $secondMinSpace); + $secondFound = true; + } + + $rows[] = array($space, $type, $line, $matches[4]); + } else { + $rows[] = $line; + + if (preg_match("/^(\s*)/", $line, $matches)) { + $space = strlen($matches[1]); + + if ($space > 0) { + $secondMinSpace = min($space, $secondMinSpace); + $secondFound = true; + } + } + } + } + + $minSpace = $found ? $minSpace : 0; + $secondMinSpace = $secondFound ? $secondMinSpace : $minSpace; + + $lastType = ''; + $leftLines = array(); + $leftStart = 0; + + foreach ($rows as $key => $row) { + if (is_array($row)) { + list ($space, $type, $line, $text) = $row; + + if ($space != $minSpace) { + $leftLines[] = preg_replace("/^\s{" . $secondMinSpace . "}/", '', $line); + } else { + if (!empty($leftLines)) { + $html .= "
  • " . $this->parse(implode("\n", $leftLines), true, $start + $leftStart) . "
  • "; + } + + if ($lastType != $type) { + if (!empty($lastType)) { + $html .= ""; + } + + $html .= "<{$type}>"; + } + + $leftStart = $key; + $leftLines = array($text); + $lastType = $type; + } + } else { + $leftLines[] = preg_replace("/^\s{" . $secondMinSpace . "}/", '', $row); + } + } + + if (!empty($leftLines)) { + $html .= "
  • " . $this->parse(implode("\n", $leftLines), true, $start + $leftStart) . "
  • "; + } + + return $html; + } + + /** + * @param array $lines + * @param array $value + * @param int $start + * @return string + */ + private function parseTable(array $lines, array $value, $start) + { + list ($ignores, $aligns) = $value; + $head = count($ignores) > 0 && array_sum($ignores) > 0; + + $html = ''; + $body = $head ? NULL : true; + $output = false; + + foreach ($lines as $key => $line) { + if (in_array($key, $ignores)) { + if ($head && $output) { + $head = false; + $body = true; + } + + continue; + } + + $line = trim($line); + $output = true; + + if ($line[0] == '|') { + $line = substr($line, 1); + + if ($line[strlen($line) - 1] == '|') { + $line = substr($line, 0, -1); + } + } + + + $rows = array_map(function ($row) { + if (preg_match("/^\s*$/", $row)) { + return ' '; + } else { + return trim($row); + } + }, explode('|', $line)); + $columns = array(); + $last = -1; + + foreach ($rows as $row) { + if (strlen($row) > 0) { + $last ++; + $columns[$last] = array( + isset($columns[$last]) ? $columns[$last][0] + 1 : 1, $row + ); + } else if (isset($columns[$last])) { + $columns[$last][0] ++; + } else { + $columns[0] = array(1, $row); + } + } + + if ($head) { + $html .= ''; + } else if ($body) { + $html .= ''; + } + + $html .= '_line ? ' class="line" data-start="' + . ($start + $key) . '" data-end="' . ($start + $key) + . '" data-id="' . $this->_uniqid . '"' : '') . '>'; + + foreach ($columns as $key => $column) { + list ($num, $text) = $column; + $tag = $head ? 'th' : 'td'; + + $html .= "<{$tag}"; + if ($num > 1) { + $html .= " colspan=\"{$num}\""; + } + + if (isset($aligns[$key]) && $aligns[$key] != 'none') { + $html .= " align=\"{$aligns[$key]}\""; + } + + $html .= '>' . $this->parseInline($text) . ""; + } + + $html .= ''; + + if ($head) { + $html .= ''; + } else if ($body) { + $body = false; + } + } + + if ($body !== NULL) { + $html .= ''; + } + + $html .= '
    '; + return $html; + } + + /** + * parseHr + * + * @param array $lines + * @param array $value + * @param int $start + * @return string + */ + private function parseHr($lines, $value, $start) + { + return $this->_line ? '
    ' : '
    '; + } + + /** + * parseNormal + * + * @param array $lines + * @param bool $inline + * @param int $start + * @return string + */ + private function parseNormal(array $lines, $inline = false, $start) + { + foreach ($lines as $key => &$line) { + $line = $this->parseInline($line); + + if (!preg_match("/^\s*$/", $line)) { + $line = $this->markLine($start + $key) . $line; + } + } + + $str = trim(implode("\n", $lines)); + $str = preg_replace("/(\n\s*){2,}/", "

    ", $str); + $str = preg_replace("/\n/", "
    ", $str); + + return preg_match("/^\s*$/", $str) ? '' : ($inline ? $str : "

    {$str}

    "); + } + + /** + * parseFootnote + * + * @param array $lines + * @param array $value + * @return string + */ + private function parseFootnote(array $lines, array $value) + { + list($space, $note) = $value; + $index = array_search($note, $this->_footnotes); + + if (false !== $index) { + $lines[0] = preg_replace("/^\[\^((?:[^\]]|\\]|\\[)+?)\]:/", '', $lines[0]); + $this->_footnotes[$index] = $lines; + } + + return ''; + } + + /** + * parseDefine + * + * @return string + */ + private function parseDefinition() + { + return ''; + } + + /** + * parseHtml + * + * @param array $lines + * @param string $type + * @param int $start + * @return string + */ + private function parseHtml(array $lines, $type, $start) + { + foreach ($lines as &$line) { + $line = $this->parseInline($line, + isset($this->_specialWhiteList[$type]) ? $this->_specialWhiteList[$type] : ''); + } + + return implode("\n", $this->markLines($lines, $start)); + } + + /** + * @param $url + * @return string + */ + public function cleanUrl($url) + { + if (preg_match("/^\s*((http|https|ftp|mailto):[\p{L}_a-z0-9-:\.\*\/%#;!@\?\+=~\|\,&\(\)]+)/iu", $url, $matches)) { + return $matches[1]; + } else if (preg_match("/^\s*([\p{L}_a-z0-9-:\.\*\/%#!@\?\+=~\|\,&]+)/iu", $url, $matches)) { + return $matches[1]; + } else { + return '#'; + } + } + + /** + * @param $str + * @return mixed + */ + public function escapeBracket($str) + { + return str_replace( + array('\[', '\]', '\(', '\)'), array('[', ']', '(', ')'), $str + ); + } + + /** + * startBlock + * + * @param mixed $type + * @param mixed $start + * @param mixed $value + * @return $this + */ + private function startBlock($type, $start, $value = NULL) + { + $this->_pos ++; + $this->_current = $type; + + $this->_blocks[$this->_pos] = array($type, $start, $start, $value); + + return $this; + } + + /** + * endBlock + * + * @return $this + */ + private function endBlock() + { + $this->_current = 'normal'; + return $this; + } + + /** + * isBlock + * + * @param mixed $type + * @param mixed $value + * @return bool + */ + private function isBlock($type, $value = NULL) + { + return $this->_current == $type + && (NULL === $value ? true : $this->_blocks[$this->_pos][3] == $value); + } + + /** + * getBlock + * + * @return array + */ + private function getBlock() + { + return isset($this->_blocks[$this->_pos]) ? $this->_blocks[$this->_pos] : NULL; + } + + /** + * setBlock + * + * @param mixed $to + * @param mixed $value + * @return $this + */ + private function setBlock($to = NULL, $value = NULL) + { + if (NULL !== $to) { + $this->_blocks[$this->_pos][2] = $to; + } + + if (NULL !== $value) { + $this->_blocks[$this->_pos][3] = $value; + } + + return $this; + } + + /** + * backBlock + * + * @param mixed $step + * @param mixed $type + * @param mixed $value + * @return $this + */ + private function backBlock($step, $type, $value = NULL) + { + if ($this->_pos < 0) { + return $this->startBlock($type, 0, $value); + } + + $last = $this->_blocks[$this->_pos][2]; + $this->_blocks[$this->_pos][2] = $last - $step; + + if ($this->_blocks[$this->_pos][1] <= $this->_blocks[$this->_pos][2]) { + $this->_pos ++; + } + + $this->_current = $type; + $this->_blocks[$this->_pos] = array( + $type, $last - $step + 1, $last, $value + ); + + return $this; + } + + /** + * @return $this + */ + private function combineBlock() + { + if ($this->_pos < 1) { + return $this; + } + + $prev = $this->_blocks[$this->_pos - 1]; + $current = $this->_blocks[$this->_pos]; + + $prev[2] = $current[2]; + $this->_blocks[$this->_pos - 1] = $prev; + $this->_current = $prev[0]; + unset($this->_blocks[$this->_pos]); + $this->_pos --; + + return $this; + } } diff --git a/Lib/main.php b/Lib/main.php index b3d9bbc..256a2ca 100644 --- a/Lib/main.php +++ b/Lib/main.php @@ -1 +1,14 @@ php 7.4 + +> +>composer 1.9.1 + -1. xxxx -2. xxxx -3. xxxx +接下来开始安装项目。 -#### 参与贡献 +- 访问[github](https://github.com/xtzero/meoBlog)的`main`分支,或[gitee](https://gitee.com/xtzero/meoBlog)的`master`分支,克隆项目到本地。 -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +```shell script +$ git clone https://gitee.com/xtzero/meoBlog.git +//或 git clone https://github.com/xtzero/meoBlog.git +``` +- 使用composer安装依赖。 + +```shell script +$ composer install -vvv +``` + +- 使用`meo`命令生成页面,开启服务。 + +```shell script +$ php meo s + +[Sun Jan 17 20:18:29 2021] PHP 7.4.1 Development Server (http://localhost:8888) started +``` + +- 点击控制台显示的地址,访问页面。 + +http://localhost:8888 + +> 文档约定 +> +>在本文档中,使用`/Volumes/proj/meoBlog`当做项目的根目录。 +> +>在本文档中,`$`表示终端提示符,`$`后是执行的命令,后面的行则是命令执行之后的回显。 +> +>在本文档中,会使用`[name]`来包含一个可选参数,使用``来包含一个必选参数。中间的单词请根据上下文或单词本身的意义来解读。 + +### 新建文章 + +使用`meo`命令可以为你新建文章 + +```shell script +$ php meo new post [post_name] +Create file success: /Volumes/proj/meoBlog/Sources/Posts/template/index.md +``` + +根据控制台输出可见,新建的markdown文档已经生成好了。你可以编辑`/Volumes/proj/meoBlog/Sources/Posts/[post_name]/index.md`来编写新的文章。 + +有关于文章内容的编辑问题,且等介绍完新建页面之后叙述。 + +### 新建页面 + +在`meo-blog`中,页面是独立于文章之外的独立页面,不会显示在文章列表页面中。 + +和新建文章一样,你也可以使用`meo`命令来新建页面。 + +```shell script +$ php meo new page [page_name] +Create file success: /Volumes/proj/meoBlog/Sources/Pages/page1/index.md +``` + +你可以编辑`/Volumes/proj/meoBlog/Sources/Pages/[page_name]/index.md`来编写新页面的内容。 + +### 编写文章和页面内容 + +使用`meo new`命令新建的文章或页面,都会有如下默认内容: +```html + +``` + +这部分叫做文章/页面的**注释部分**,使用html注释语法包含。默认模板只能解析到`title`和`datetime`,如果想要解析更多内容,可以通过重写模板来实现。 + +其中,`[default_title]`会被当成文章/页面标题,显示在内容页的顶部;`[create_datetime]`会被当成文章/页面的新建时间,放在标题下面。 + +对于文章,文章列表页会显示标题和创建日期,并且会按照创建日期排序。 + +> 目前(pre-release 1)还不会按创建日期排序(忘了) + +除了注释部分,其余都是正文部分。正文支持`markdown`语法,使用[SegmentFault/HyperDown](https://github.com/SegmentFault/HyperDown)进行解析。除此之外,还支持传统的html语法。也就是说你可以在正文部分通过写html标签来实现更多定制功能。 + +### 生成静态页 + +使用如下`meo`命令来生成静态页。 + +```shell script +$ php meo b +Load theme from path: /Volumes/proj/meoBlog/Templates/ +Load theme success! +Build index page... +Build archives... +Build posts... +... +Build pages... +... +Build finish! +See at /Volumes/proj/meoBlog/Dist +``` + +生成的静态页内容是项目路径下的`Dist`目录,你可以直接访问文件来查看页面效果。 + +### 访问页面 + +你可以使用`meo`命令来生成静态页并开启php开发服务器来查看页面效果。 + +```shell script +$ php meo s +[Sun Jan 17 20:53:02 2021] PHP 7.4.1 Development Server (http://localhost:8888) started +``` + +访问显示的地址,可以查看生成的结果。 + +> `php meo s`命令还未支持实时刷新。 + +### 发布 + +你可以将`Dist`路径完整复制到服务器目录,或将网站根路径指向`Dist`目录的位置,即可发布你的网站。 + +## 高级 + +### 配置 + +配置文件是`/env.php`,你可以编辑你的个性化配置。 + +|配置项|解释| +|:--:|:--:| +|title|网站主标题| +|subtitle|网站副标题| +|develop_server_host|开发服务地址| +|develop_server_port|开发服务端口| + +在开发中,任何位置都可以通过访问`ENV`来使用`env.php`中的值。例如 + +```php +// 取网站主标题 +$title = ENV['title']; +``` + +### 全局变量 + +全局变量都在`/meo`中定义。`/meo`是一个php文件,为了命令简洁,所以省略了用户名。这样做才可以使用`php meo `来执行操作。 + +|全局变量|解释| +|:--:|:--:| +|ENV|`/env.php`返回的数组| +|THEME_CONFIG|主题配置,通常是`/Templates/_config.php`| +|ROOT_DIR|项目根目录| +|DIST_DIR|静态文件目录,通常是`/Dist`| +|SOURCES_DIR|源文件目录,通常是`/Sources`| +|POST_DIR|文章源文件目录,通常是`/Sources/Pages`| +|PAGE_DIR|页面源文件目录,通常是`/Sources/Pages`| +|THEME_BASE_DIR|模板根目录,通常是`/Templates`| + +### `meo`命令 + +你可以使用`php meo help`来查看所有支持的`meo`命令。 +```shell script +$ php meo h + +Welcome to use meo blog! +This is a blog system written by a white cat's father! + +Usage: php meo \ +Here are all of commands: + +- php meo help | h: Get help(just like you now!) +- php meo new \ \: Create a new post +- php meo build | b: Build static files +- php meo serve | s: Start develop server, and check page render when file change +- php meo clean | c: Clean static files and db file + +If you want join us, visit https://gitee.com/xtzero/meoBlog. + +Enjoy yourself~ +``` + +除此之外,你可以定制自己的`meo`命令来实现一些自定义操作。自定义命令写在`/Cmd`中,一个命令文件以大驼峰命名,需要以`Cmd.php`结尾。类名与文件名相同,而且需要实现`Lib\Base\BaseCmd`接口。例如: + +```php +Template`命名,且类名和文件名相同,且实现自接口`Lib\Base\BaseTemplate`。 + +对于`meo-blog`,最低要求是需要有Archives、Index、Page、Post,这四个模板在`BuildCmd`中使用了。如果需要增加新的页面渲染,需要修改`BuildCmd`,并在`Templates`里增加新的渲染逻辑。 + + +### features + +> 这里写的是一些准备要做的事 + ++ 初始模板 ++ 文章列表排序 -#### 特技 -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/Sources/Pages/about/index.md b/Sources/Pages/about/index.md new file mode 100644 index 0000000..7c638fb --- /dev/null +++ b/Sources/Pages/about/index.md @@ -0,0 +1,10 @@ + + +## 关于 + +这是一个关于页面,由于主页有这个页面的超链接,所以写了这么一个页面充数。 + + diff --git a/Sources/Pages/doc/index.md b/Sources/Pages/doc/index.md new file mode 100644 index 0000000..b14b78c --- /dev/null +++ b/Sources/Pages/doc/index.md @@ -0,0 +1,236 @@ + +``` + _ _ + _ __ ___ ___ ___ | |__ | | ___ __ _ +| '_ ` _ \ / _ \/ _ \ | '_ \| |/ _ \ / _` | +| | | | | | \__/ (_) | | |_) | | (_) | (_| | +|_| |_| |_|\___|\___/ |_.__/|_|\___/ \__, | + |___/ +``` +## 基础使用 + +### 安装并快速开始 + +在安装前,你需要先准备好必要的环境。 + +> php 7.4 + +> +>composer 1.9.1 + + +接下来开始安装项目。 + +- 访问[github](https://github.com/xtzero/meoBlog)的`main`分支,或[gitee](https://gitee.com/xtzero/meoBlog)的`master`分支,克隆项目到本地。 + +```shell script +$ git clone https://gitee.com/xtzero/meoBlog.git +//或 git clone https://github.com/xtzero/meoBlog.git +``` + +- 使用composer安装依赖。 + +```shell script +$ composer install -vvv +``` + +- 使用`meo`命令生成页面,开启服务。 + +```shell script +$ php meo s + +[Sun Jan 17 20:18:29 2021] PHP 7.4.1 Development Server (http://localhost:8888) started +``` + +- 点击控制台显示的地址,访问页面。 + +http://localhost:8888 + +> 文档约定 +> +>在本文档中,使用`/Volumes/proj/meoBlog`当做项目的根目录。 +> +>在本文档中,`$`表示终端提示符,`$`后是执行的命令,后面的行则是命令执行之后的回显。 +> +>在本文档中,会使用`[name]`来包含一个可选参数,使用``来包含一个必选参数。中间的单词请根据上下文或单词本身的意义来解读。 + +### 新建文章 + +使用`meo`命令可以为你新建文章 + +```shell script +$ php meo new post [post_name] +Create file success: /Volumes/proj/meoBlog/Sources/Posts/template/index.md +``` + +根据控制台输出可见,新建的markdown文档已经生成好了。你可以编辑`/Volumes/proj/meoBlog/Sources/Posts/[post_name]/index.md`来编写新的文章。 + +有关于文章内容的编辑问题,且等介绍完新建页面之后叙述。 + +### 新建页面 + +在`meo-blog`中,页面是独立于文章之外的独立页面,不会显示在文章列表页面中。 + +和新建文章一样,你也可以使用`meo`命令来新建页面。 + +```shell script +$ php meo new page [page_name] +Create file success: /Volumes/proj/meoBlog/Sources/Pages/page1/index.md +``` + +你可以编辑`/Volumes/proj/meoBlog/Sources/Pages/[page_name]/index.md`来编写新页面的内容。 + +### 编写文章和页面内容 + +使用`meo new`命令新建的文章或页面,都会有如下默认内容: +```html + +``` + +这部分叫做文章/页面的**注释部分**,使用html注释语法包含。默认模板只能解析到`title`和`datetime`,如果想要解析更多内容,可以通过重写模板来实现。 + +其中,`[default_title]`会被当成文章/页面标题,显示在内容页的顶部;`[create_datetime]`会被当成文章/页面的新建时间,放在标题下面。 + +对于文章,文章列表页会显示标题和创建日期,并且会按照创建日期排序。 + +> 目前(pre-release 1)还不会按创建日期排序(忘了) + +除了注释部分,其余都是正文部分。正文支持`markdown`语法,使用[SegmentFault/HyperDown](https://github.com/SegmentFault/HyperDown)进行解析。除此之外,还支持传统的html语法。也就是说你可以在正文部分通过写html标签来实现更多定制功能。 + +### 生成静态页 + +使用如下`meo`命令来生成静态页。 + +```shell script +$ php meo b +Load theme from path: /Volumes/proj/meoBlog/Templates/ +Load theme success! +Build index page... +Build archives... +Build posts... +... +Build pages... +... +Build finish! +See at /Volumes/proj/meoBlog/Dist +``` + +生成的静态页内容是项目路径下的`Dist`目录,你可以直接访问文件来查看页面效果。 + +### 访问页面 + +你可以使用`meo`命令来生成静态页并开启php开发服务器来查看页面效果。 + +```shell script +$ php meo s +[Sun Jan 17 20:53:02 2021] PHP 7.4.1 Development Server (http://localhost:8888) started +``` + +访问显示的地址,可以查看生成的结果。 + +> `php meo s`命令还未支持实时刷新。 + +### 发布 + +你可以将`Dist`路径完整复制到服务器目录,或将网站根路径指向`Dist`目录的位置,即可发布你的网站。 + +## 高级 + +### 配置 + +配置文件是`/env.php`,你可以编辑你的个性化配置。 + +|配置项|解释| +|:--:|:--:| +|title|网站主标题| +|subtitle|网站副标题| +|develop_server_host|开发服务地址| +|develop_server_port|开发服务端口| + +在开发中,任何位置都可以通过访问`ENV`来使用`env.php`中的值。例如 + +```php +// 取网站主标题 +$title = ENV['title']; +``` + +### 全局变量 + +全局变量都在`/meo`中定义。`/meo`是一个php文件,为了命令简洁,所以省略了用户名。这样做才可以使用`php meo `来执行操作。 + +|全局变量|解释| +|:--:|:--:| +|ENV|`/env.php`返回的数组| +|THEME_CONFIG|主题配置,通常是`/Templates/_config.php`| +|ROOT_DIR|项目根目录| +|DIST_DIR|静态文件目录,通常是`/Dist`| +|SOURCES_DIR|源文件目录,通常是`/Sources`| +|POST_DIR|文章源文件目录,通常是`/Sources/Pages`| +|PAGE_DIR|页面源文件目录,通常是`/Sources/Pages`| +|THEME_BASE_DIR|模板根目录,通常是`/Templates`| + +### `meo`命令 + +你可以使用`php meo help`来查看所有支持的`meo`命令。 +```shell script +$ php meo h + +Welcome to use meo blog! +This is a blog system written by a white cat's father! + +Usage: php meo \ +Here are all of commands: + +- php meo help | h: Get help(just like you now!) +- php meo new \ \: Create a new post +- php meo build | b: Build static files +- php meo serve | s: Start develop server, and check page render when file change +- php meo clean | c: Clean static files and db file + +If you want join us, visit https://gitee.com/xtzero/meoBlog. + +Enjoy yourself~ +``` + +除此之外,你可以定制自己的`meo`命令来实现一些自定义操作。自定义命令写在`/Cmd`中,一个命令文件以大驼峰命名,需要以`Cmd.php`结尾。类名与文件名相同,而且需要实现`Lib\Base\BaseCmd`接口。例如: + +```php +Template`命名,且类名和文件名相同,且实现自接口`Lib\Base\BaseTemplate`。 + +对于`meo-blog`,最低要求是需要有Archives、Index、Page、Post,这四个模板在`BuildCmd`中使用了。如果需要增加新的页面渲染,需要修改`BuildCmd`,并在`Templates`里增加新的渲染逻辑。 + + +### features + +> 这里写的是一些准备要做的事 + ++ 初始模板 ++ 文章列表排序 + + diff --git a/Sources/Pages/page1/index.md b/Sources/Pages/page1/index.md new file mode 100644 index 0000000..1a4ab64 --- /dev/null +++ b/Sources/Pages/page1/index.md @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/Sources/Posts/template/index.md b/Sources/Posts/template/index.md new file mode 100644 index 0000000..7864805 --- /dev/null +++ b/Sources/Posts/template/index.md @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/Templates/ArchiveTemplate.php b/Templates/ArchiveTemplate.php index 7d49cfd..60fd208 100644 --- a/Templates/ArchiveTemplate.php +++ b/Templates/ArchiveTemplate.php @@ -4,7 +4,81 @@ namespace Templates; -class ArchiveTemplate +use Lib\Base\BaseTemplate; + +class ArchiveTemplate implements BaseTemplate { + public static function render($data) + { + $postsHtml = ""; + $posts = self::getPosts(); + foreach ($posts as $_post) { + $postsHtml .= " + + "; + } + $html = << + + + + + 文章列表 + + + +
    +

    文章列表

    + {$postsHtml} +
    + + +EOF; + return $html; + } + public static function getPosts() + { + $postsRes = []; + $posts = scandir(POST_DIR); + foreach ($posts as $v) { + if (stripos($v, '.') !== false) continue; + $_postPath = POST_DIR . '/' . $v; + if (is_dir($_postPath)) { + $mdContent = file_get_contents($_postPath . '/index.md'); + $postSplit = PostTemplate::splitPost($mdContent); + $postsRes[] = [ + 'path' => "_posts/{$v}/index.html", + 'title' => $postSplit['title'] ?? '', + 'datetime' => $postSplit['datetime'] ?? '' + ]; + } else if (is_file($_postPath) && stripos($_postPath, ".md") !== false) { + $mdContent = file_get_contents($_postPath); + $postSplit = PostTemplate::splitPost($mdContent); + $postsRes[] = [ + 'path' => "_posts/" . str_replace("md", 'html', $v), + 'title' => $postSplit['title'] ?? '', + 'datetime' => $postSplit['datetime'] ?? '' + ]; + } + } + return $postsRes; + } } diff --git a/Templates/IndexTemplate.php b/Templates/IndexTemplate.php new file mode 100644 index 0000000..2bda3ea --- /dev/null +++ b/Templates/IndexTemplate.php @@ -0,0 +1,80 @@ + + + + + + {{title}} + + + +
    +

    {{title}}

    +

    {{subtitle}}

    + +
    + + +EOF; + + $replaceArr = [ + 'title' => ENV['title'], + 'subtitle' => ENV['subtitle'], + ]; + + foreach ($replaceArr as $k => $v) { + $html = str_replace("{{{$k}}}", $v, $html); + } + + return $html; + } +} diff --git a/Templates/PageTemplate.php b/Templates/PageTemplate.php index 1b0e904..e9c6122 100644 --- a/Templates/PageTemplate.php +++ b/Templates/PageTemplate.php @@ -7,7 +7,7 @@ namespace Templates; use Lib\Base\BaseTemplate; use Lib\Parser; -class PostTemplate implements BaseTemplate +class PageTemplate implements BaseTemplate { public static function render($data) { diff --git a/Templates/PostTemplate.php b/Templates/PostTemplate.php index 8fc4f95..abcb0f5 100644 --- a/Templates/PostTemplate.php +++ b/Templates/PostTemplate.php @@ -4,7 +4,60 @@ namespace Templates; -class PostTemplate +use Lib\Base\BaseTemplate; +use Lib\Parser; + +class PostTemplate implements BaseTemplate { + public static function render($data) + { + $filePath = $data['filePath']; + $mdFileContent = file_get_contents($filePath); + $postSplit = self::splitPost($mdFileContent); + $html = << + + + + + {$postSplit['title']} + + +

    {$postSplit['title']}

    +

    {$postSplit['datetime']}

    +

    +
    +EOF + .(new Parser())->makeHtml($postSplit['body']). + << + + +EOF; + + return $html; + } + public static function splitPost($mdFileContent) + { + $aStr = ""; + $b = stripos($mdFileContent, $bStr); + $comments = substr($mdFileContent, $a, $b - $a); + $commentArr = explode("\n", $comments); + foreach ($commentArr as $commentLine) { + if (!stripos($commentLine, "|")) continue; + list($_k, $_v) = explode("|", $commentLine); + switch (trim($_k)) { + case 'title': $_title = trim($_v); break; + case 'datetime': $_datetime = trim($_v); break; + } + } + return [ + 'title' => $_title ?? '', + 'datetime' => $_datetime ?? '', + 'body' => substr($mdFileContent, stripos($mdFileContent, "-->") + 3) + ]; + } } diff --git a/Templates/_config.php b/Templates/_config.php index b3d9bbc..bee73cf 100644 --- a/Templates/_config.php +++ b/Templates/_config.php @@ -1 +1,4 @@ + + + + + 文章列表 + + + + + + \ No newline at end of file diff --git a/Templates/page/index.php b/Templates/page/index.php new file mode 100644 index 0000000..527f1b9 --- /dev/null +++ b/Templates/page/index.php @@ -0,0 +1,53 @@ + + + + + + meoBlog + + + +
    +

    meoBlog

    +

    从前从前,有个人爱你很久

    + +
    + + diff --git a/Templates/page/page.php b/Templates/page/page.php new file mode 100644 index 0000000..59c1a8c --- /dev/null +++ b/Templates/page/page.php @@ -0,0 +1,58 @@ + + + + + + 页面标题 + + + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 +
    + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 +
    + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 +
    + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 +
    + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 +
    + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 + 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 +
    + + \ No newline at end of file diff --git a/Templates/page/post.php b/Templates/page/post.php new file mode 100644 index 0000000..b7a02c4 --- /dev/null +++ b/Templates/page/post.php @@ -0,0 +1,25 @@ + + + + + + 文章标题 + + +

    文章标题

    +

    发表于 时间

    +

    +
    + 文章内容 + 文章内容 + 文章内容 + 文章内容 + 文章内容文章内容文章内容文章内容文章内容文章内容文章内容
    + 文章内容文章内容文章内容文章内容文章内容 + 文章内容文章内容文章内容文章内容文章内容文章内容 +
    + 文章内容文章内容文章内容文章内容 + 文章内容文章内容文章内容 +
    + + \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..5b51035 --- /dev/null +++ b/composer.json @@ -0,0 +1,19 @@ +{ + "name": "xt/meo-blog", + "description": "a meo blog", + "license": "MIT", + "authors": [ + { + "name": "xt", + "email": "t@xtzero.me" + } + ], + "require": {}, + "autoload": { + "psr-4": { + "Cmd\\":".\\Cmd", + "Lib\\":".\\Lib", + "Templates\\":".\\Templates" + } + } +} diff --git a/meo b/meo new file mode 100755 index 0000000..e921aee --- /dev/null +++ b/meo @@ -0,0 +1,16 @@ + Date: Wed, 20 Jan 2021 15:05:01 +0800 Subject: [PATCH 03/28] =?UTF-8?q?=E5=A2=9E=E5=8A=A0s=E7=9B=91=E5=90=AC?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=92=8C=E6=96=87=E7=AB=A0=E9=A1=B5=E6=8E=92?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cmd/BuildCmd.php | 2 +- Cmd/ServeCmd.php | 5 ++- Lib/HttpServer.php | 76 +++++++++++++++++++++++++++++++++++ Templates/ArchiveTemplate.php | 20 +++++++-- 4 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 Lib/HttpServer.php diff --git a/Cmd/BuildCmd.php b/Cmd/BuildCmd.php index 5d01325..0184347 100644 --- a/Cmd/BuildCmd.php +++ b/Cmd/BuildCmd.php @@ -36,7 +36,7 @@ class BuildCmd implements BaseCmd echo "Build pages...\n"; self::buildPages(PAGE_DIR); - echo "Build finish!\nSee at " . DIST_DIR; + echo "Build finish!\nSee at " . DIST_DIR . "\n"; } public static function buildIndex() diff --git a/Cmd/ServeCmd.php b/Cmd/ServeCmd.php index 0269c68..68e6ff0 100644 --- a/Cmd/ServeCmd.php +++ b/Cmd/ServeCmd.php @@ -5,12 +5,15 @@ namespace Cmd; use Lib\Base\BaseCmd; +use Lib\HttpServer; class ServeCmd implements BaseCmd { public static function handle($argv) { + echo "Exec php meo b \n\n"; BuildCmd::handle($argv); - system("cd ". DIST_DIR ." && php -S ". ENV['develop_server_host'] .":".ENV['develop_server_port']); + echo "Exec php meo s \n\n"; + HttpServer::init()->listen(); } } diff --git a/Lib/HttpServer.php b/Lib/HttpServer.php new file mode 100644 index 0000000..cd0d911 --- /dev/null +++ b/Lib/HttpServer.php @@ -0,0 +1,76 @@ + "_posts/{$v}/index.html", 'title' => $postSplit['title'] ?? '', - 'datetime' => $postSplit['datetime'] ?? '' + 'datetime' => $postSplit['datetime'] ?? '', + 'timestamp' => $postSplit['datetime'] ? strtotime($postSplit['datetime']) : 0 ]; } else if (is_file($_postPath) && stripos($_postPath, ".md") !== false) { $mdContent = file_get_contents($_postPath); @@ -75,10 +76,23 @@ EOF; $postsRes[] = [ 'path' => "_posts/" . str_replace("md", 'html', $v), 'title' => $postSplit['title'] ?? '', - 'datetime' => $postSplit['datetime'] ?? '' + 'datetime' => $postSplit['datetime'] ?? '', + 'timestamp' => $postSplit['datetime'] ? strtotime($postSplit['datetime']) : 0 ]; } } - return $postsRes; + + $timeStamps = array_column($postsRes, 'timestamp'); + rsort($timeStamps); + $res = []; + foreach($timeStamps as $t) { + foreach($postsRes as $p) { + if ($p['timestamp'] == $t) { + $res[] = $p; + } + } + } + + return $res; } } -- Gitee From 04500fdc499bf90bbf73f76c18f8e1add8ea32da Mon Sep 17 00:00:00 2001 From: xt Date: Wed, 20 Jan 2021 15:46:55 +0800 Subject: [PATCH 04/28] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cmd/BuildCmd.php | 87 ++--------------------------------- README.md | 25 ++++------ Sources/Pages/doc/index.md | 30 ++++++------ Templates/ArchiveTemplate.php | 6 ++- Templates/IndexTemplate.php | 14 ++++-- Templates/PageTemplate.php | 74 +++++++++++++++++++---------- Templates/PostTemplate.php | 76 ++++++++++++++++++++---------- Templates/_config.php | 15 +++++- meo | 3 +- 9 files changed, 160 insertions(+), 170 deletions(-) diff --git a/Cmd/BuildCmd.php b/Cmd/BuildCmd.php index 0184347..fad8160 100644 --- a/Cmd/BuildCmd.php +++ b/Cmd/BuildCmd.php @@ -27,87 +27,10 @@ class BuildCmd implements BaseCmd die("{$themePath} not exist!\n"); } - echo "Load theme success! \n Build index page...\n"; - self::buildIndex(); - echo "Build archives...\n"; - self::buildArchives(); - echo "Build posts...\n"; - self::buildPosts(POST_DIR); - echo "Build pages...\n"; - self::buildPages(PAGE_DIR); - - echo "Build finish!\nSee at " . DIST_DIR . "\n"; - } - - public static function buildIndex() - { - $html = IndexTemplate::render([]); - file_put_contents(DIST_DIR . '/' . 'index.html', $html); - } - - public static function buildPosts($postDir) - { - $posts = scandir($postDir); - foreach ($posts as $v) { - $filePath = $postDir . '/' . $v; - if (is_dir($filePath)) { - echo "{$filePath} is dir \n"; - if (in_array($v, ['.', '..'])) continue; - if (!is_dir(DIST_DIR . '/_posts/' . $v)) { - if (!is_dir(DIST_DIR . '/_posts')) mkdir (DIST_DIR . '/_posts'); - mkdir(DIST_DIR . '/_posts/' . $v); - } - self::buildPosts($filePath); - } else if(is_file($filePath) && stripos($filePath, '.md')) { - echo "{$filePath} is file \n"; - $htmlFilePath = str_replace('.md', '.html', $filePath); - $htmlFilePath = str_replace('Sources/Posts', 'Dist/_posts', $htmlFilePath); - if (!file_exists($htmlFilePath)) { - touch($htmlFilePath); - } - $html = PostTemplate::render([ - 'filePath' => $filePath - ]); - file_put_contents($htmlFilePath, $html); - } - } - } - - public static function buildPages($pageDir) - { - $posts = scandir($pageDir); - foreach ($posts as $v) { - $filePath = $pageDir . '/' . $v; - if (is_dir($filePath)) { - echo "{$filePath} is dir \n"; - if (in_array($v, ['.', '..'])) continue; - if (!is_dir(DIST_DIR . '/' . $v)) { - if (!is_dir(DIST_DIR)) mkdir (DIST_DIR . '/_posts'); - mkdir(DIST_DIR . '/' . $v); - } - self::buildPages($filePath); - } else if(is_file($filePath) && stripos($filePath, '.md')) { - echo "{$filePath} is file \n"; - $htmlFilePath = str_replace('.md', '.html', $filePath); - $htmlFilePath = str_replace('Sources/Pages', 'Dist', $htmlFilePath); - if (!file_exists($htmlFilePath)) { - touch($htmlFilePath); - } - $html = PageTemplate::render([ - 'filePath' => $filePath - ]); - file_put_contents($htmlFilePath, $html); - } - } - } - - public static function buildArchives() - { - $html = ArchiveTemplate::render([]); - $archivesFilePath = DIST_DIR . '/archives.html'; - if (!file_exists($archivesFilePath)) { - touch($archivesFilePath); + $buildSort = THEME_CONFIG['build']['builder']; + foreach ($buildSort as $v) { + $v::render([]); } - file_put_contents($archivesFilePath, $html); + echo "See at ". DIST_DIR ." \n"; } -} +} \ No newline at end of file diff --git a/README.md b/README.md index 3ba0fa7..ba21c42 100644 --- a/README.md +++ b/README.md @@ -98,8 +98,6 @@ datetime|[create_datetime] 对于文章,文章列表页会显示标题和创建日期,并且会按照创建日期排序。 -> 目前(pre-release 1)还不会按创建日期排序(忘了) - 除了注释部分,其余都是正文部分。正文支持`markdown`语法,使用[SegmentFault/HyperDown](https://github.com/SegmentFault/HyperDown)进行解析。除此之外,还支持传统的html语法。也就是说你可以在正文部分通过写html标签来实现更多定制功能。 ### 生成静态页 @@ -108,15 +106,10 @@ datetime|[create_datetime] ```shell script $ php meo b -Load theme from path: /Volumes/proj/meoBlog/Templates/ -Load theme success! -Build index page... -Build archives... -Build posts... ... -Build pages... +Clean dist path finish +Load theme from path: /Volumes/proj/meoBlog/Templates/ ... -Build finish! See at /Volumes/proj/meoBlog/Dist ``` @@ -128,12 +121,13 @@ See at /Volumes/proj/meoBlog/Dist ```shell script $ php meo s -[Sun Jan 17 20:53:02 2021] PHP 7.4.1 Development Server (http://localhost:8888) started +Started develop server: http://localhost:8888 +You can use 'php meo b' in another terminal to rebuild dist files! ``` 访问显示的地址,可以查看生成的结果。 -> `php meo s`命令还未支持实时刷新。 +> `php meo s`命令还未支持实时刷新,但你可以另外开启一个终端,使用`php meo b`来重新生成静态文件。用这种方式不需要重新执行`php meo s`也能看到新的结果。 ### 发布 @@ -224,14 +218,13 @@ class OneCmd implements BaseCmd 模板使用`Template`命名,且类名和文件名相同,且实现自接口`Lib\Base\BaseTemplate`。 -对于`meo-blog`,最低要求是需要有Archives、Index、Page、Post,这四个模板在`BuildCmd`中使用了。如果需要增加新的页面渲染,需要修改`BuildCmd`,并在`Templates`里增加新的渲染逻辑。 +对于`meo-blog`,默认模板里包含Archives、Index、Page、Post,这四个模板在模板的配置文件`/Templates/_config.php`中配置了渲染顺序,在执行`php meo b`时,`meo`会按照`/Templates/_config.php`中配置的顺序依次调用模板下的`render`方法来执行渲染。 + +如果需要更改渲染逻辑,或是开发新的模板,先在`Templates`里写好渲染逻辑,再在`/Templates/_config.php`中配置渲染顺序即可。 ### features > 这里写的是一些准备要做的事 -+ 初始模板 -+ 文章列表排序 - - ++ 初始模板 \ No newline at end of file diff --git a/Sources/Pages/doc/index.md b/Sources/Pages/doc/index.md index b14b78c..e22208c 100644 --- a/Sources/Pages/doc/index.md +++ b/Sources/Pages/doc/index.md @@ -1,7 +1,12 @@ +# meoBlog + +是一个会meo meo叫的博客系统。 + + ``` _ _ _ __ ___ ___ ___ | |__ | | ___ __ _ @@ -97,8 +102,6 @@ datetime|[create_datetime] 对于文章,文章列表页会显示标题和创建日期,并且会按照创建日期排序。 -> 目前(pre-release 1)还不会按创建日期排序(忘了) - 除了注释部分,其余都是正文部分。正文支持`markdown`语法,使用[SegmentFault/HyperDown](https://github.com/SegmentFault/HyperDown)进行解析。除此之外,还支持传统的html语法。也就是说你可以在正文部分通过写html标签来实现更多定制功能。 ### 生成静态页 @@ -107,15 +110,10 @@ datetime|[create_datetime] ```shell script $ php meo b -Load theme from path: /Volumes/proj/meoBlog/Templates/ -Load theme success! -Build index page... -Build archives... -Build posts... ... -Build pages... +Clean dist path finish +Load theme from path: /Volumes/proj/meoBlog/Templates/ ... -Build finish! See at /Volumes/proj/meoBlog/Dist ``` @@ -127,12 +125,13 @@ See at /Volumes/proj/meoBlog/Dist ```shell script $ php meo s -[Sun Jan 17 20:53:02 2021] PHP 7.4.1 Development Server (http://localhost:8888) started +Started develop server: http://localhost:8888 +You can use 'php meo b' in another terminal to rebuild dist files! ``` 访问显示的地址,可以查看生成的结果。 -> `php meo s`命令还未支持实时刷新。 +> `php meo s`命令还未支持实时刷新,但你可以另外开启一个终端,使用`php meo b`来重新生成静态文件。用这种方式不需要重新执行`php meo s`也能看到新的结果。 ### 发布 @@ -223,7 +222,9 @@ class OneCmd implements BaseCmd 模板使用`Template`命名,且类名和文件名相同,且实现自接口`Lib\Base\BaseTemplate`。 -对于`meo-blog`,最低要求是需要有Archives、Index、Page、Post,这四个模板在`BuildCmd`中使用了。如果需要增加新的页面渲染,需要修改`BuildCmd`,并在`Templates`里增加新的渲染逻辑。 +对于`meo-blog`,默认模板里包含Archives、Index、Page、Post,这四个模板在模板的配置文件`/Templates/_config.php`中配置了渲染顺序,在执行`php meo b`时,`meo`会按照`/Templates/_config.php`中配置的顺序依次调用模板下的`render`方法来执行渲染。 + +如果需要更改渲染逻辑,或是开发新的模板,先在`Templates`里写好渲染逻辑,再在`/Templates/_config.php`中配置渲染顺序即可。 ### features @@ -231,6 +232,5 @@ class OneCmd implements BaseCmd > 这里写的是一些准备要做的事 + 初始模板 -+ 文章列表排序 diff --git a/Templates/ArchiveTemplate.php b/Templates/ArchiveTemplate.php index 48772b8..07f565d 100644 --- a/Templates/ArchiveTemplate.php +++ b/Templates/ArchiveTemplate.php @@ -51,7 +51,11 @@ class ArchiveTemplate implements BaseTemplate EOF; - return $html; + $archivesFilePath = DIST_DIR . '/archives.html'; + if (!file_exists($archivesFilePath)) { + touch($archivesFilePath); + } + file_put_contents($archivesFilePath, $html); } public static function getPosts() diff --git a/Templates/IndexTemplate.php b/Templates/IndexTemplate.php index 2bda3ea..d2c6e86 100644 --- a/Templates/IndexTemplate.php +++ b/Templates/IndexTemplate.php @@ -44,10 +44,16 @@ class IndexTemplate implements BaseTemplate .menu { margin: 0 10px; + background-color: #50A8EC; + width: 160px; + height: 50px; + border-radius: 5px; + line-height: 50px; + text-align: center; + color: white; } .menu:hover { - text-decoration: underline; - color: #3498DB; + background-color: #447DBC; cursor: pointer; } @@ -57,7 +63,7 @@ class IndexTemplate implements BaseTemplate

    {{title}}

    {{subtitle}}

    @@ -75,6 +81,6 @@ EOF; $html = str_replace("{{{$k}}}", $v, $html); } - return $html; + file_put_contents(DIST_DIR . '/' . 'index.html', $html); } } diff --git a/Templates/PageTemplate.php b/Templates/PageTemplate.php index e9c6122..6298403 100644 --- a/Templates/PageTemplate.php +++ b/Templates/PageTemplate.php @@ -11,31 +11,7 @@ class PageTemplate implements BaseTemplate { public static function render($data) { - $filePath = $data['filePath']; - $mdFileContent = file_get_contents($filePath); - $postSplit = self::splitPost($mdFileContent); - $html = << - - - - - {$postSplit['title']} - - -

    {$postSplit['title']}

    -

    {$postSplit['datetime']}

    -

    -
    -EOF - .(new Parser())->makeHtml($postSplit['body']). - << - - -EOF; - - return $html; + self::buildPages(PAGE_DIR); } private static function splitPost($mdFileContent) @@ -60,4 +36,52 @@ EOF; 'body' => substr($mdFileContent, stripos($mdFileContent, "-->") + 3) ]; } + + public static function buildPages($pageDir) + { + $posts = scandir($pageDir); + foreach ($posts as $v) { + $filePath = $pageDir . '/' . $v; + if (is_dir($filePath)) { + echo "{$filePath} is dir \n"; + if (in_array($v, ['.', '..'])) continue; + if (!is_dir(DIST_DIR . '/' . $v)) { + if (!is_dir(DIST_DIR)) mkdir (DIST_DIR . '/_posts'); + mkdir(DIST_DIR . '/' . $v); + } + self::buildPages($filePath); + } else if(is_file($filePath) && stripos($filePath, '.md')) { + echo "{$filePath} is file \n"; + $htmlFilePath = str_replace('.md', '.html', $filePath); + $htmlFilePath = str_replace('Sources/Pages', 'Dist', $htmlFilePath); + if (!file_exists($htmlFilePath)) { + touch($htmlFilePath); + } + + $mdFileContent = file_get_contents($filePath); + $postSplit = self::splitPost($mdFileContent); + $html = << + + + + + {$postSplit['title']} + + +

    {$postSplit['title']}

    +

    {$postSplit['datetime']}

    +

    +
    +EOF + .(new Parser())->makeHtml($postSplit['body']). + << + + +EOF; + file_put_contents($htmlFilePath, $html); + } + } + } } diff --git a/Templates/PostTemplate.php b/Templates/PostTemplate.php index abcb0f5..295b626 100644 --- a/Templates/PostTemplate.php +++ b/Templates/PostTemplate.php @@ -11,31 +11,7 @@ class PostTemplate implements BaseTemplate { public static function render($data) { - $filePath = $data['filePath']; - $mdFileContent = file_get_contents($filePath); - $postSplit = self::splitPost($mdFileContent); - $html = << - - - - - {$postSplit['title']} - - -

    {$postSplit['title']}

    -

    {$postSplit['datetime']}

    -

    -
    -EOF - .(new Parser())->makeHtml($postSplit['body']). - << - - -EOF; - - return $html; + self::buildPosts(POST_DIR); } public static function splitPost($mdFileContent) @@ -60,4 +36,54 @@ EOF; 'body' => substr($mdFileContent, stripos($mdFileContent, "-->") + 3) ]; } + + public static function buildPosts($postDir) + { + $posts = scandir($postDir); + foreach ($posts as $v) { + $filePath = $postDir . '/' . $v; + if (is_dir($filePath)) { + echo "{$filePath} is dir \n"; + if (in_array($v, ['.', '..'])) continue; + if (!is_dir(DIST_DIR . '/_posts/' . $v)) { + if (!is_dir(DIST_DIR . '/_posts')) mkdir (DIST_DIR . '/_posts'); + mkdir(DIST_DIR . '/_posts/' . $v); + } + self::buildPosts($filePath); + } else if(is_file($filePath) && stripos($filePath, '.md')) { + echo "{$filePath} is file \n"; + $htmlFilePath = str_replace('.md', '.html', $filePath); + $htmlFilePath = str_replace('Sources/Posts', 'Dist/_posts', $htmlFilePath); + if (!file_exists($htmlFilePath)) { + touch($htmlFilePath); + } + + $mdFileContent = file_get_contents($filePath); + $postSplit = self::splitPost($mdFileContent); + + $html = << + + + + + {$postSplit['title']} + + +

    {$postSplit['title']}

    +

    {$postSplit['datetime']}

    +

    +
    +EOF + .(new Parser())->makeHtml($postSplit['body']). + << + + +EOF; + + file_put_contents($htmlFilePath, $html); + } + } + } } diff --git a/Templates/_config.php b/Templates/_config.php index bee73cf..f417bd5 100644 --- a/Templates/_config.php +++ b/Templates/_config.php @@ -1,4 +1,17 @@ [ + "builder" => [ + IndexTemplate::class, + ArchiveTemplate::class, + PostTemplate::class, + PageTemplate::class + ] + ] ]; diff --git a/meo b/meo index e921aee..361b56f 100755 --- a/meo +++ b/meo @@ -1,7 +1,8 @@ Date: Wed, 20 Jan 2021 16:34:52 +0800 Subject: [PATCH 05/28] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/Pages/doc/index.md | 1 + Templates/PageTemplate.php | 6 ++ Templates/PostTemplate.php | 6 ++ Templates/components/contentCss | 73 +++++++++++++++++ Templates/page/page.html | 139 ++++++++++++++++++++++++++++++++ Templates/page/page.php | 58 ------------- 6 files changed, 225 insertions(+), 58 deletions(-) create mode 100644 Templates/components/contentCss create mode 100644 Templates/page/page.html delete mode 100644 Templates/page/page.php diff --git a/Sources/Pages/doc/index.md b/Sources/Pages/doc/index.md index e22208c..e6876ad 100644 --- a/Sources/Pages/doc/index.md +++ b/Sources/Pages/doc/index.md @@ -1,6 +1,7 @@ # meoBlog diff --git a/Templates/PageTemplate.php b/Templates/PageTemplate.php index 6298403..2633b60 100644 --- a/Templates/PageTemplate.php +++ b/Templates/PageTemplate.php @@ -28,11 +28,13 @@ class PageTemplate implements BaseTemplate switch (trim($_k)) { case 'title': $_title = trim($_v); break; case 'datetime': $_datetime = trim($_v); break; + case 'emoji': $_emoji = trim($_v); break; } } return [ 'title' => $_title ?? '', 'datetime' => $_datetime ?? '', + 'emoji' => $_emoji ?? '📔', 'body' => substr($mdFileContent, stripos($mdFileContent, "-->") + 3) ]; } @@ -67,8 +69,12 @@ class PageTemplate implements BaseTemplate {$postSplit['title']} +EOF +. file_get_contents(THEME_BASE_DIR . '/components/contentCss') +. << +

    {$postSplit['title']}

    {$postSplit['datetime']}



    diff --git a/Templates/PostTemplate.php b/Templates/PostTemplate.php index 295b626..8f3a704 100644 --- a/Templates/PostTemplate.php +++ b/Templates/PostTemplate.php @@ -28,11 +28,13 @@ class PostTemplate implements BaseTemplate switch (trim($_k)) { case 'title': $_title = trim($_v); break; case 'datetime': $_datetime = trim($_v); break; + case 'emoji': $_emoji = trim($_v); break; } } return [ 'title' => $_title ?? '', 'datetime' => $_datetime ?? '', + 'emoji' => $_emoji ?? '📒', 'body' => substr($mdFileContent, stripos($mdFileContent, "-->") + 3) ]; } @@ -68,8 +70,12 @@ class PostTemplate implements BaseTemplate {$postSplit['title']} +EOF +. file_get_contents(THEME_BASE_DIR . '/components/contentCss') +. << +

    {$postSplit['title']}

    {$postSplit['datetime']}



    diff --git a/Templates/components/contentCss b/Templates/components/contentCss new file mode 100644 index 0000000..a7f6a23 --- /dev/null +++ b/Templates/components/contentCss @@ -0,0 +1,73 @@ + \ No newline at end of file diff --git a/Templates/page/page.html b/Templates/page/page.html new file mode 100644 index 0000000..bff2d40 --- /dev/null +++ b/Templates/page/page.html @@ -0,0 +1,139 @@ + + + + + + + meo-blog: 一个meo~meo叫的博客系统 develop 2 + + + + +

    meo-blog: 一个meo~meo叫的博客系统 develop 2

    +

    2021-01-20 15:42:00

    +

    +

    meoBlog

    是一个会meo meo叫的博客系统。

                            _     _
    + _ __ ___   ___  ___   | |__ | | ___   __ _
    +| '_ ` _ \ / _ \/ _ \  | '_ \| |/ _ \ / _` |
    +| | | | | |  \__/ (_) | | |_) | | (_) | (_| |
    +|_| |_| |_|\___|\___/  |_.__/|_|\___/ \__, |
    +                                      |___/

    基础使用

    安装并快速开始

    在安装前,你需要先准备好必要的环境。

    php 7.4 +

    composer 1.9.1 +

    接下来开始安装项目。

    • 访问githubmain分支,或giteemaster分支,克隆项目到本地。
    $ git clone https://gitee.com/xtzero/meoBlog.git
    +//或 git clone https://github.com/xtzero/meoBlog.git
    • 使用composer安装依赖。
    $ composer install -vvv
    • 使用meo命令生成页面,开启服务。
    $ php meo s
    +
    +[Sun Jan 17 20:18:29 2021] PHP 7.4.1 Development Server (http://localhost:8888) started
    • 点击控制台显示的地址,访问页面。

    http://localhost:8888

    文档约定

    在本文档中,使用/Volumes/proj/meoBlog当做项目的根目录。

    在本文档中,$表示终端提示符,$后是执行的命令,后面的行则是命令执行之后的回显。

    在本文档中,会使用[name]来包含一个可选参数,使用<name>来包含一个必选参数。中间的单词请根据上下文或单词本身的意义来解读。

    新建文章

    使用meo命令可以为你新建文章

    $ php meo new post [post_name]
    +Create file success: /Volumes/proj/meoBlog/Sources/Posts/template/index.md

    根据控制台输出可见,新建的markdown文档已经生成好了。你可以编辑/Volumes/proj/meoBlog/Sources/Posts/[post_name]/index.md来编写新的文章。

    有关于文章内容的编辑问题,且等介绍完新建页面之后叙述。

    新建页面

    meo-blog中,页面是独立于文章之外的独立页面,不会显示在文章列表页面中。

    和新建文章一样,你也可以使用meo命令来新建页面。

    $ php meo new page [page_name]
    +Create file success: /Volumes/proj/meoBlog/Sources/Pages/page1/index.md

    你可以编辑/Volumes/proj/meoBlog/Sources/Pages/[page_name]/index.md来编写新页面的内容。

    编写文章和页面内容

    使用meo new命令新建的文章或页面,都会有如下默认内容:

    <!--
    +title|[default_title]
    +datetime|[create_datetime]
    +-->

    这部分叫做文章/页面的注释部分,使用html注释语法包含。默认模板只能解析到titledatetime,如果想要解析更多内容,可以通过重写模板来实现。

    其中,[default_title]会被当成文章/页面标题,显示在内容页的顶部;[create_datetime]会被当成文章/页面的新建时间,放在标题下面。

    对于文章,文章列表页会显示标题和创建日期,并且会按照创建日期排序。

    除了注释部分,其余都是正文部分。正文支持markdown语法,使用SegmentFault/HyperDown进行解析。除此之外,还支持传统的html语法。也就是说你可以在正文部分通过写html标签来实现更多定制功能。

    生成静态页

    使用如下meo命令来生成静态页。

    $ php meo b
    +...
    +Clean dist path finish
    +Load theme from path: /Volumes/proj/meoBlog/Templates/
    +...
    +See at /Volumes/proj/meoBlog/Dist

    生成的静态页内容是项目路径下的Dist目录,你可以直接访问文件来查看页面效果。

    访问页面

    你可以使用meo命令来生成静态页并开启php开发服务器来查看页面效果。

    $ php meo s
    +Started develop server: http://localhost:8888
    +You can use 'php meo b' in another terminal to rebuild dist files! 

    访问显示的地址,可以查看生成的结果。

    php meo s命令还未支持实时刷新,但你可以另外开启一个终端,使用php meo b来重新生成静态文件。用这种方式不需要重新执行php meo s也能看到新的结果。

    发布

    你可以将Dist路径完整复制到服务器目录,或将网站根路径指向Dist目录的位置,即可发布你的网站。

    高级

    配置

    配置文件是/env.php,你可以编辑你的个性化配置。

    配置项解释
    title网站主标题
    subtitle网站副标题
    develop_server_host开发服务地址
    develop_server_port开发服务端口

    在开发中,任何位置都可以通过访问ENV来使用env.php中的值。例如

    // 取网站主标题
    +$title = ENV['title'];

    全局变量

    全局变量都在/meo中定义。/meo是一个php文件,为了命令简洁,所以省略了用户名。这样做才可以使用php meo <command>来执行操作。

    全局变量解释
    ENV/env.php返回的数组
    THEME_CONFIG主题配置,通常是/Templates/_config.php
    ROOT_DIR项目根目录
    DIST_DIR静态文件目录,通常是/Dist
    SOURCES_DIR源文件目录,通常是/Sources
    POST_DIR文章源文件目录,通常是/Sources/Pages
    PAGE_DIR页面源文件目录,通常是/Sources/Pages
    THEME_BASE_DIR模板根目录,通常是/Templates

    meo命令

    你可以使用php meo help来查看所有支持的meo命令。

    $ php meo h
    +                            
    +Welcome to use meo blog!
    +This is a blog system written by a white cat's father!
    +
    +Usage: php meo \<command\>
    +Here are all of commands: 
    +
    +- php meo help | h: Get help(just like you now!)
    +- php meo new \<post|page\> \<title\>: Create a new post
    +- php meo build | b: Build static files
    +- php meo serve | s: Start develop server, and check page render when file change
    +- php meo clean | c: Clean static files and db file
    +
    +If you want join us, visit https://gitee.com/xtzero/meoBlog.
    +
    +Enjoy yourself~ 

    除此之外,你可以定制自己的meo命令来实现一些自定义操作。自定义命令写在/Cmd中,一个命令文件以大驼峰命名,需要以Cmd.php结尾。类名与文件名相同,而且需要实现Lib\Base\BaseCmd接口。例如:

    <?php
    +// OneCmd.php
    +namespace Cmd;
    +
    +use Lib\Base\BaseCmd;
    +
    +class OneCmd implements BaseCmd
    +{
    +    public static function handle($argv)
    +    {
    +       // To do sth...
    +    }
    +}
    +

    执行php meo example的时候会调用/Cmd/ExampleCmdhandle方法。

    模板和渲染器

    模板和渲染器是在Templates里的方法,这里实现了页面渲染的逻辑。

    模板使用<Template-name>Template命名,且类名和文件名相同,且实现自接口Lib\Base\BaseTemplate

    对于meo-blog,默认模板里包含Archives、Index、Page、Post,这四个模板在模板的配置文件/Templates/_config.php中配置了渲染顺序,在执行php meo b时,meo会按照/Templates/_config.php中配置的顺序依次调用模板下的render方法来执行渲染。

    如果需要更改渲染逻辑,或是开发新的模板,先在Templates里写好渲染逻辑,再在/Templates/_config.php中配置渲染顺序即可。

    features

    这里写的是一些准备要做的事
    • 初始模板
    + + \ No newline at end of file diff --git a/Templates/page/page.php b/Templates/page/page.php deleted file mode 100644 index 59c1a8c..0000000 --- a/Templates/page/page.php +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - 页面标题 - - - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 -
    - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 -
    - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 -
    - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 -
    - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 -
    - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 - 页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容页面内容 -
    - - \ No newline at end of file -- Gitee From e5277cc0d3bfc32405ea7e89be0891df6af345cb Mon Sep 17 00:00:00 2001 From: xt Date: Thu, 21 Jan 2021 11:15:44 +0800 Subject: [PATCH 06/28] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E8=AF=AD=E6=B3=95=EF=BC=8C=E5=88=9D=E5=A7=8B=E4=B8=BB=E9=A2=98?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/functions.php | 13 +++++++ Lib/main.php | 2 + README.md | 2 +- Templates/ArchiveTemplate.php | 5 +++ Templates/IndexTemplate.php | 5 +++ Templates/PageTemplate.php | 7 +++- Templates/PostTemplate.php | 5 +++ Templates/components/navbar | 65 +++++++++++++++++++++++++++++++++ Templates/page/page.html | 69 +++++++++++++++++++++++++++++++++++ 9 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 Lib/functions.php create mode 100644 Templates/components/navbar diff --git a/Lib/functions.php b/Lib/functions.php new file mode 100644 index 0000000..4e5922f --- /dev/null +++ b/Lib/functions.php @@ -0,0 +1,13 @@ + $v) { + $contents = str_replace("{{{$k}}}", $v, $contents); + } + } + return $contents; +} \ No newline at end of file diff --git a/Lib/main.php b/Lib/main.php index 256a2ca..71545e7 100644 --- a/Lib/main.php +++ b/Lib/main.php @@ -11,4 +11,6 @@ if (!file_exists($cmdFile)) { require "./vendor/autoload.php"; $class = "Cmd\\{$className}"; +require_once("functions.php"); + $class::handle($argv); diff --git a/README.md b/README.md index ba21c42..099ebc7 100644 --- a/README.md +++ b/README.md @@ -227,4 +227,4 @@ class OneCmd implements BaseCmd > 这里写的是一些准备要做的事 -+ 初始模板 \ No newline at end of file ++ `meo b`的时候同步除了md之外的其他文件(主要是图片) \ No newline at end of file diff --git a/Templates/ArchiveTemplate.php b/Templates/ArchiveTemplate.php index 07f565d..ee7481f 100644 --- a/Templates/ArchiveTemplate.php +++ b/Templates/ArchiveTemplate.php @@ -44,6 +44,11 @@ class ArchiveTemplate implements BaseTemplate +EOF +.C(THEME_BASE_DIR . '/components/navbar', [ + "title" => ENV['title'] +]) +.<<

    文章列表

    {$postsHtml} diff --git a/Templates/IndexTemplate.php b/Templates/IndexTemplate.php index d2c6e86..f88af37 100644 --- a/Templates/IndexTemplate.php +++ b/Templates/IndexTemplate.php @@ -59,6 +59,11 @@ class IndexTemplate implements BaseTemplate +EOF +.C(THEME_BASE_DIR . '/components/navbar', [ + "title" => ENV['title'] +]) +.<<

    {{title}}

    {{subtitle}}

    diff --git a/Templates/PageTemplate.php b/Templates/PageTemplate.php index 2633b60..ecaf6ee 100644 --- a/Templates/PageTemplate.php +++ b/Templates/PageTemplate.php @@ -70,10 +70,15 @@ class PageTemplate implements BaseTemplate {$postSplit['title']} EOF -. file_get_contents(THEME_BASE_DIR . '/components/contentCss') +. C(THEME_BASE_DIR . '/components/contentCss') . << +EOF +. C(THEME_BASE_DIR . '/components/navbar', [ + "title" => ENV['title'] +]) +. <<

    {$postSplit['title']}

    {$postSplit['datetime']}

    diff --git a/Templates/PostTemplate.php b/Templates/PostTemplate.php index 8f3a704..4e790da 100644 --- a/Templates/PostTemplate.php +++ b/Templates/PostTemplate.php @@ -75,6 +75,11 @@ EOF . << +EOF +.C(THEME_BASE_DIR . '/components/navbar', [ + "title" => ENV['title'] +]) +.<<

    {$postSplit['title']}

    {$postSplit['datetime']}

    diff --git a/Templates/components/navbar b/Templates/components/navbar new file mode 100644 index 0000000..3a26ced --- /dev/null +++ b/Templates/components/navbar @@ -0,0 +1,65 @@ + + + \ No newline at end of file diff --git a/Templates/page/page.html b/Templates/page/page.html index bff2d40..668cdcb 100644 --- a/Templates/page/page.html +++ b/Templates/page/page.html @@ -8,6 +8,7 @@ + + + + + +

    meo-blog: 一个meo~meo叫的博客系统 develop 2

    2021-01-20 15:42:00

    -- Gitee From 9af047b3e2021b043320837c96aadcf4b4b8bf5e Mon Sep 17 00:00:00 2001 From: xt Date: Fri, 22 Jan 2021 13:59:42 +0800 Subject: [PATCH 07/28] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=A7=A3=E6=9E=90html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/Parser.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/Parser.php b/Lib/Parser.php index bfcd703..fb887ba 100644 --- a/Lib/Parser.php +++ b/Lib/Parser.php @@ -23,7 +23,7 @@ class Parser * * @var string */ - public $_blockHtmlTags = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption|svg|script|noscript'; + public $_blockHtmlTags = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption|svg|script|noscript|style'; /** * _specialWhiteList @@ -140,6 +140,7 @@ class Parser $this->_holders = array(); $this->_uniqid = md5(uniqid()); $this->_id = 0; + $this->enableHtml(); usort($this->blockParsers, function ($a, $b) { return $a[1] < $b[1] ? -1 : 1; -- Gitee From c4b9707af10230bf4af407cb5ac2e7aae7aa8c16 Mon Sep 17 00:00:00 2001 From: xt Date: Fri, 22 Jan 2021 14:14:29 +0800 Subject: [PATCH 08/28] add LICENSE. --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0fd0680 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 xt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -- Gitee From 753f7ad2bbf9005d27e282346abed32a49fd7a1e Mon Sep 17 00:00:00 2001 From: xt Date: Fri, 22 Jan 2021 14:17:26 +0800 Subject: [PATCH 09/28] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 099ebc7..54470c8 100644 --- a/README.md +++ b/README.md @@ -227,4 +227,6 @@ class OneCmd implements BaseCmd > 这里写的是一些准备要做的事 -+ `meo b`的时候同步除了md之外的其他文件(主要是图片) \ No newline at end of file ++ `meo b`的时候同步除了md之外的其他文件(主要是图片) ++ 主题用二级文件夹保存,在env增加主题配置 ++ 将除了sources和vendor都挪到Lib里,原有Lib放到更深一级里。方便升级。 -- Gitee From 2ade91658215bf38767c6c45e8d11461a38da13f Mon Sep 17 00:00:00 2001 From: xt Date: Fri, 22 Jan 2021 16:37:12 +0800 Subject: [PATCH 10/28] update LICENSE. --- LICENSE | 360 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 339 insertions(+), 21 deletions(-) diff --git a/LICENSE b/LICENSE index 0fd0680..89e08fb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,339 @@ -MIT License - -Copyright (c) 2021 xt - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. -- Gitee From cf03af007869dbf4b43d21bdb3f5c5567daaeefc Mon Sep 17 00:00:00 2001 From: xt Date: Fri, 22 Jan 2021 16:40:08 +0800 Subject: [PATCH 11/28] develop2 --- Cmd/BCmd.php | 21 ------ Cmd/BuildCmd.php | 36 ---------- Cmd/CCmd.php | 21 ------ Cmd/CleanCmd.php | 34 --------- Cmd/HCmd.php | 21 ------ Cmd/HelpCmd.php | 38 ---------- Cmd/NewCmd.php | 84 ---------------------- Cmd/SCmd.php | 21 ------ Cmd/ServeCmd.php | 19 ----- Templates/{ => base}/ArchiveTemplate.php | 0 Templates/{ => base}/IndexTemplate.php | 0 Templates/{ => base}/PageTemplate.php | 0 Templates/{ => base}/PostTemplate.php | 0 Templates/{ => base}/_config.php | 0 Templates/{ => base}/components/contentCss | 0 Templates/{ => base}/components/navbar | 0 Templates/{ => base}/page/archives.php | 0 Templates/{ => base}/page/index.php | 0 Templates/{ => base}/page/page.html | 0 Templates/{ => base}/page/post.php | 0 20 files changed, 295 deletions(-) delete mode 100644 Cmd/BCmd.php delete mode 100644 Cmd/BuildCmd.php delete mode 100644 Cmd/CCmd.php delete mode 100644 Cmd/CleanCmd.php delete mode 100644 Cmd/HCmd.php delete mode 100644 Cmd/HelpCmd.php delete mode 100644 Cmd/NewCmd.php delete mode 100644 Cmd/SCmd.php delete mode 100644 Cmd/ServeCmd.php rename Templates/{ => base}/ArchiveTemplate.php (100%) rename Templates/{ => base}/IndexTemplate.php (100%) rename Templates/{ => base}/PageTemplate.php (100%) rename Templates/{ => base}/PostTemplate.php (100%) rename Templates/{ => base}/_config.php (100%) rename Templates/{ => base}/components/contentCss (100%) rename Templates/{ => base}/components/navbar (100%) rename Templates/{ => base}/page/archives.php (100%) rename Templates/{ => base}/page/index.php (100%) rename Templates/{ => base}/page/page.html (100%) rename Templates/{ => base}/page/post.php (100%) diff --git a/Cmd/BCmd.php b/Cmd/BCmd.php deleted file mode 100644 index 7b3f425..0000000 --- a/Cmd/BCmd.php +++ /dev/null @@ -1,21 +0,0 @@ - -Here are all of commands: - -- php meo help | h: Get help(just like you now!) -- php meo new \ \: Create a new post -- php meo build | b: Build static files -- php meo serve | s: Start develop server, and check page render when file change -- php meo clean | c: Clean static files and db file - -If you want join us, visit https://gitee.com/xtzero/meoBlog. - -Enjoy yourself~ -"; - } -} diff --git a/Cmd/NewCmd.php b/Cmd/NewCmd.php deleted file mode 100644 index df28b26..0000000 --- a/Cmd/NewCmd.php +++ /dev/null @@ -1,84 +0,0 @@ -"; - file_put_contents($mdFilePath, $defaultContent); - die("Create file success: {$mdFilePath}"); - } - - public static function init() - { - $postPath = POST_DIR; - $pagePath = PAGE_DIR; - if (!is_dir($postPath)) { - mkdir($postPath); - } - if (!is_dir($pagePath)) { - mkdir($pagePath); - } - } -} diff --git a/Cmd/SCmd.php b/Cmd/SCmd.php deleted file mode 100644 index aafde5c..0000000 --- a/Cmd/SCmd.php +++ /dev/null @@ -1,21 +0,0 @@ -listen(); - } -} diff --git a/Templates/ArchiveTemplate.php b/Templates/base/ArchiveTemplate.php similarity index 100% rename from Templates/ArchiveTemplate.php rename to Templates/base/ArchiveTemplate.php diff --git a/Templates/IndexTemplate.php b/Templates/base/IndexTemplate.php similarity index 100% rename from Templates/IndexTemplate.php rename to Templates/base/IndexTemplate.php diff --git a/Templates/PageTemplate.php b/Templates/base/PageTemplate.php similarity index 100% rename from Templates/PageTemplate.php rename to Templates/base/PageTemplate.php diff --git a/Templates/PostTemplate.php b/Templates/base/PostTemplate.php similarity index 100% rename from Templates/PostTemplate.php rename to Templates/base/PostTemplate.php diff --git a/Templates/_config.php b/Templates/base/_config.php similarity index 100% rename from Templates/_config.php rename to Templates/base/_config.php diff --git a/Templates/components/contentCss b/Templates/base/components/contentCss similarity index 100% rename from Templates/components/contentCss rename to Templates/base/components/contentCss diff --git a/Templates/components/navbar b/Templates/base/components/navbar similarity index 100% rename from Templates/components/navbar rename to Templates/base/components/navbar diff --git a/Templates/page/archives.php b/Templates/base/page/archives.php similarity index 100% rename from Templates/page/archives.php rename to Templates/base/page/archives.php diff --git a/Templates/page/index.php b/Templates/base/page/index.php similarity index 100% rename from Templates/page/index.php rename to Templates/base/page/index.php diff --git a/Templates/page/page.html b/Templates/base/page/page.html similarity index 100% rename from Templates/page/page.html rename to Templates/base/page/page.html diff --git a/Templates/page/post.php b/Templates/base/page/post.php similarity index 100% rename from Templates/page/post.php rename to Templates/base/page/post.php -- Gitee From 7d5d6a599d8710cc0d8dc413ccb99ba1609ebfa9 Mon Sep 17 00:00:00 2001 From: xt Date: Fri, 22 Jan 2021 16:40:12 +0800 Subject: [PATCH 12/28] develop2 --- Lib/Cmd/BCmd.php | 21 ++++++++ Lib/Cmd/BuildCmd.php | 36 +++++++++++++ Lib/Cmd/CCmd.php | 21 ++++++++ Lib/Cmd/CleanCmd.php | 34 ++++++++++++ Lib/Cmd/HCmd.php | 21 ++++++++ Lib/Cmd/HelpCmd.php | 38 ++++++++++++++ Lib/Cmd/NewCmd.php | 84 ++++++++++++++++++++++++++++++ Lib/Cmd/SCmd.php | 21 ++++++++ Lib/Cmd/ServeCmd.php | 19 +++++++ Lib/main.php | 2 +- README.md | 3 +- Templates/base/ArchiveTemplate.php | 2 +- Templates/base/IndexTemplate.php | 2 +- Templates/base/PageTemplate.php | 4 +- Templates/base/PostTemplate.php | 4 +- Templates/base/_config.php | 8 +-- composer.json | 2 +- meo | 7 ++- 18 files changed, 313 insertions(+), 16 deletions(-) create mode 100644 Lib/Cmd/BCmd.php create mode 100644 Lib/Cmd/BuildCmd.php create mode 100644 Lib/Cmd/CCmd.php create mode 100644 Lib/Cmd/CleanCmd.php create mode 100644 Lib/Cmd/HCmd.php create mode 100644 Lib/Cmd/HelpCmd.php create mode 100644 Lib/Cmd/NewCmd.php create mode 100644 Lib/Cmd/SCmd.php create mode 100644 Lib/Cmd/ServeCmd.php diff --git a/Lib/Cmd/BCmd.php b/Lib/Cmd/BCmd.php new file mode 100644 index 0000000..7b3f425 --- /dev/null +++ b/Lib/Cmd/BCmd.php @@ -0,0 +1,21 @@ + +Here are all of commands: + +- php meo help | h: Get help(just like you now!) +- php meo new \ \: Create a new post +- php meo build | b: Build static files +- php meo serve | s: Start develop server, and check page render when file change +- php meo clean | c: Clean static files and db file + +If you want join us, visit https://gitee.com/xtzero/meoBlog. + +Enjoy yourself~ +"; + } +} diff --git a/Lib/Cmd/NewCmd.php b/Lib/Cmd/NewCmd.php new file mode 100644 index 0000000..df28b26 --- /dev/null +++ b/Lib/Cmd/NewCmd.php @@ -0,0 +1,84 @@ +"; + file_put_contents($mdFilePath, $defaultContent); + die("Create file success: {$mdFilePath}"); + } + + public static function init() + { + $postPath = POST_DIR; + $pagePath = PAGE_DIR; + if (!is_dir($postPath)) { + mkdir($postPath); + } + if (!is_dir($pagePath)) { + mkdir($pagePath); + } + } +} diff --git a/Lib/Cmd/SCmd.php b/Lib/Cmd/SCmd.php new file mode 100644 index 0000000..aafde5c --- /dev/null +++ b/Lib/Cmd/SCmd.php @@ -0,0 +1,21 @@ +listen(); + } +} diff --git a/Lib/main.php b/Lib/main.php index 71545e7..db3083f 100644 --- a/Lib/main.php +++ b/Lib/main.php @@ -4,7 +4,7 @@ if ($argc == 1) { } $command = $argv[1]; $className = strtoupper(substr($command, 0, 1)) . strtolower(substr($command, 1)) . 'Cmd'; -$cmdFile = "./Cmd/" . $className . '.php'; +$cmdFile = CMD_DIR . "/" . $className . '.php'; if (!file_exists($cmdFile)) { die("command `{$command}` doesn't exist!"); } diff --git a/README.md b/README.md index 54470c8..0607efd 100644 --- a/README.md +++ b/README.md @@ -228,5 +228,4 @@ class OneCmd implements BaseCmd > 这里写的是一些准备要做的事 + `meo b`的时候同步除了md之外的其他文件(主要是图片) -+ 主题用二级文件夹保存,在env增加主题配置 -+ 将除了sources和vendor都挪到Lib里,原有Lib放到更深一级里。方便升级。 + diff --git a/Templates/base/ArchiveTemplate.php b/Templates/base/ArchiveTemplate.php index ee7481f..23fb826 100644 --- a/Templates/base/ArchiveTemplate.php +++ b/Templates/base/ArchiveTemplate.php @@ -1,7 +1,7 @@ EOF; - + file_put_contents($htmlFilePath, $html); } } diff --git a/Templates/base/_config.php b/Templates/base/_config.php index f417bd5..6289811 100644 --- a/Templates/base/_config.php +++ b/Templates/base/_config.php @@ -1,9 +1,9 @@ [ diff --git a/composer.json b/composer.json index 5b51035..320e88d 100644 --- a/composer.json +++ b/composer.json @@ -11,8 +11,8 @@ "require": {}, "autoload": { "psr-4": { - "Cmd\\":".\\Cmd", "Lib\\":".\\Lib", + "Cmd\\":".\\Lib\\Cmd", "Templates\\":".\\Templates" } } diff --git a/meo b/meo index 361b56f..b353227 100755 --- a/meo +++ b/meo @@ -1,17 +1,20 @@ Date: Fri, 22 Jan 2021 17:00:26 +0800 Subject: [PATCH 13/28] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=A6=96=E9=A1=B5?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Templates/base/IndexTemplate.php | 18 +++++++++++++++--- Templates/base/_config.php | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Templates/base/IndexTemplate.php b/Templates/base/IndexTemplate.php index 7f5413f..1874799 100644 --- a/Templates/base/IndexTemplate.php +++ b/Templates/base/IndexTemplate.php @@ -68,9 +68,21 @@ EOF

    {{title}}

    {{subtitle}}

    diff --git a/Templates/base/_config.php b/Templates/base/_config.php index 6289811..4b3cbfb 100644 --- a/Templates/base/_config.php +++ b/Templates/base/_config.php @@ -13,5 +13,23 @@ return [ PostTemplate::class, PageTemplate::class ] + ], + "homepageMenu" => [ + [ + "url" => "/doc", + "title" => "文档" + ], + [ + "url" => "/doc", + "title" => "文档" + ], + [ + "url" => "/doc", + "title" => "文档" + ], + [ + "url" => "/doc", + "title" => "文档" + ] ] ]; -- Gitee From 2587091003def25846cb8598ec81a6f38daac8ab Mon Sep 17 00:00:00 2001 From: xt Date: Fri, 22 Jan 2021 17:05:29 +0800 Subject: [PATCH 14/28] =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=99=A8=20a=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E6=94=B9=E4=B8=BAblank?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/Parser.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/Parser.php b/Lib/Parser.php index fb887ba..9743af9 100644 --- a/Lib/Parser.php +++ b/Lib/Parser.php @@ -425,7 +425,7 @@ class Parser $link = $self->call('parseLink', $matches[1]); return $self->makeHolder( - "{$link}" + "{$link}" ); }, $text @@ -509,7 +509,7 @@ class Parser ); $url = $self->escapeBracket($matches[2]); $url = $self->cleanUrl($url); - return $self->makeHolder("{$escaped}"); + return $self->makeHolder("{$escaped}"); }, $text ); @@ -543,7 +543,7 @@ class Parser "/(^|[^\"])((https?):[\p{L}_0-9-\.\/%#!@\?\+=~\|\,&\(\)]+)($|[^\"])/iu", function ($matches) use ($self) { $link = $self->call('parseLink', $matches[2]); - return "{$matches[1]}{$link}{$matches[4]}"; + return "{$matches[1]}{$link}{$matches[4]}"; }, $text ); -- Gitee From 747f12ed74c4d228ecefa1ea17f350de5433c574 Mon Sep 17 00:00:00 2001 From: xt Date: Fri, 22 Jan 2021 17:23:18 +0800 Subject: [PATCH 15/28] update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0607efd..863ee8b 100644 --- a/README.md +++ b/README.md @@ -228,4 +228,6 @@ class OneCmd implements BaseCmd > 这里写的是一些准备要做的事 + `meo b`的时候同步除了md之外的其他文件(主要是图片) ++ 搜索功能。meo b的时候建立分词数据库。引入分词库。 ++ 重写`meo s`,访问的时候跟着路由去重新处理对应的md。(重新生成静态文件 | 实时读md内容) -- Gitee From 734aab3cd5347ce050f0e4c1599709b938b2cb15 Mon Sep 17 00:00:00 2001 From: xt Date: Sun, 24 Jan 2021 01:27:57 +0800 Subject: [PATCH 16/28] =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=92=8C=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E9=80=BB=E8=BE=91=E5=88=86=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/functions.php | 3 + README.md | 3 +- Templates/base/ArchiveTemplate.php | 57 +---- Templates/base/IndexTemplate.php | 96 +------- Templates/base/PageTemplate.php | 43 ++-- Templates/base/PostTemplate.php | 46 ++-- Templates/base/components/busuanzi | 5 + Templates/base/components/contentCss | 149 +++++++------ Templates/base/components/footer | 20 ++ Templates/base/components/indexNav | 1 + Templates/base/components/postContent | 3 + .../base/page/{archives.php => archive.html} | 9 +- Templates/base/page/{index.php => index.html} | 34 ++- Templates/base/page/page.html | 211 ++---------------- Templates/base/page/post.html | 23 ++ Templates/base/page/post.php | 25 --- composer.json | 4 +- composer.lock | 76 +++++++ 18 files changed, 305 insertions(+), 503 deletions(-) create mode 100644 Templates/base/components/busuanzi create mode 100644 Templates/base/components/footer create mode 100644 Templates/base/components/indexNav create mode 100644 Templates/base/components/postContent rename Templates/base/page/{archives.php => archive.html} (68%) rename Templates/base/page/{index.php => index.html} (52%) create mode 100644 Templates/base/page/post.html delete mode 100644 Templates/base/page/post.php create mode 100644 composer.lock diff --git a/Lib/functions.php b/Lib/functions.php index 4e5922f..debea8d 100644 --- a/Lib/functions.php +++ b/Lib/functions.php @@ -1,4 +1,7 @@ - {$_post['datetime']} {$_post['title']} -
    - "; - } - $html = << - - - - - 文章列表 - - - -EOF -.C(THEME_BASE_DIR . '/components/navbar', [ - "title" => ENV['title'] -]) -.<< -

    文章列表

    - {$postsHtml} -
    - - -EOF; + $html = C(THEME_BASE_DIR . '/page/archive.html', [ + "navbar" => C(THEME_BASE_DIR . '/components/navbar', [ + "title" => ENV['title'] + ]), + "posts" => implode("\n", array_map(function($_post){ + return C(THEME_BASE_DIR . '/components/postContent', $_post); + }, self::getPosts())), + 'footer' => C(THEME_BASE_DIR . '/components/footer', [ + "busuanzi" => C(THEME_BASE_DIR . '/components/busuanzi') + ]) + ]); $archivesFilePath = DIST_DIR . '/archives.html'; if (!file_exists($archivesFilePath)) { touch($archivesFilePath); diff --git a/Templates/base/IndexTemplate.php b/Templates/base/IndexTemplate.php index 1874799..bc86b24 100644 --- a/Templates/base/IndexTemplate.php +++ b/Templates/base/IndexTemplate.php @@ -10,93 +10,19 @@ class IndexTemplate implements BaseTemplate { public static function render($data) { - $html = << - - - - - {{title}} - - - -EOF -.C(THEME_BASE_DIR . '/components/navbar', [ - "title" => ENV['title'] -]) -.<< -

    {{title}}

    -

    {{subtitle}}

    - -
    - - -EOF; - - $replaceArr = [ + $html = C(THEME_BASE_DIR . '/page/index.html', [ 'title' => ENV['title'], 'subtitle' => ENV['subtitle'], - ]; - - foreach ($replaceArr as $k => $v) { - $html = str_replace("{{{$k}}}", $v, $html); - } + 'navbar' => C(THEME_BASE_DIR . '/components/navbar', [ + "title" => ENV['title'] + ]), + 'menu' => implode("\n", array_map(function($v) { + return C(THEME_BASE_DIR . '/components/indexNav', $v); + }, THEME_CONFIG['homepageMenu'])), + 'footer' => C(THEME_BASE_DIR . '/components/footer', [ + "busuanzi" => C(THEME_BASE_DIR . '/components/busuanzi') + ]) + ]); file_put_contents(DIST_DIR . '/' . 'index.html', $html); } diff --git a/Templates/base/PageTemplate.php b/Templates/base/PageTemplate.php index 076f707..1d969c8 100644 --- a/Templates/base/PageTemplate.php +++ b/Templates/base/PageTemplate.php @@ -62,35 +62,20 @@ class PageTemplate implements BaseTemplate $mdFileContent = file_get_contents($filePath); $postSplit = self::splitPost($mdFileContent); - $html = << - - - - - {$postSplit['title']} -EOF -. C(THEME_BASE_DIR . '/components/contentCss') -. << - -EOF -. C(THEME_BASE_DIR . '/components/navbar', [ - "title" => ENV['title'] -]) -. <<