diff --git a/content/00-welcome/03-markdown-test.md b/content/00-welcome/03-markdown-test.md index b6458bb..6678c47 100644 --- a/content/00-welcome/03-markdown-test.md +++ b/content/00-welcome/03-markdown-test.md @@ -137,15 +137,19 @@ Or you can use a shortcut like http://typemill.net. ```` The same rules as with links, but with a ! ![alt-text](media/markdown.png) +*With caption* ![alt-text](media/markdown.png "my title"){#myid .imgClass} +*With caption that spans over several lines* ![alt-text](media/markdown.png "my title"){#myid .otherclass width=150px} ```` The same rules as with links, but with a ! -![alt-text](media/markdown.png) +![alt-text](media/markdown.png){.center} +*With Caption* -![alt-text](media/markdown.png "my title"){#myid .imgClass} +![alt-text](media/markdown.png "my title"){#myid .imgClass} +*With a caption that spans over several lines.* ![alt-text](media/markdown.png "my title"){#myid .otherclass width=150px} @@ -165,21 +169,27 @@ You can link an image with a nested syntax like this: ```` You can controll the image position with the classes .left, .right and .middle like this: ![alt-text](media/markdown.png){.left} +*With caption that spans over several lines* ![alt-text](media/markdown.png){.right} -![alt-text](media/markdown.png){.middle} +*With caption that spans over several lines* +![alt-text](media/markdown.png){.center} +*With caption that spans over several lines* ```` ![image float left](media/markdown.png){.left} +*With caption that spans over several lines* -The first image should float on the left side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog.) +The first image should float on the left side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "center". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog). Images in a separate line are rendered with the html5 elements `figure` and `figcapture`. ![image float right](media/markdown.png){.right} +*With caption that spans over several lines* -The second image should float on the right side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog.) +The second image should float on the right side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "center". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog). Images in a separate line are rendered with the html5 elements `figure` and `figcapture`. -![image middle](media/markdown.png){.middle} +![image center](media/markdown.png){.center} +*With caption that spans over several lines* -The thirds image should be placed above this paragraph and centered to the middle of the content area. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle". +The thirds image should be placed above this paragraph and centered to the middle of the content area. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "center". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog). Images in a separate line are rendered with the html5 elements `figure` and `figcapture`. ## Blockquote @@ -239,6 +249,22 @@ Orange +## Notices + +You can create different notices if you add a '!', '!!', '!!!', '!!!!' before a line. This will wrap the content into a div-class with the classes `notice1`, `notice2`, `notice3` and `notice4`. You can also span notices over several lines. This logic follows some other CMS like Grav, Lektor or Yellow and it is not compatible with other markdown processors or editors. + +! Notice 1 +! +! Please note that you can use **markdown** inside of the notice so you can *format* your text here. + +!! Notice 2 +!! +!! Please note that you can use **markdown** inside of the notice so you can *format* your text here. + +!!! Notice 3 +!!! +!!! Please note that you can use **markdown** inside of the notice so you can *format* your text here. + ## Tables ```` diff --git a/system/Controllers/PageController.php b/system/Controllers/PageController.php index 26e23b9..5395943 100644 --- a/system/Controllers/PageController.php +++ b/system/Controllers/PageController.php @@ -72,6 +72,29 @@ class PageController extends Controller exit(1); } + # get meta-Information + $writeMeta = new WriteMeta(); + $theme = $settings['theme']; + + # check if there is a custom theme css + $customcss = $writeMeta->checkFile('cache', $theme . '-custom.css'); + if($customcss) + { + $this->c->assets->addCSS($base_url . '/cache/' . $theme . '-custom.css'); + } + + $logo = false; + if(isset($settings['logo']) && $settings['logo'] != '') + { + $logo = 'media/files/' . $settings['logo']; + } + + $favicon = false; + if(isset($settings['favicon']) && $settings['favicon'] != '') + { + $favicon = true; + } + # get the cached navigation here (structure without hidden files ) $navigation = $cache->getCache('cache', 'navigation.txt'); if(!$navigation) @@ -99,7 +122,19 @@ class PageController extends Controller # if there is still no item, return a 404-page if(!$item) { - return $this->render404($response, array( 'navigation' => $navigation, 'settings' => $settings, 'base_url' => $base_url )); + return $this->render404($response, array( + 'navigation' => $navigation, + 'settings' => $settings, + 'base_url' => $base_url, + 'title' => false, + 'content' => false, + 'item' => false, + 'breadcrumb' => false, + 'metatabs' => false, + 'image' => false, + 'logo' => $logo, + 'favicon' => $favicon + )); } if(!$item->hide) @@ -151,9 +186,6 @@ class PageController extends Controller # dispatch the original content without plugin-manipulations for case anyone wants to use it $this->c->dispatcher->dispatch('onOriginalLoaded', new OnOriginalLoaded($contentMD)); - # get meta-Information - $writeMeta = new WriteMeta(); - # makes sure that you always have the full meta with title, description and all the rest. $metatabs = $writeMeta->completePageMeta($contentMD, $settings, $item); @@ -174,10 +206,7 @@ class PageController extends Controller /* parse markdown-file to content-array */ $contentArray = $parsedown->text($contentMD, $itemUrl); $contentArray = $this->c->dispatcher->dispatch('onContentArrayLoaded', new OnContentArrayLoaded($contentArray))->getData(); - - /* get the first image from content array */ - $firstImage = $this->getFirstImage($contentArray); - + /* parse markdown-content-array to content-string */ $contentHTML = $parsedown->markup($contentArray, $itemUrl); $contentHTML = $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData(); @@ -188,40 +217,33 @@ class PageController extends Controller $contentHTML = isset($contentParts[1]) ? $contentParts[1] : $contentHTML; - /* get url and alt-tag for first image, if exists */ - if($firstImage) + # get the first image from content array */ + $img_url = isset($metatabs['meta']['heroimage']) ? $metatabs['meta']['heroimage'] : false; + $img_alt = isset($metatabs['meta']['heroimagealt']) ? $metatabs['meta']['heroimagealt'] : false; + + # get url and alt-tag for first image, if exists */ + if(!$img_url OR $img_url == '') { - preg_match('#\((.*?)\)#', $firstImage, $img_url); - if($img_url[1]) + # extract first image from content + $firstImageMD = $this->getFirstImage($contentArray); + + if($firstImageMD) { - preg_match('#\[(.*?)\]#', $firstImage, $img_alt); + preg_match('#\((.*?)\)#', $firstImageMD, $img_url_result); + $img_url = isset($img_url_result[1]) ? $img_url_result[1] : false; - $firstImage = array('img_url' => $base_url . '/' . $img_url[1], 'img_alt' => $img_alt[1]); + if($img_url) + { + preg_match('#\[(.*?)\]#', $firstImageMD, $img_alt_result); + $img_alt = isset($img_alt_result[1]) ? $img_alt_result[1] : false; + } } } - $theme = $settings['theme']; + $firstImage = array('img_url' => $base_url . '/' . $img_url, 'img_alt' => $img_alt); + $route = empty($args) && isset($settings['themes'][$theme]['cover']) ? '/cover.twig' : '/index.twig'; - # check if there is a custom theme css - $customcss = $writeMeta->checkFile('cache', $theme . '-custom.css'); - if($customcss) - { - $this->c->assets->addCSS($base_url . '/cache/' . $theme . '-custom.css'); - } - - $logo = false; - if(isset($settings['logo']) && $settings['logo'] != '') - { - $logo = 'media/files/' . $settings['logo']; - } - - $favicon = false; - if(isset($settings['favicon']) && $settings['favicon'] != '') - { - $favicon = true; - } - return $this->render($response, $route, [ 'home' => $home, 'navigation' => $navigation, diff --git a/system/Extensions/ParsedownExtension.php b/system/Extensions/ParsedownExtension.php index e7b736f..a40b7fd 100644 --- a/system/Extensions/ParsedownExtension.php +++ b/system/Extensions/ParsedownExtension.php @@ -22,9 +22,12 @@ class ParsedownExtension extends \ParsedownExtra $this->inlineMarkerList .= '\\'; $this->inlineMarkerList .= '$'; + $this->BlockTypes['!'][] = 'Image'; + $this->BlockTypes['!'][] = "Notice"; + $this->visualMode = false; - # table of content support + # identify Table Of contents after footnotes and links array_unshift($this->BlockTypes['['], 'TableOfContents'); } @@ -90,6 +93,148 @@ class ParsedownExtension extends \ParsedownExtra return $footnotes; } + # BlockImages + protected function blockImage($line, $block) + { + if (preg_match('/^\!\[/', $line['text'], $matches)) + { + + $Block = array( + 'element' => array( + 'name' => 'figure', + 'elements' => array( + ) + ), + ); + + $Elements = array( + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $line['text'], + 'destination' => 'elements', + ) + ); + + if (preg_match('/[ ]*{('.$this->regexAttribute.'+)}/', $line['text'], $matches, PREG_OFFSET_CAPTURE)) + { + $attributeString = $matches[1][0]; + $dataAttributes = $this->parseAttributeData($attributeString); + + # move classes and ids from img to the figure element + $figureAttributes = array(); + if(isset($dataAttributes['class'])) + { + $figureAttributes['class'] = $dataAttributes['class']; + $classes = explode(' ', $dataAttributes['class']); + foreach($classes as $class) + { + $attributeString = str_replace('.'.$class, '', $attributeString); + } + } + if(isset($dataAttributes['id'])) + { + $figureAttributes['id'] = $dataAttributes['id']; + $attributeString = str_replace('#'.$dataAttributes['id'], '', $attributeString); + } + + $attributeString = trim(str_replace(' ', ' ', $attributeString)); + $line['text'] = substr($line['text'], 0, $matches[0][1]); + if(str_replace(' ', '', $attributeString) != '') + { + $line['text'] .= '{' . $attributeString . '}'; + } + + $Block['element']['attributes'] = $figureAttributes; + + $Elements['handler']['argument'] = $line['text']; + } + + $Block['element']['elements'][] = $Elements; + + return $Block; + } + } + + protected function blockImageContinue($line, $block) + { + if (isset($block['complete'])) + { + return; + } + + # A blank newline has occurred, so it is a new content-block and not a caption + if (isset($block['interrupted'])) + { + return; + } + + $block['element']['elements'][] = array( + 'name' => 'figcaption', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $line['text'], + 'destination' => 'elements', + ) + ); + + $block['complete'] = true; + + return $block; + } + + protected function blockImageComplete($block) + { + return $block; + } + + # Handle notice blocks + # adopted from grav: https://github.com/getgrav/grav-plugin-markdown-notices/blob/develop/markdown-notices.php + # and yellow / datenstrom: https://raw.githubusercontent.com/datenstrom/yellow-extensions/master/features/markdown/markdownx.php + protected function blockNotice($Line, $Block) + { + if (preg_match("/^!(?!\[)[ ]?+(.*+)/", $Line["text"], $matches)) + { + $level = strspn(str_replace(array("![", " "), "", $Line["text"]), "!"); + $text = substr($matches[0], $level); + + $Block = [ + 'element' => [ + 'name' => 'div', + 'handler' => array( + 'function' => 'linesElements', + 'argument' => (array) $text, + 'destination' => 'elements', + ), + 'attributes' => [ + 'class' => "notice$level", + ], + ], + ]; + + return $Block; + } + } + + # Handle notice blocks over multiple lines + # adopted from grav: https://github.com/getgrav/grav-plugin-markdown-notices/blob/develop/markdown-notices.php + # and yellow / datenstrom: https://raw.githubusercontent.com/datenstrom/yellow-extensions/master/features/markdown/markdownx.php + protected function blockNoticeContinue($Line, $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if (preg_match("/^!(?!\[)[ ]?+(.*+)/", $Line["text"], $matches) ) + { + $level = strspn(str_replace(array("![", " "), "", $Line["text"]), "!"); + $text = substr($matches[0], $level); + + $Block['element']['handler']['argument'][] = $text; + return $Block; + } + } + # TableOfContents protected function blockTableOfContents($line, $block) @@ -322,27 +467,6 @@ class ParsedownExtension extends \ParsedownExtra return $Element; } - protected function paragraph($Line) - { - $paragraph = array( - 'type' => 'Paragraph', - 'element' => array( - 'name' => 'p', - 'handler' => array( - 'function' => 'lineElements', - 'argument' => $Line['text'], - 'destination' => 'elements', - ), - ), - ); - - if(isset($Line['text'][1]) && $Line['text'][0] == '!' && $Line['text'][1] == '[') - { - $paragraph['element']['attributes']['class'] = 'p-image'; - } - return $paragraph; - } - # Inline Math # check https://github.com/BenjaminHoegh/ParsedownMath @@ -451,6 +575,7 @@ class ParsedownExtension extends \ParsedownExtra # advanced attribute data, check parsedown extra plugin: https://github.com/tovic/parsedown-extra-plugin protected function parseAttributeData($text) { + // Allow compact attributes ... $text = str_replace(array('#', '.'), array(' #', ' .'), $text); if (strpos($text, '="') !== false || strpos($text, '=\'') !== false) { diff --git a/system/author/css/style.css b/system/author/css/style.css index a4f02ff..2adabc7 100644 --- a/system/author/css/style.css +++ b/system/author/css/style.css @@ -215,6 +215,52 @@ aside.sidebar{ float: right; } +img, figure,figure img{ + max-width: 100%; +} +figure{ + display: table; + margin: 2em auto; + padding: 0; +} +figure.left{ + float: none; +} +figure.right{ + float: none; +} +figure img{ + display: block; + margin: auto; +} +figcaption{ + display: table-caption; + caption-side: bottom; +} + +.notice1 { + margin: 1em 0; + padding: 10px 1em; + background-color: #fdf0f0; + border-left: 10px solid #d00; +} +.notice2 { + margin: 1em 0; + padding: 10px 1em; + background-color: #fffbf0; + border-left: 10px solid #fb0; +} +.notice3, +.notice4, +.notice5, +.notice6 { + margin: 1em 0; + padding: 10px 1em; + background-color: #f0f8fe; + border-left: 10px solid #08e; +} + + /******************** * NAVI * ********************/ diff --git a/themes/typemill/404.twig b/themes/typemill/404.twig index ae8ef44..e3dd1ab 100644 --- a/themes/typemill/404.twig +++ b/themes/typemill/404.twig @@ -1,4 +1,4 @@ -{% extends 'partials/layout.twig' %} +{% extends '/partials/layout.twig' %} {% block title %}ERROR 404: Page not found{% endblock %} diff --git a/themes/typemill/css/style.css b/themes/typemill/css/style.css index 63a2fa0..5721e1d 100644 --- a/themes/typemill/css/style.css +++ b/themes/typemill/css/style.css @@ -327,27 +327,35 @@ article{ font-size: 1em; line-height: 1.35em; } -article img{ - max-width: 100%; + +img, figure,figure img{ + max-width: 100%; } -article img.left{ +figure{ + display: table; + margin: 2em auto; + padding: 0; +} +figure.left{ width: auto; - max-width: 100%; float: left; - margin: 10px 10px 10px 0; + margin: 0px 30px 30px 0; } -article img.right{ +figure.right{ width: auto; - max-width: 100%; float: right; - margin: 10px 0px 10px 10px; + margin: 0px 0px 30px 30px; } -article img.middle{ - display:block; - width: auto; - max-width: 100%; +figure img{ + display: block; margin: auto; } +figcaption{ + display: table-caption; + caption-side: bottom; +} + + article img.youtube{ position: relative; max-width: 560px; @@ -404,22 +412,29 @@ article .gitlink{ left: 10%; width: 80%; } -/* -.p-image{ - display: table; - margin:auto; - max-width: 100%; +.notice1 { + margin: 1em 0; + padding: 10px 1em; + background-color: #fdf0f0; + border-left: 10px solid #d00; } -.p-image + .left{ - float:left; +.notice2 { + margin: 1em 0; + padding: 10px 1em; + background-color: #fffbf0; + border-left: 10px solid #fb0; } -.p-image img{ - display: block; -} -.p-image em{ - display: table-caption; - caption-side: bottom; +.notice3, +.notice4, +.notice5, +.notice6 { + margin: 1em 0; + padding: 10px 1em; + background-color: #f0f8fe; + border-left: 10px solid #08e; } + + /************************ * PAGING / BREADCRUMB * ************************/