bee

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 268c9d8f8a4db4c98fa41de3c7cd497a0d2a14fd
parent 76e05398ac22a20f0835bb314caeeb6da52f9790
Author: dankert <devnull@localhost>
Date:   Tue,  3 Oct 2017 22:11:40 +0200

Großes Refactoring. Jetzt sieht es schon ganz gut aus, nur das Thema ist immer noch nicht so ganz hübsch.

Diffstat:
.hgignore | 4++++
bee/SiteReader.class.php | 274+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bee/bee.php | 579+++++++++++++++++++++++--------------------------------------------------------
bee/gen/CategoryGenerator.class.php | 30++++++++++++++++++++++++++++++
bee/gen/DateGenerator.class.php | 32++++++++++++++++++++++++++++++++
bee/gen/GeneratorBase.class.php | 32++++++++++++++++++++++++++++++++
bee/gen/HtmlGenerator.class.php | 18++++++++++++++++++
bee/gen/IndexGenerator.class.php | 38++++++++++++++++++++++++++++++++++++++
bee/gen/KeywordGenerator.class.php | 39+++++++++++++++++++++++++++++++++++++++
bee/gen/MarkdownPageGenerator.class.php | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bee/gen/StaticFileGenerator.class.php | 20++++++++++++++++++++
bee/gen/ThemeResourceGenerator.class.php | 23+++++++++++++++++++++++
bee/util/HttpUtil.class.php | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
create.sh | 2+-
site/default/impressum.md | 16++++++++++++++++
site/default/site-config.ini | 13++++++++++---
site/default/start.md | 16++++++++++++++++
theme/default.php | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
theme/default/default.css | 28++++++++++++++++++++++++++--
theme/default/lang_de.php | 1-
20 files changed, 1021 insertions(+), 465 deletions(-)

diff --git a/.hgignore b/.hgignore @@ -0,0 +1,4 @@ +syntax: glob +.settings/ +.project +.buildpath diff --git a/bee/SiteReader.class.php b/bee/SiteReader.class.php @@ -0,0 +1,273 @@ +<?php + +class SiteReader +{ + var $pages = array(); + var $pagesByDate = array(); + var $pagesByKeyword = array(); + var $pagesByUrl = array(); + var $pagesByCategory = array(); + var $keywordNames = array(); + var $categoryNames = array(); + + /** + * Reads all pages of a site. + */ + function readSite() + { + $this->pages = $this->read_all_pages(''); + $this->filter_future_pages(); + $this->filter_unique_page_url(); + + foreach( $this->pages as $page ) + { + $url = $page['url']; + $this->pagesByUrl[$url] = $page; + + $date = $page['date']; + if ( !isset($this->pagesByDate[$date])) + $this->pagesByDate[$date] = array(); + $this->pagesByDate[$date][] = $page; + + $keywords = $page['keywords']; + foreach( $keywords as $keyword ) { + if ( !isset($this->pagesByKeyword[$keyword])) + $this->pagesByKeyword[$keyword] = array(); + $this->pagesByKeyword[$keyword][] = $page; + } + + if ( isset( $page['category'] ) ) + { + $category = $this->urlify($page['category']); + if ( !isset($this->pagesByCategory[$category])) + $this->pagesByCategory[$category] = array(); + $this->pagesByCategory[$category][] = $page; + $this->categoryNames[$category] = $page['category']; + } + } + + + ksort($this->pagesByDate ); + ksort($this->pagesByKeyword ); + ksort($this->pagesByUrl ); + ksort($this->pagesByCategory); + ksort($this->keywordNames ); + ksort($this->categoryNames ); + } + + + /** + * Reads a content file. + * + * @param Filename $filename + * @return Array of pages + */ + function read_page($filename) + { + $file = file(SITE_DIR.SLASH.$filename); + $page = array(); + $fileParts = $this->explode_array('---',$file); + if (isset($fileParts[1])) + { + $pageYaml = spyc_load( implode("\n",$fileParts[1])); + $page['config'] = array_change_key_case($pageYaml); // force lowercase on page attributes + } + + if (isset($fileParts[2])) + { + $page['value'] = implode("\n",$fileParts[2]); + } + $page['filename' ] = $filename; + $page['filemtime'] = filemtime(SITE_DIR.SLASH.$filename); + + + if (isset($page['config']['date'])) + $page['date'] = strtotime($page['config']['date']); + else + $page['date'] = $page['filemtime']; + + // Category + if ( isset( $page['config']['category'] )) + $page['category'] = $page['config']['category']; + + + // keywords is similar to tags + if ( isset( $page['config']['keywords'] )) + $page['config']['tags'] = $page['config']['keywords']; + + if ( isset( $page['config']['tags'] )) + if ( is_array($page['config']['tags']) ) + $page['keywords'] = $this->urlify_array($page['config']['tags']); + else + $page['keywords'] = $this->urlify_array(explode(',',$page['config']['tags'])); + else + $page['keywords'] = array(); + + + + if (!empty($page['config']['url'])) + if ( substr($page['config']['url'],0,1)=='/') + $page['url'] = $this->urlify(substr($page['config']['url'],1)); + else + $page['url'] = dirname($page['filename']).SLASH.$this->urlify($page['config']['url']); + elseif (!empty($page['config']['title'])) + { + if (!empty($page['config']['category'])) + $page['url'] = $this->urlify($page['config']['category']).SLASH.$this->urlify($page['config']['title']); + else + $page['url'] = $this->urlify($page['config']['title']); + } + else + // no title available, so we need to use the filename instead. + if (!empty($page['config']['category'])) + $page['url'] = $this->urlify($page['config']['category']).SLASH.$this->urlify(substr(basename($page['filename']),0,-3)); + else + $page['url'] = $this->urlify(substr(basename($page['filename']),0,-3)); + + if (isset($page['config']['title'])) + $page['title'] = $page['config']['title']; + else + $page['title'] = basename($page['filename']); + + if (isset($page['config']['author'])) + // Username from page config. + $page['author'] = $page['config']['author']; + else + { + // Fallback: Detect username from os. + $uid = posix_getpwuid( fileowner(SITE_DIR.SLASH.$filename) ); + $page['author'] = $uid['name']; + } + + return $page; + } + + + /** + * removes pages which has a future date. + * @param unknown_type $pages + * @return multitype:unknown + */ + function filter_future_pages() + { + $validatedPages = array(); + + foreach( $this->pages as $page ) + { + if ( $page['date'] <= time() ) + $validatedPages[] = $page; + else + ; // pages in the future are not being published yet + } + + $this->pages = $validatedPages; + } + + + /** + * forces that every page url is unique. + * @param unknown_type $pages + * @return multitype:Ambigous <string, unknown> + */ + function filter_unique_page_url() + { + $urls = array(); + $validatedPages = array(); + + $i=1; + + foreach( $this->pages as $page ) + { + $url = $page['url']; + + // Prüfung auf doppelte Dateinamen. Jeder Dateiname muss eindeutig sein. Ggf. wird ein Suffix ergänzt. + while( in_array($url, $urls) ) + { + $url = $page['url'].'-'.++$i; + $page['url'] = $url; + } + $urls[] = $url; + $validatedPages[] = $page; + } + $this->pages = $validatedPages; + } + + + + function read_all_pages( $dir ) + { + $pages = array(); + if ( $handle = opendir(SITE_DIR.SLASH.$dir) ) + { + while (false !== ($entry = readdir($handle))) + { + if ( $entry[0] == '.' ) + continue; + if ( is_dir( SITE_DIR.SLASH.$dir.(!empty($dir)?SLASH:'').$entry )) + { + $pages = array_merge($pages,$this->read_all_pages($dir.(!empty($dir)?SLASH:'').$entry)); + } + if ( is_file( SITE_DIR.SLASH.$dir.(!empty($dir)?SLASH:'').$entry ) && substr($entry,-3)=='.md') + { + $page = $this->read_page($dir.(!empty($dir)?SLASH:'').$entry); + + $pages[] = $page; + } + } + closedir($handle); + } + + return $pages; + } + + + /** + * Creates a slug url out of the filename. + * @param $filename Name + * @return string + */ + function urlify( $filename ) + { + $slug = $filename; + $slug = iconv('utf-8', 'ascii//TRANSLIT', $slug); + $slug = preg_replace('/[^A-Za-z0-9-]+/', '-', $slug); + $slug = trim($slug, '-'); + $slug = strtolower($slug); + + return $slug; + } + + + function urlify_array( $nameArray ) + { + $newNames = array(); + foreach( $nameArray as $name) + { + $newNames[] = $this->urlify($name); + } + return $newNames; + + } + + + function explode_array($sep, $array) + { + $idx = 0; + $target = array(); + $target[$idx] = array(); + + foreach( $array as $line ) + { + if ( trim($line) == $sep ) + { + $target[++$idx] = array(); + continue; + } + $target[$idx][] = $line; + } + + return $target; + } +} + +?>+ \ No newline at end of file diff --git a/bee/bee.php b/bee/bee.php @@ -1,88 +1,105 @@ <?php +error_reporting( E_ALL ); -define('DEVELOPMENT',true); -define('HTML_EXTENSION','.html'); - -$site = array( - 'title'=>'My new Blog', - 'keywords_dir'=>'tags', - 'category_dir'=>'category', - 'sites_dir'=>'site', - 'theme_dir'=>'theme', - 'html_extension'=>false, - 'http_caching'=>true, - 'theme'=>'default' -); +// Include external dependencies. require('./parsedown/ParseDown.php'); // Markdown require('./spyc/Spyc.php'); // YAML -define('SCRIPT',basename($_SERVER['SCRIPT_FILENAME'])); +require('./gen/GeneratorBase.class.php'); +require('./gen/DateGenerator.class.php'); +require('./gen/CategoryGenerator.class.php'); +require('./gen/HtmlGenerator.class.php'); +require('./gen/IndexGenerator.class.php'); +require('./gen/KeywordGenerator.class.php'); +require('./gen/MarkdownPageGenerator.class.php'); +require('./gen/StaticFileGenerator.class.php'); +require('./gen/ThemeResourceGenerator.class.php'); +require('./util/HttpUtil.class.php'); +require('./SiteReader.class.php'); -define('SITES_DIR','../site'); -define('THEME_DIR','../theme'); -define('SLASH' ,'/'); -$querypath = explode('/',$_SERVER['PATH_INFO']); +function init() +{ + define('DEVELOPMENT',true); + define('HTML_EXTENSION','.html'); + + $site = array( + 'title'=>'My new Blog', + 'keywords_dir'=>'tags', + 'category_dir'=>'category', + 'sites_dir'=>'site', + 'theme_dir'=>'theme', + 'html_extension'=>false, + 'http_caching'=>true, + 'theme'=>'default' + ); + + define('SCRIPT',basename($_SERVER['SCRIPT_FILENAME'])); + + define('SITES_DIR','../site'); + define('THEME_DIR','../theme'); + define('SLASH' ,'/'); + + global $querypath; + $querypath = explode('/',$_SERVER['PATH_INFO']); + + define('ROOT_UP',str_repeat('../',substr_count($_SERVER['PATH_INFO'],'/'))); + define('SITE_UP',str_repeat('../',substr_count($_SERVER['PATH_INFO'],'/')-3)); + + array_shift($querypath); // Remove first slash + define('CMD' ,array_shift($querypath)); + define('SITE' ,array_shift($querypath)); +} + +init(); -define('ROOT_UP',str_repeat('../',substr_count($_SERVER['PATH_INFO'],'/'))); -define('SITE_UP',str_repeat('../',substr_count($_SERVER['PATH_INFO'],'/')-3)); - array_shift($querypath); // Remove first slash -define('CMD' ,array_shift($querypath)); -define('SITE' ,array_shift($querypath)); if (is_null(CMD)) { - $sitelinks = ''; - if ( $handle = opendir(SITES_DIR.SLASH.SITE.SLASH.implode('/',PATH)) ) - { - while (false !== ($entry = readdir($handle))) { - if ( $entry[0] != '.' ) - { - $sitelinks.= '<a href="'.ROOT_UP.SCRIPT.'/preview/'.$entry.'">'.$entry.'</a>'; - } - } - closedir($handle); - } - $preview_link = '<a href="'.ROOT_UP.SCRIPT.'/preview'.'">Preview</a>'; - output( 'Menu',$sitelinks); + $options = array('Preview'=>ROOT_UP.SCRIPT.'/preview/'); + output( 'Select an option',$options); } elseif (CMD=='preview') { if ( is_null(SITE) || SITE=='') { - $sitelinks = ''; + $sitelinks = array(); if ( $handle = opendir(SITES_DIR.SLASH.SITE.SLASH.implode('/',PATH)) ) { while (false !== ($entry = readdir($handle))) { if ( $entry[0] != '.' ) { - $sitelinks.= '<a href="'.ROOT_UP.SCRIPT.'/preview/'.$entry.'/">'.$entry.'</a>'; + $sitelinks[$entry] = ROOT_UP.SCRIPT.'/preview/'.$entry.'/'; } } closedir($handle); } - output( 'Sites',$sitelinks); + output( 'Select a site for previewing',$sitelinks); } else { if (!is_dir(SITES_DIR.SLASH.SITE)) { - http(404,"Not found"); // Site does not exist + HttpUtil::sendStatus(404,"Site ".SITE." not found"); // Site does not exist } - if ( is_file(SITES_DIR.SLASH.'site-config.ini')) + if ( is_file(SITES_DIR.SLASH.SITE.SLASH.'site-config.ini')) { - $siteconfig = parse_ini_file(SITES_DIR.SLASH.'site-config.ini'); + $siteconfig = parse_ini_file(SITES_DIR.SLASH.SITE.SLASH.'site-config.ini'); if ( isset($siteconfig['site_dir'])) define('SITE_DIR',$siteconfig['site_dir']); else define('SITE_DIR',SITES_DIR.SLASH.SITE); + if ( isset($siteconfig['theme'])) define('THEME',$siteconfig['theme']); else define('THEME','default'); + + if ( isset($siteconfig['locale'])) + setlocale(LC_ALL,$siteconfig['locale']); } else { @@ -91,432 +108,170 @@ elseif (CMD=='preview') } if ( !is_file(THEME_DIR.SLASH.THEME.'.php') ) - http(501,'Internal Server Error: Theme '.THEME.' not found'); // Theme does not exist + HttpUtil::sendStatus(501,'Internal Server Error: Theme '.THEME.' not found'); // Theme does not exist + // global used arrays. + $PAGES_BY_URL = array(); + $PAGES_BY_DATE = array(); + $PAGES_BY_KEYWORD = array(); + $PAGES_BY_CATEGORY = array(); + $PAGES_RELATED = array(); + $CATEGORY_NAMES = array(); + $KEYWORD_NAMES = array(); + $PAGE = null; - $pages = read_all_pages(''); - $pages_by_url = array(); - $pages_by_date = array(); - $pages_by_keyword = array(); - - foreach( $pages as $page ) - { - $url = $page['url']; - $i=1; - - // Prüfung auf doppelte Dateinamen. Jeder Dateiname muss eindeutig sein. Ggf. wird ein Suffix ergänzt. - while( isset($pages_by_url[$url]) ) - { - $url = $page['url'].'-'.++$i; - } - $pages_by_url[$url] = $page; - $page['url'] = $url; - - $date = $page['date']; - if ( !isset($pages_by_date[$date])) - $pages_by_date[$date] = array(); - $pages_by_date[$date][] = $page; - - $keywords = $page['keywords']; - foreach( $keywords as $keyword ) { - if ( !isset($pages_by_keyword[$keyword])) - $pages_by_keyword[$keyword] = array(); - $pages_by_keyword[$keyword][] = $page; - } - - } - - - ksort($pages_by_date ); - ksort($pages_by_keyword); - ksort($pages_by_url ); - - define('PAGES_BY_DATE' ,$pages_by_date ); - define('PAGES_BY_KEYWORD',$pages_by_keyword); - define('PAGES_BY_URL' ,$pages_by_url ); - define('KEYWORDS' ,array_keys($pages_by_keyword) ); + // read all pages from file system. + readSite(); $filename = implode(SLASH,$querypath); define('PATH',$querypath); + $generator = null; - // Pfad /tag für die Keywords if (count($querypath)==2 && $querypath[0]=='tag' ) { if ( !isset($querypath[1])) - http(410,'Bad request'); // Pfad '/tag' ohne weiteres + HttpUtil::sendStatus(410,'Missing keyword name'); // Pfad '/tag' ohne weiteres + + + $generator = new KeywordGenerator( $querypath[1] ); + } + + elseif (count($querypath)==2 && $querypath[0]=='category' ) + { + if ( !isset($querypath[1])) + HttpUtil::sendStatus(410,'Missing category name'); // Pfad '/category' ohne weiteres + + $generator = new CategoryGenerator( $querypath[1] ); + } + + elseif (count($querypath)>=1 && strlen($querypath[0])==4 && is_numeric(($querypath[0])) ) + { + // By date + $year = intval($querypath[0]); + if ( isset($querypath[1]) ) + { + $month = intval($querypath[1]); + $toYear = $year; + + if ( isset($querypath[2]) ) + { + // by day + $day = intval($querypath[2]); + $toMonth = $month; + $toDay = $day+1; + } + else + { + // by month + $day = 1; + $toDay = 1; + $toMonth = $month+1; + } + + } + else + { + // By year + $day = 1; + $toDay = 1; + $month = 1; + $toMonth = 1; + $toYear = $year+1; + } + + $from = mktime(0,0,0,$month,$day,$year); + $to = mktime(0,0,0,$toMonth,$toDay,$toYear); + $generator = new DateGenerator( $from,$to-1 ); - generate_keyword($querypath[1]); } + // Directory-Listing - if (is_dir(SITES_DIR.SLASH.SITE.SLASH.$filename)) + elseif ( empty($filename)) { - generate_directory(); + $generator = new IndexGenerator(); } + // Static file from theme elseif (is_file(THEME_DIR.SLASH.THEME.SLASH.$filename)) { - // Read a static file from theme (Images, CSS or JS) - lastModified(filemtime(THEME_DIR.SLASH.THEME.SLASH.$filename)); - readfile(THEME_DIR.SLASH.THEME.SLASH.$filename); + $generator = new ThemeResourceGenerator($filename); } + // Static file from site elseif (is_file(SITE_DIR.SLASH.$filename)) { - // Read a static file from site (Images, CSS or JS) - lastModified(filemtime(SITE_DIR.SLASH.$filename)); - readfile(SITE_DIR.SLASH.$filename); + $generator = new StaticFileGenerator($filename); } - elseif (isset($pages_by_url[$filename])) + elseif (isset($PAGES_BY_URL[$filename])) { - generate_markdown_page($pages_by_url[$filename]); + $generator = new MarkdownPageGenerator( $PAGES_BY_URL[$filename] ); } elseif (is_file(SITES_DIR.SLASH.SITE.SLASH.$filename.'.html')) { - generate_html_page(); + $generator = new HtmlGenerator( $filename ); } else { - http(404,'Not Found'); + HttpUtil::sendStatus(404,'Resource not Found'); } + $generator->generate(); + } } else { - http(400,'Unknown command'); + HttpUtil::sendStatus(400,'Unknown command'); } -function generate_directory() -{ - define('INDEX' ,true); - $article = '<div class="directory"'; - - - foreach( array_reverse(PAGES_BY_DATE) as $pages ) - { - foreach( $pages as $page) - { - if ( substr($page['filename'],0,strlen(implode('/',PATH))) == implode('/',PATH) ) - $article .= '<div><a href="'.SITE_UP.$page['url'].'">'.$page['title'].'</a></div>'; - } - } - $article .= '</div>'; - - - define('ARTICLE',$article); - define('TITLE',implode('/',PATH)); - define('UP' ,'../'); - require( THEME_DIR.SLASH.THEME.'.php'); - exit; -} -function generate_markdown_page($page) -{ - lastModified( $page['date'] ); - - $parsedown = new Parsedown(); - - $html = $parsedown->text( $page['value'] ); - - define('ARTICLE',$html); - define('INDEX' ,false); - define('TITLE' ,$page['title']); - define('UP' ,SITE_UP); - define('PAGE' ,$page); - - $relatedPages = array(); - foreach( $page['keywords'] as $keyword ) - { - foreach( PAGES_BY_KEYWORD[$keyword] as $pagesPerKeyword) - { - foreach($pagesPerKeyword as $page) - { - if ( !isset($relatedPages[$page['url']])) - $relatedPages[$page['url']] = 1; - else - $relatedPages[$page['url']] = $relatedPages[$page['url']] + 1; - } - } - } - arsort($relatedPages); - $pages = array(); - foreach( $relatedPages as $url) - { - $pages[] = PAGES_BY_URL[$url]; - } - define('RELATED_PAGES',$pages); - if ( is_file(SITES_DIR.SLASH.SITE.'/site-config.ini')) - extract(parse_ini_file( SITES_DIR.SLASH.SITE.'/site-config.ini'),EXTR_PREFIX_ALL,'site'); - - require( THEME_DIR.SLASH.THEME.'.php'); - - exit; -} -function generate_html_page() -{ - $file = file(SITES_DIR.SLASH.SITE.SLASH.implode('/',PATH).'.html'); - lastModified( filemtime(SITES_DIR.SLASH.SITE.SLASH.implode('/',PATH).'.html') ); - $html = implode("\n",$file); - define('ARTICLE',$html); - define('INDEX' ,false); - define('TITLE' ,implode('/',PATH)); - define('UP' ,SITE_UP); - require( THEME_DIR.SLASH.THEME.'.php'); - exit; -} - - -function generate_keyword( $keyword ) -{ - $html = '<ul>'; - foreach( PAGES_BY_KEYWORD[$keyword] as $page ) - { - $html.='<li><a href="'.SITE_UP.$page['url'].'">'.$page['config']['title'].'</a></li>'; - } - $html .= '</ul>'; - define('ARTICLE',$html); - define('INDEX' ,false); - define('TITLE' ,$keyword); - define('UP' ,'../'); - require( THEME_DIR.SLASH.THEME.'.php'); - exit; -} - - - -function output( $title, $body ) -{ - header('Content-Type: text/html'); - ?> -<html> -<head> -<title><?php echo $title ?></title> -<link rel="stylesheet" href="<?php echo ROOT_UP ?>bee.css"> -</head> -<body> -<h1>Bee Site Generator - <?php echo $title ?></h1> -<?php echo $body ?> -</body></html> -<?php - exit; -} - -/** - * Schickt einen HTTP-Status zum Client und beendet das Skript. - * - * @param Integer $status HTTP-Status (ganzzahlig) (Default: 501) - * @param String $text HTTP-Meldung (Default: 'Internal Server Error') - */ -function http( $status=501,$text='Internal Server Error' ) +function readSite() { - if ( headers_sent() ) - { - echo "$status $text\n$message"; - exit; - } - - header('HTTP/1.0 '.intval($status).' '.$text); - header('Content-Type: text/html'); - echo <<<HTML -<html> -<head><title>$status $text</title></head> -<body> -<h1>$text</h1> -<hr> -<address>Bee</adddress> -</body> -</html> -HTML; - exit; -} - - -function explode_array($sep, $array) -{ - $idx = 0; - $target = array(); - $target[$idx] = array(); - - foreach( $array as $line ) - { - if ( trim($line) == $sep ) - { - $target[++$idx] = array(); - continue; - } - $target[$idx][] = $line; - } + global $PAGES_BY_URL; + global $PAGES_BY_DATE; + global $PAGES_BY_KEYWORD; + global $PAGES_BY_CATEGORY; + global $KEYWORD_NAMES; + global $CATEGORY_NAMES; - return $target; -} - - -function read_page($filename) -{ - $file = file(SITE_DIR.SLASH.$filename); - $page = array(); - $fileParts = explode_array('---',$file); - if (isset($fileParts[1])) - { - $pageYaml = spyc_load( implode("\n",$fileParts[1])); - $page['config'] = array_change_key_case($pageYaml); - } - - if (isset($fileParts[2])) - { - $page['value'] = implode("\n",$fileParts[2]); - } - $page['filename' ] = $filename; - $page['filemtime'] = filemtime(SITE_DIR.SLASH.$filename); + $reader = new SiteReader(); + $reader->readSite(); + $PAGES_BY_URL = $reader->pagesByUrl; + $PAGES_BY_DATE = $reader->pagesByDate; + $PAGES_BY_KEYWORD = $reader->pagesByKeyword; + $PAGES_BY_CATEGORY = $reader->pagesByCategory; - if (isset($page['config']['date'])) - $page['date'] = strtotime($page['config']['date']); - else - $page['date'] = $page['filemtime']; - - if ( isset( $page['config']['tags'] )) - if ( is_array($page['config']['tags']) ) - $page['keywords'] = urlify_array($page['config']['tags']); - else - $page['keywords'] = urlify_array(explode(',',$page['config']['tags'])); - else - $page['keywords'] = array(); - - - - if (!empty($page['config']['url'])) - if ( substr($page['config']['url'],0,1)=='/') - $page['url'] = urlify(substr($page['config']['url'],1)); - else - $page['url'] = dirname($page['filename']).SLASH.urlify($page['config']['url']); - elseif (!empty($page['config']['title'])) - { - if (!empty($page['config']['category'])) - $page['url'] = urlify($page['config']['category']).SLASH.urlify($page['config']['title']); - else - $page['url'] = urlify($page['config']['title']); - } - else - $page['url'] = dirname($page['filename']).SLASH.urlify(substr(basename($page['filename']),0,-3)); + $KEYWORD_NAMES = $reader->keywordNames; + $CATEGORY_NAMES = $reader->categoryNames; - if (isset($page['config']['title'])) - $page['title'] = $page['config']['title']; - else - $page['title'] = basename($page['filename']); - if (isset($page['config']['author'])) - $page['author'] = $page['config']['author']; - else - $page['author'] = posix_getpwuid(fileowner(SITE_DIR.SLASH.$filename))['name']; - - return $page; -} - - -function read_all_pages( $dir ) -{ - $pages = array(); - if ( $handle = opendir(SITE_DIR.SLASH.$dir) ) - { - while (false !== ($entry = readdir($handle))) - { - if ( $entry[0] == '.' ) - continue; - if ( is_dir( SITE_DIR.SLASH.$dir.(!empty($dir)?SLASH:'').$entry )) - { - $pages = array_merge($pages,read_all_pages($dir.(!empty($dir)?SLASH:'').$entry)); - } - if ( is_file( SITE_DIR.SLASH.$dir.(!empty($dir)?SLASH:'').$entry ) && substr($entry,-3)=='.md') - { - $page = read_page($dir.(!empty($dir)?SLASH:'').$entry); - - if ( $page['date'] <= time() ) - $pages[] = $page; - else - ; // pages in the future are not being published yet - } - } - closedir($handle); - } - - return $pages; -} - - - - - -function lastModified( $time ) -{ - - if ( DEVELOPMENT ) return; - - // Conditional-Get eingeschaltet? - $lastModified = substr(date('r',$time -date('Z')),0,-5).'GMT'; - $etag = '"'.md5($lastModified).'"'; - - // Header senden - header('Last-Modified: '.$lastModified ); - header('ETag: ' .$etag ); - - // Die vom Interpreter sonst automatisch gesetzten - // Header uebersteuern - header('Cache-Control: must-revalidate'); - header('Pragma:'); - - // See if the client has provided the required headers - $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false; - $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH'] ) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH'] ) : false; - - if ( !$if_modified_since && !$if_none_match ) - return; - - // At least one of the headers is there - check them - if ( $if_none_match && $if_none_match != $etag ) - return; // etag is there but doesn't match - - if ( $if_modified_since && $if_modified_since != $lastModified ) - return; // if-modified-since is there but doesn't match - - // Der entfernte Browser bzw. Proxy holt die Seite nun aus seinem Cache - header('HTTP/1.0 304 Not Modified'); - exit; // Sofortiges Skript-Ende -} - - - -/** - * Macht aus einem Namen eine korrekte, lesebare URL. - * @param $filename Name - * @return string - */ -function urlify( $filename ) -{ - // thx https://stackoverflow.com/questions/2955251/php-function-to-make-slug-url-string - $slug = strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $filename))); - return $slug; } -function urlify_array( $nameArray ) +function output( $text, $options ) { - $newNames = array(); - foreach( $nameArray as $name) - { - $newNames[] = urlify($name); - } - return $newNames; - + echo '<html><head><meta charset="utf-8"><title>'.$text.' - Bee Static Site Generator</title>'; + echo '<style>'; + readfile('bee.css'); + echo '</style>'; + echo '</head>'; + echo '<body>'; + echo '<h1>'.$text.'</h1><ul>'; + foreach( $options as $option=>$url ) + { + echo '<li><a href="'.$url.'">'.$option.'</a></li>'; + } + echo '</ul></body>'; + echo '</html>'; } - ?> \ No newline at end of file diff --git a/bee/gen/CategoryGenerator.class.php b/bee/gen/CategoryGenerator.class.php @@ -0,0 +1,29 @@ +<?php + +class CategoryGenerator extends GeneratorBase +{ + var $category; + + function __construct( $category ) + { + $this->category = $category; + } + function generate() + { + global $PAGES_BY_CATEGORY; + + if ( ! isset($PAGES_BY_CATEGORY[$this->category]) ) + HttpUtil::sendStatus(404,"Category not found"); + + $html = '<ul>'; + foreach( $PAGES_BY_CATEGORY[$this->category] as $page ) + $html.='<li><a href="'.SITE_UP.$page['url'].'">'.$page['config']['title'].'</a></li>'; + $html .= '</ul>'; + define('CONTENT',$html); + define('TITLE' ,$keyword); + $this->outputTheme(); + } +} + + +?>+ \ No newline at end of file diff --git a/bee/gen/DateGenerator.class.php b/bee/gen/DateGenerator.class.php @@ -0,0 +1,31 @@ +<?php + +class DateGenerator extends GeneratorBase +{ + var $category; + + function __construct( $from,$to ) + { + $this->from = $from; + $this->to = $to; + } + + function generate() + { + global $PAGES_BY_DATE; + + $html = '<ul>'; + foreach( $PAGES_BY_DATE as $pageOfDate ) + foreach( $pageOfDate as $page ) + if ( $page['date'] >= $this->from && $page['date'] <= $this->to ) + $html.='<li><a href="'.SITE_UP.$page['url'].'">'.$page['config']['title'].'</a></li>'; + $html .= '</ul>'; + define('CONTENT',$html); + define('TITLE' ,'from '.date('r',$this->from).' to '.date('r',$this->to)); + + $this->outputTheme(); + } +} + + +?>+ \ No newline at end of file diff --git a/bee/gen/GeneratorBase.class.php b/bee/gen/GeneratorBase.class.php @@ -0,0 +1,31 @@ +<?php + +class GeneratorBase +{ + function generate() + { + + } + + + function outputTheme() + { + global $PAGES_BY_CATEGORY; + global $PAGES_BY_KEYWORD; + global $PAGES_BY_DATE; + global $PAGES_RELATED; + global $PAGE; + global $KEYWORD_NAMES; + global $CATEGORY_NAMES; + + global $siteconfig; + extract($siteconfig,EXTR_PREFIX_ALL,'site'); + + require( THEME_DIR.SLASH.THEME.'.php'); + exit; + } + +} + + +?>+ \ No newline at end of file diff --git a/bee/gen/HtmlGenerator.class.php b/bee/gen/HtmlGenerator.class.php @@ -0,0 +1,17 @@ +<?php + +class HtmlGenerator extends GeneratorBase +{ + function generate() + { + $file = file(SITES_DIR.SLASH.SITE.SLASH.implode('/',PATH).'.html'); + + lastModified( filemtime(SITES_DIR.SLASH.SITE.SLASH.implode('/',PATH).'.html') ); + $html = implode("\n",$file); + define('ARTICLE',$html); + define('TITLE' ,implode('/',PATH)); + $this->outputTheme(); + exit; + } + +}+ \ No newline at end of file diff --git a/bee/gen/IndexGenerator.class.php b/bee/gen/IndexGenerator.class.php @@ -0,0 +1,37 @@ +<?php + +class IndexGenerator extends GeneratorBase +{ + var $count = 20; + + function generate() + { + define('INDEX' ,true); + $article = '<ul>'; + + global $PAGES_BY_DATE; + + $nr = 0; + foreach( array_reverse($PAGES_BY_DATE) as $pagesByDate ) + { + foreach( $pagesByDate as $page) + { + if ( ++$nr > $this->count ) + break; + + $article .= '<li><a href="'.SITE_UP.$page['url'].'">'.$page['title'].'</a></li>'; + } + } + $article .= '</ul>'; + + + define('CONTENT',$article); + define('TITLE','Index'); + + $this->outputTheme(); + exit; + } +} + + +?>+ \ No newline at end of file diff --git a/bee/gen/KeywordGenerator.class.php b/bee/gen/KeywordGenerator.class.php @@ -0,0 +1,38 @@ +<?php + +class KeywordGenerator extends GeneratorBase +{ + + var $keyword; + + function __construct( $keyword ) + { + $this->keyword = $keyword; + } + + + function generate() + { + global $PAGES_BY_KEYWORD; + + if ( ! isset($PAGES_BY_KEYWORD[$this->keyword]) ) + HttpUtil::sendStatus(404,"Category not found"); + + $html = '<ul>'; + + foreach( $PAGES_BY_KEYWORD[$this->keyword] as $page ) + { + $html.='<li><a href="'.SITE_UP.$page['url'].'">'.$page['config']['title'].'</a></li>'; + } + $html .= '</ul>'; + define('CONTENT',$html); + define('TITLE' ,'Keyword '.$this->keyword); + + $this->outputTheme(); + exit; + } + +} + + +?>+ \ No newline at end of file diff --git a/bee/gen/MarkdownPageGenerator.class.php b/bee/gen/MarkdownPageGenerator.class.php @@ -0,0 +1,77 @@ +<?php + +class MarkdownPageGenerator extends GeneratorBase +{ + var $page; + + function __construct( $page ) + { + $this->page = $page; + } + + function generate() + { + + HttpUtil::lastModified( $this->page['date'] ); + + $parsedown = new Parsedown(); + + $html = $parsedown->text( $this->page['value'] ); + + define('CONTENT',$html); + define('TITLE' ,$this->page['title']); + global $PAGE; + $PAGE = $this->page; + + global $PAGES_RELATED; + $PAGES_RELATED = $this->get_related_pages(); + + + if ( is_file(SITES_DIR.SLASH.SITE.'/site-config.ini')) + extract(parse_ini_file( SITES_DIR.SLASH.SITE.'/site-config.ini'),EXTR_PREFIX_ALL,'site'); + + $this->outputTheme(); + + exit; + } + + + + + /** + * Determine related pages. + */ + function get_related_pages() + { + global $PAGES_BY_KEYWORD; + global $PAGES_BY_URL; + + $relatedPages = array(); + foreach( $this->page['keywords'] as $keyword ) + { + foreach( $PAGES_BY_KEYWORD[$keyword] as $page) + { + if ( $page['url'] == $this->page['url'] ) + continue; // only other sites are related. + + if ( !isset($relatedPages[$page['url']])) + $relatedPages[$page['url']] = 1; + else + $relatedPages[$page['url']] = $relatedPages[$page['url']] + 1; + } + } + arsort($relatedPages); + $pages = array(); + foreach( $relatedPages as $url=>$count) + { + $pages[] = $PAGES_BY_URL[$url]; + } + + return $pages; + } + + + +} + +?>+ \ No newline at end of file diff --git a/bee/gen/StaticFileGenerator.class.php b/bee/gen/StaticFileGenerator.class.php @@ -0,0 +1,19 @@ +<?php + +class StaticFileGenerator extends GeneratorBase +{ + var $filename; + + function __construct( $filename ) + { + $this->filename = $filename; + } + + function generate() + { + // Read a static file from site (Images, CSS or JS) + header('Content-Type: application/octet-stream'); + HttpUtil::lastModified(filemtime(SITE_DIR.SLASH.$this->filename)); + readfile(SITE_DIR.SLASH.$this->filename); + } +}+ \ No newline at end of file diff --git a/bee/gen/ThemeResourceGenerator.class.php b/bee/gen/ThemeResourceGenerator.class.php @@ -0,0 +1,22 @@ +<?php + +class ThemeResourceGenerator extends GeneratorBase +{ + var $filename; + + function __construct( $filename ) + { + $this->filename = $filename; + } + + + function generate() + { + // Read a static file from theme (Images, CSS or JS) + HttpUtil::lastModified(filemtime(THEME_DIR.SLASH.THEME.SLASH.$this->filename)); + readfile(THEME_DIR.SLASH.THEME.SLASH.$this->filename); + } +} + + +?>+ \ No newline at end of file diff --git a/bee/util/HttpUtil.class.php b/bee/util/HttpUtil.class.php @@ -0,0 +1,78 @@ +<?php + +class HttpUtil +{ + + /** + * Schickt einen HTTP-Status zum Client und beendet das Skript. + * + * @param Integer $status HTTP-Status (ganzzahlig) (Default: 501) + * @param String $text HTTP-Meldung (Default: 'Internal Server Error') + */ + static function sendStatus( $status=501,$text='Internal Server Error' ) + { + if ( headers_sent() ) + { + echo "$status $text\n$message"; + exit; + } + + header('HTTP/1.0 '.intval($status).' '.$text); + header('Content-Type: text/html'); + echo <<<HTML + <html> + <head><title>$status $text</title></head> + <body> + <h1>$text</h1> + <hr> + <address>Bee</adddress> + </body> + </html> +HTML; + exit; + } + + + static function lastModified( $time ) + { + + if ( DEVELOPMENT ) return; + + // Conditional-Get eingeschaltet? + $lastModified = substr(date('r',$time -date('Z')),0,-5).'GMT'; + $etag = '"'.md5($lastModified).'"'; + + // Header senden + header('Last-Modified: '.$lastModified ); + header('ETag: ' .$etag ); + + // Die vom Interpreter sonst automatisch gesetzten + // Header uebersteuern + header('Cache-Control: must-revalidate'); + header('Pragma:'); + + // See if the client has provided the required headers + $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false; + $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH'] ) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH'] ) : false; + + if ( !$if_modified_since && !$if_none_match ) + return; + + // At least one of the headers is there - check them + if ( $if_none_match && $if_none_match != $etag ) + return; // etag is there but doesn't match + + if ( $if_modified_since && $if_modified_since != $lastModified ) + return; // if-modified-since is there but doesn't match + + // Der entfernte Browser bzw. Proxy holt die Seite nun aus seinem Cache + header('HTTP/1.0 304 Not Modified'); + exit; // Sofortiges Skript-Ende + } +} + + + + + +?>+ \ No newline at end of file diff --git a/create.sh b/create.sh @@ -27,7 +27,7 @@ echo "Date: $UTC" >> $FILE echo "Keywords: $TITLE" >> $FILE echo "Category: $TITLE" >> $FILE echo "Url:" >> $FILE -echo "Author: " >> $FILE +echo "Author: `whoami`" >> $FILE echo "---" >> $FILE echo "" >> $FILE diff --git a/site/default/impressum.md b/site/default/impressum.md @@ -0,0 +1,15 @@ +--- +layout: default +title: Impressum +url: +published: true +category: About +tags: +date: 2017-01-01 22:00:00 +author: Someone +redirect: +--- + +This is the blog of ... + +This page is necessary for german blogs because of the TDG.+ \ No newline at end of file diff --git a/site/default/site-config.ini b/site/default/site-config.ini @@ -1,4 +1,12 @@ -title = Bee Static Site generator +; Theme theme = default -site_lang_related_pages=Ähnliche Seiten- \ No newline at end of file + +; Title of your site +title = yet another blog + +; you may overwrite some language settings +;lang_related_pages=Related Pages + +; setting the locale +locale = EN diff --git a/site/default/start.md b/site/default/start.md @@ -0,0 +1,15 @@ +--- +layout: default +title: My first blog entry +url: +published: true +category: Hobby +tags: Blog +date: 2017-01-01 21:00:00 +author: Someone +redirect: +--- + +This is my first blog entry. + +Thank you for reading.+ \ No newline at end of file diff --git a/theme/default.php b/theme/default.php @@ -1,12 +1,19 @@ <?php $lang = array( - 'recent_pages'=>'Recent Pages', - 'related_pages'=>'Related Pages', - 'keywords'=>'Keywords', - 'categories'=>'Categories', - 'date_format'=>'d.m.Y \u\m G:i \U\h\r' + 'site_lang_recent_pages' =>'Recent Pages', + 'site_lang_related_pages'=>'Related Pages', + 'site_lang_keywords' =>'Keywords', + 'site_lang_categories' =>'Categories', + 'site_lang_calendar' =>'Calendar', + 'site_lang_from' =>'from', + 'site_lang_to' =>'to', + 'site_lang_in' =>'in', + 'site_date_format' =>'%x', + 'site_date_format_full' =>'%x', + 'site_footer_category' =>'about', + 'site_title' =>'My Blog' ); -extract($lang,EXTR_PREFIX_ALL,'site_lang'); +extract($lang,EXTR_SKIP); ?><html lang="de"> <head> @@ -16,90 +23,156 @@ extract($lang,EXTR_PREFIX_ALL,'site_lang'); <link rel="stylesheet" href="<?php echo SITE_UP ?>default.css"> </head> <body> -<header id="name"> -<h1><?php echo TITLE ?></h1> + +<header> + <h1><?php echo $site_title ?></h1> </header> <article> -<span class="date"><?php echo date($site_lang_date_format,PAGE['date']) ?></span> -<span class="author"><?php echo PAGE['author'] ?></span> -<span class="category"><?php echo PAGE['author'] ?></span> -<span class="keywords"><?php foreach( PAGE['keywords'] as $keyword ) { ?><a href="<?php echo UP.$keyword ?>"><?php echo $keyword ?></a><?php } ?></span> - -<?php echo ARTICLE ?> + <header id="name"> + <h1><?php echo TITLE ?></h1> + + <?php if ( is_array($PAGE) ) { ?> + <span class="date"><?php echo strftime($site_date_format_full,$PAGE['date']) ?></span> + <span class="author"><?php echo $PAGE['author'] ?></span> + <span class="category"><a href="<?php echo SITE_UP.'category'.SLASH.$PAGE['category'] ?>"><?php echo $PAGE['category'] ?></a></span> + <ul class="keywords"><?php foreach( $PAGE['keywords'] as $keyword ) { ?><li><a href="<?php echo SITE_UP.'tag'.SLASH.$keyword ?>"><?php echo $keyword ?></a></li><?php } ?></ul> + <?php } ?> + </header> + + <?php echo CONTENT ?> </article> + +<?php if ( is_array($PAGE) ) { ?> <nav id="related"> <header> <h1><?php echo $site_lang_related_pages ?></h1> </header> <ul> -<?php foreach( PAGES_RELATED as $page ) +<?php foreach( $PAGES_RELATED as $page ) {?> - <li><a href="<?php $page['url'] ?>"> - <span class="date"><?php echo date('r',$page['date']) ?></span> - <span class="title"><?php echo $page['title'] ?></span> - </a></li> + <li> + <a href="<?php echo SITE_UP.$page['url'] ?>"><?php echo $page['title'] ?></a> + </li> <?php } ?> </ul> </nav> +<?php } ?> + + +<nav id="categories"> +<header> +<h1><?php echo $site_lang_categories ?></h1> +</header> + +<ul> +<?php $count=0; +foreach( $PAGES_BY_CATEGORY as $category=>$pages) + { ?> + <li><a href="<?php echo SITE_UP.'category/'.$category ?>"><?php echo $CATEGORY_NAMES[$category] ?></a></li> + <?php } ?> +</ul> +</nav> + <nav id="recent"> <header> -<h1><?php $site_lang_recent_pages ?></h1> +<h1><?php echo $site_lang_recent_pages ?></h1> </header> <ul> -<?php $count=0; foreach( PAGES_BY_DATE as $page) - { if ($count++ > 10) break; ?> +<?php $count=0; foreach( array_reverse($PAGES_BY_DATE) as $pagesOnDate) + { + foreach( $pagesOnDate as $page ) + { + + if ($count++ > 10) break; ?> <li> - <span class="date"><?php echo date('r',$page['date']) ?></span> - <span class="title"><?php echo $page['title'] ?></span> - </li> - <?php } ?> + <a href="<?php echo SITE_UP.$page['url'] ?>"><?php echo $page['title'] ?></a> + </li> + <?php }} ?> </ul> </nav> -<nav id="keywords"> -<header> -<h1><?php $site_lang_keywords ?></h1> -</header> -<?php $count=0; foreach( KEYWORDS as $keyword) - { ?> - <span style="font-size:1em;"><a href="<?php echo UP.$keyword ?>"><?php echo $keyword ?></a> - </span> - <?php } ?> +<nav id="calendar"> + <header> + <h1><?php echo $site_lang_calendar ?></h1> + </header> -</nav> +<ul class="year"> +<?php $oldYear = 0; foreach( array_reverse($PAGES_BY_DATE,true) as $date => $pagesOnDate) + { + $year = date('Y',$date); + if ( $year != $oldYear ) + { + $oldYear = $year; + ?> + <li> + <a href="<?php echo SITE_UP.$year ?>"><?php echo $year ?></a> + <ul class="month"> + <?php + $oldMonth = 0; + foreach( array_reverse($PAGES_BY_DATE,true) as $date2 => $pagesOnDate) + { + $y = date('Y',$date2); + if ( $y != $year) + continue; + + $month = date('m',$date2); + if ( $month != $oldMonth ) + { + $oldMonth = $month; + + ?> + <li> + <a href="<?php echo SITE_UP.$year.SLASH.$month ?>"><?php echo date('F',$date2) ?></a> + </li> + <?php }} ?> + </ul> + </li> + + <?php } } ?> +</ul> +</nav> -<nav id="categories"> +<nav id="keywords"> <header> -<h1><?php $site_lang_categories ?></h1> +<h1><?php echo $site_lang_keywords ?></h1> </header> -<?php $count=0; foreach( KEYWORDS as $keyword) - { ?> - <span style="font-size:1em;"><a href="<?php echo UP.'tag/'.$keyword ?>"><?php echo $keyword ?></a> - </span> + +<ul> +<?php $count=0; foreach( $PAGES_BY_KEYWORD as $keyword=>$pages) + { + $fontsize = min(3,((count($pages)-1)/5)+1); ?> + + <li style="font-size:<?php echo $fontsize ?>em;"><a href="<?php echo SITE_UP.'tag/'.$keyword ?>"><?php echo $keyword ?></a> + </li> <?php } ?> +</ul> </nav> + + <footer> <ul> -<li><a href="<?php echo UP ?>datenschutz">Datenschutz</a></li> -<li><a href="<?php echo UP ?>impressum">Impressum</a></li> +<?php foreach( $PAGES_BY_CATEGORY[$site_footer_category] as $page) {?> +<li><a href="<?php echo SITE_UP.$page['url'] ?>"><?php echo $page['title']?></a></li> +<?php } ?> </ul> </footer> -</body>- \ No newline at end of file +</body> +</html> diff --git a/theme/default/default.css b/theme/default/default.css @@ -13,6 +13,29 @@ section { display: block; } +footer { + display:block; + background-color:silver; padding:20px; +} + +body > header +{ + width:100%; + background:silver; +} + +nav#recent,nav#calendar,nav#keywords,nav#categories { + float:right; + width:33%; + clear:right; +} + +article,nav#related +{ + float:left; + width:66.9%; + clear:left; +} h1 { font-size: 2em; margin: 0.67em 0; @@ -34,8 +57,9 @@ body { background-color: DimGray; color:white; width: 100%; - max-width:95em; - padding:1em; + xmax-width:95em; + padding:0; + margin:0; } html { width:100%; box-sizing: border-box; } diff --git a/theme/default/lang_de.php b/theme/default/lang_de.php @@ -1 +0,0 @@ -<?php