From 1aa20023d07e5660094b38ab03542bf873727559 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 6 Jul 2018 10:31:20 +0200 Subject: [PATCH] Version 1.2.1 Editor Improvement --- cache/lastCache.txt | 2 +- content/0_typemill/03-features.md | 2 +- content/0_typemill/index.md | 4 +- content/5_info/15_markdown-test.md | 54 +++- content/index.md | 2 +- readme.md | 29 ++- system/Controllers/ContentController.php | 23 +- system/Controllers/PageController.php | 6 +- system/Controllers/SettingsController.php | 10 +- system/Extensions/ParsedownExtension.php | 231 ++---------------- system/Middleware/RedirectIfAuthenticated.php | 2 +- .../Middleware/RedirectIfUnauthenticated.php | 4 +- system/Middleware/RestrictApiAccess.php | 26 ++ system/Models/Folder.php | 4 +- system/Models/Validation.php | 2 +- system/Models/Write.php | 7 +- system/Routes/Api.php | 8 +- system/author/content/content.twig | 31 ++- system/author/js/vue-editor.js | 127 +++------- system/author/settings/system.twig | 4 +- system/system.php | 12 +- themes/typemill/cover.twig | 13 +- themes/typemill/css/style.css | 31 ++- 23 files changed, 254 insertions(+), 380 deletions(-) create mode 100644 system/Middleware/RestrictApiAccess.php diff --git a/cache/lastCache.txt b/cache/lastCache.txt index b881c39..cea0087 100644 --- a/cache/lastCache.txt +++ b/cache/lastCache.txt @@ -1 +1 @@ -1530040430 \ No newline at end of file +1530865190 \ No newline at end of file diff --git a/content/0_typemill/03-features.md b/content/0_typemill/03-features.md index e24b697..169f5ea 100644 --- a/content/0_typemill/03-features.md +++ b/content/0_typemill/03-features.md @@ -1,4 +1,4 @@ -# Features +# Features qwer TYPEMILL has a limited set of features right now. It transforms a bunch of **markdown files** into a **website** and generates a list of contents for **navigation**. diff --git a/content/0_typemill/index.md b/content/0_typemill/index.md index 33a30b0..9b57e2a 100644 --- a/content/0_typemill/index.md +++ b/content/0_typemill/index.md @@ -1,6 +1,6 @@ -# About TYPEMILL +# Typemill -TYPEMILL is a simple flat file CMS to create a website like this. It transforms a bunch of **text files** (Markdown) into a **website** and generates a **navigation**. +TYPEMILL is a simple flat file CMS to create a website like this. It transforms a bunch of **text files** (Markdown) into a **website** and generates a **navigation**. TYPEMILL is under construction. Right now it provides only a very basic editor and a simple admin area for settings, plugins and themes. The author-experience will be improved step by step and output formats for e-books like mobi and epub are planned for the future. diff --git a/content/5_info/15_markdown-test.md b/content/5_info/15_markdown-test.md index 403a9ae..0030278 100644 --- a/content/5_info/15_markdown-test.md +++ b/content/5_info/15_markdown-test.md @@ -51,6 +51,20 @@ A paragraph is a simple text-block separated with a new line above and below. A paragraph is a simple text-block separated with a new line above and below. +## Soft Linebreak + +```` +For a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return. + +She said: "Hello" +He said: "again" +```` + +For a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return. + +She said: "Hello" +He said: "again" + ##Emphasis ```` @@ -137,11 +151,11 @@ Or you can use a shortcut like http://typemill.net. ``` The same rules as with links, but with a ! -![alt-text](/info/markdown.png) +![alt-text](/media/markdown.png) -![alt-text](/info/markdown.png "my title") +![alt-text](/media/markdown.png "my title") -![alt-text](/info/markdown.png "my title"){#myid .myclass} +![alt-text](/media/markdown.png "my title"){#myid .myclass} ``` The same rules as with links, but with a ! @@ -152,6 +166,40 @@ The same rules as with links, but with a ! ![alt-text](/media/markdown.png "my title"){#myid .imgClass .myClass} +## Linked Images + +```` +You can link an image with a nested syntax like this: + +[![alt-text](/media/markdown.png)](https://typemill.net) +```` + +You can link an image with a nested syntax like this: + +[![alt-text](/media/markdown.png)](https://typemill.net) + +## Image Position + +```` +You can controll the image position with the classes .left, .right and .middle like this: + +![alt-text](/media/markdown.png){.left} +![alt-text](/media/markdown.png){.right} +![alt-text](/media/markdown.png){.middle} +```` + +![image float left](/media/markdown.png){.left} + +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". + +![image float right](/media/markdown.png){.right} + +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". + +![image middle](/media/markdown.png){.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 "middle". + ## Blockquote ``` diff --git a/content/index.md b/content/index.md index fc9a5e1..af930f9 100644 --- a/content/index.md +++ b/content/index.md @@ -1,3 +1,3 @@ # Typemill -TYPEMILL is a small flat file cms designed for **writers**. It creates websites based on markdown files and is a perfect solution for text-works like studies, manuals or documentations. TYPEMILL is simple, lightweight and open source. Just download and start. \ No newline at end of file +TYPEMILL is a small flat file cms designed for **writers**. It creates websites based on markdown files and is a perfect solution for text-works like studies, manuals or documentations. TYPEMILL is simple, lightweight and open source. Just download and start. \ No newline at end of file diff --git a/readme.md b/readme.md index 61acc43..8f56777 100644 --- a/readme.md +++ b/readme.md @@ -15,7 +15,7 @@ TYPEMILL is a small flat file cms designed for writers. It creates websites base * Supports configurable themes and plugins. * Provides an author panel to configure the system, the themes and the plugins. * Creates and manages users. -* Online editing is on its way (for time beeing upload markdown files). +* Provides a basic online editing (only for existing files so far, in development). * Markdown supports table of contents (TOC), tables, footnotes, abbreviations and definition lists. * Supports MathJax and KaTeX (plugin). * Supports code highlighting (plugin). @@ -24,7 +24,9 @@ TYPEMILL is a small flat file cms designed for writers. It creates websites base ## Installation -Download TYPEMILL from the [TYPEMILL website](http://typemill.net) or clone this repository with git. Open your git command line (e.g. gitbash), go to your project folder (e.g. htdocs) and type: +Download TYPEMILL from the [TYPEMILL website](http://typemill.net), unzip the files and you are done. + +If you are a developer, you can also clone this repository. To do so, open your git command line (e.g. gitbash), go to your project folder (e.g. htdocs) and type: git clone git://github.com/trendschau/typemill.git @@ -33,7 +35,7 @@ The GitHub-version has no vendor-folder, so you have to update and include all l composer update If you did not use composer before, please go to the [composer website](http://getcomposer.org) and start to learn. -To run TYPEMILL **live**, simply upload the files to your server. +To run TYPEMILL on a **live** system, simply upload the files to your server. ## Setup @@ -41,7 +43,7 @@ Please go to `your-typemill-website.com/setup`, create an initial user and then ## Login -You can find your login screen under `/tm-author/login` or simply go to `/setup` and you will be redirected to the login-page. +You can find your login screen under `/tm/login` or simply go to `/setup` and you will be redirected to the login-page. ## Requirements @@ -53,10 +55,25 @@ You can read the full documentation for writers, for theme developers and for pl ## Contribute -If you want to contribute to TYPEMILL, please fork this GitHub repository first. Then make your changes and create a pull request. I will review all request as soon as possible. +Typemill is still in an early stage and contributions are highly welcome. Here are some ideas for non-coder: + +* Find bugs and errors (open a new issue on github for it). +* Improve the documentation. +* Describe some missing features and explain, why they are important for other users. + +Some ideas for devs (please fork this repository make your changes and create a pull request): + +* Fix a bug. +* Create a nice theme. +* Create a new plugin. +* Improve the CSS-code with BEM and make it modular. +* Rebuild the theme with css-grid. +* Improve accessibility of html and css. +* Help to establish autotests with selenium or cypress. +* Write unit-tests. For hints, questions, problems and support, please open up a new issue on GitHub. ## Licence -TYPEMILL is published under MIT licence. \ No newline at end of file +TYPEMILL is published under MIT licence. Please check the licence of the included libraries, too. \ No newline at end of file diff --git a/system/Controllers/ContentController.php b/system/Controllers/ContentController.php index c2e605b..21f505c 100644 --- a/system/Controllers/ContentController.php +++ b/system/Controllers/ContentController.php @@ -29,7 +29,6 @@ class ContentController extends Controller public function showContent(Request $request, Response $response, $args) { - $settings = $this->c->get('settings'); $pathToContent = $settings['rootPath'] . $settings['contentFolder']; $uri = $request->getUri(); @@ -115,8 +114,6 @@ class ContentController extends Controller return $this->render($response, 'content/content.twig', array('navigation' => $structure, 'title' => $title, 'content' => $content, 'item' => $item, 'settings' => $settings )); } - - public function updateArticle(Request $request, Response $response, $args) { /* Extract the parameters from get-call */ @@ -147,7 +144,7 @@ class ContentController extends Controller $structure = $this->getFreshStructure($pathToContent, $write, $uri); if(!$structure) { - return $response->withJson(['errors' => ['content folder is empty']], 404); + return $response->withJson(['errors' => ['message' => 'content folder is empty']], 404); } } @@ -163,10 +160,10 @@ class ContentController extends Controller /* search for the url in the structure */ $item = Folder::getItemForUrl($structure, $params['url']); } - + if(!$item) { - return $response->withJson(['errors' => ['requested page-url not found']], 404); + return $response->withJson(['errors' => ['message' => 'requested page-url not found']], 404); } if($item->elementType == 'folder') @@ -182,14 +179,20 @@ class ContentController extends Controller $mdFile = $write->getFile($settings['contentFolder'], $path); if($mdFile) { - /* merge title with content for complete markdown document */ + /* merge title with content forcomplete markdown document */ $updatedContent = '# ' . $params['title'] . "\r\n\r\n" . $params['content']; /* update the file */ - $write->writeFile($settings['contentFolder'], $path, $updatedContent); - return $response->withJson(['success'], 200); + if($write->writeFile($settings['contentFolder'], $path, $updatedContent)) + { + return $response->withJson(['success'], 200); + } + else + { + return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if file is writable']], 404); + } } - return $response->withJson(['errors' => ['requested markdown-file not found']], 404); + return $response->withJson(['errors' => ['message' => 'requested markdown-file not found']], 404); } protected function getFreshStructure($pathToContent, $cache, $uri) diff --git a/system/Controllers/PageController.php b/system/Controllers/PageController.php index 0c72175..c684c32 100644 --- a/system/Controllers/PageController.php +++ b/system/Controllers/PageController.php @@ -119,15 +119,17 @@ class PageController extends Controller } $contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData(); - + /* initialize parsedown */ $parsedown = new ParsedownExtension(); + + /* set safe mode to escape javascript and html in markdown */ $parsedown->setSafeMode(true); /* parse markdown-file to content-array */ $contentArray = $parsedown->text($contentMD); $contentArray = $this->c->dispatcher->dispatch('onContentArrayLoaded', new OnContentArrayLoaded($contentArray))->getData(); - + /* get the first image from content array */ $firstImage = $this->getFirstImage($contentArray); diff --git a/system/Controllers/SettingsController.php b/system/Controllers/SettingsController.php index 34d4cf3..2326629 100644 --- a/system/Controllers/SettingsController.php +++ b/system/Controllers/SettingsController.php @@ -40,11 +40,11 @@ class SettingsController extends Controller { /* make sure only allowed fields are stored */ $newSettings = array( - 'title' => $newSettings['title'], - 'author' => $newSettings['author'], - 'copyright' => $newSettings['copyright'], - 'year' => $newSettings['year'], - 'statpage' => isset($newSettings['startpage']) ? true : false + 'title' => $newSettings['title'], + 'author' => $newSettings['author'], + 'copyright' => $newSettings['copyright'], + 'year' => $newSettings['year'], + 'startpage' => isset($newSettings['startpage']) ? true : false ); $copyright = $this->getCopyright(); diff --git a/system/Extensions/ParsedownExtension.php b/system/Extensions/ParsedownExtension.php index c425143..058ef67 100644 --- a/system/Extensions/ParsedownExtension.php +++ b/system/Extensions/ParsedownExtension.php @@ -18,34 +18,25 @@ class ParsedownExtension extends \ParsedownExtra array_unshift($this->BlockTypes['['], 'TableOfContents'); } - function text($text) - { - # make sure no definitions are set - $this->DefinitionData = array(); - - # standardize line breaks - $text = str_replace(array("\r\n", "\r"), "\n", $text); - - # remove surrounding line breaks - $text = trim($text, "\n"); - - # split text into lines - $lines = explode("\n", $text); - - # iterate through lines to identify blocks and return array of content - $blocks = $this->getContentArray($lines); + function text($text) + { + $Elements = $this->textElements($text); - return $blocks; + return $Elements; } - function markup($blocks) - { - # iterate through array of content and get markup - $markup = $this->getMarkup($blocks); - + function markup($Elements) + { + # convert to markup + $markup = $this->elements($Elements); + # trim line breaks $markup = trim($markup, "\n"); + # merge consecutive dl elements + $markup = preg_replace('/<\/dl>\s+
\s+/', '', $markup); + + # create table of contents if(isset($this->DefinitionData['TableOfContents'])) { $TOC = $this->buildTOC($this->headlines); @@ -53,9 +44,6 @@ class ParsedownExtension extends \ParsedownExtra $markup = preg_replace('%(]*>\[TOC\]

)%i', $TOC, $markup); } - # merge consecutive dl elements - $markup = preg_replace('/<\/dl>\s+
\s+/', '', $markup); - # add footnotes if (isset($this->DefinitionData['Footnote'])) { @@ -63,10 +51,10 @@ class ParsedownExtension extends \ParsedownExtra $markup .= "\n" . $this->element($Element); } - - return $markup; - } + return $markup; + } + # TableOfContents protected function blockTableOfContents($line, $block) @@ -157,181 +145,8 @@ class ParsedownExtension extends \ParsedownExtra return $markup; } - - # - # Blocks - # - - protected function getContentArray(array $lines) - { - $CurrentBlock = null; - - foreach ($lines as $line) - { - if (chop($line) === '') - { - if (isset($CurrentBlock)) - { - $CurrentBlock['interrupted'] = true; - } - - continue; - } - - if (strpos($line, "\t") !== false) - { - $parts = explode("\t", $line); - - $line = $parts[0]; - - unset($parts[0]); - - foreach ($parts as $part) - { - $shortage = 4 - mb_strlen($line, 'utf-8') % 4; - - $line .= str_repeat(' ', $shortage); - $line .= $part; - } - } - - $indent = 0; - - while (isset($line[$indent]) and $line[$indent] === ' ') - { - $indent ++; - } - - $text = $indent > 0 ? substr($line, $indent) : $line; - - # ~ - - $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); - - # ~ - - if (isset($CurrentBlock['continuable'])) - { - $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); - - if (isset($Block)) - { - $CurrentBlock = $Block; - - continue; - } - else - { - if ($this->isBlockCompletable($CurrentBlock['type'])) - { - $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); - } - } - } - - # ~ - - $marker = $text[0]; - - # ~ - - $blockTypes = $this->unmarkedBlockTypes; - - if (isset($this->BlockTypes[$marker])) - { - foreach ($this->BlockTypes[$marker] as $blockType) - { - $blockTypes []= $blockType; - } - } - - # - # ~ - - foreach ($blockTypes as $blockType) - { - $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); - - if (isset($Block)) - { - $Block['type'] = $blockType; - - if ( ! isset($Block['identified'])) - { - $Blocks []= $CurrentBlock; - - $Block['identified'] = true; - } - - if ($this->isBlockContinuable($blockType)) - { - $Block['continuable'] = true; - } - - $CurrentBlock = $Block; - - continue 2; - } - } - - # ~ - - if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) - { - $CurrentBlock['element']['text'] .= "\n".$text; - } - else - { - $Blocks []= $CurrentBlock; - - $CurrentBlock = $this->paragraph($Line); - - $CurrentBlock['identified'] = true; - } - } - - # ~ - - if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) - { - $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); - } - - # ~ - - $Blocks []= $CurrentBlock; - - unset($Blocks[0]); - - # ~ - return $Blocks; - } - - public function getMarkup($Blocks) - { - $markup = ''; - - foreach ($Blocks as $Block) - { - if (isset($Block['hidden'])) - { - continue; - } - - $markup .= "\n"; - $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); - } - - $markup .= "\n"; - - # ~ - - return $markup; - } - # math support. Check https://github.com/aidantwoods/parsedown/blob/mathjaxlatex/ParsedownExtensionMathJaxLaTeX.php - protected function inlineCode($Excerpt) { $marker = $Excerpt['text'][0]; @@ -401,18 +216,7 @@ class ParsedownExtension extends \ParsedownExtra return $Block; } } - /* - protected function blockFencedCodeComplete($Block) - { - $text = $Block['element']['element']['text']; - unset($Block['element']['element']['text']); - $Block['element']['element']['rawHtml'] = "

$text

"; - $Block['element']['element']['allowRawHtmlInSafeMode'] = true; - - return $Block; - } - */ # # Fenced MathJax protected function blockFencedMathJaxLaTeX($Line) @@ -436,7 +240,8 @@ class ParsedownExtension extends \ParsedownExtra protected function blockFencedMathJaxLaTeXContinue($Line, $Block) { - if (isset($Block['complete'])) + + if (isset($Block['complete'])) { return; } diff --git a/system/Middleware/RedirectIfAuthenticated.php b/system/Middleware/RedirectIfAuthenticated.php index 4fb5aa8..e32a9a3 100644 --- a/system/Middleware/RedirectIfAuthenticated.php +++ b/system/Middleware/RedirectIfAuthenticated.php @@ -19,7 +19,7 @@ class RedirectIfAuthenticated { if(isset($_SESSION['login'])) { - $response = $response->withRedirect($this->router->pathFor('settings.show')); + $response = $response->withRedirect($this->router->pathFor('content.show')); } return $next($request, $response); diff --git a/system/Middleware/RedirectIfUnauthenticated.php b/system/Middleware/RedirectIfUnauthenticated.php index 23f2c33..16fc97f 100644 --- a/system/Middleware/RedirectIfUnauthenticated.php +++ b/system/Middleware/RedirectIfUnauthenticated.php @@ -16,10 +16,10 @@ class RedirectIfUnauthenticated } public function __invoke(Request $request, Response $response, $next) - { + { if(!isset($_SESSION['login'])) { - $response = $response->withRedirect($this->router->pathFor('auth.show')); + return $response->withRedirect($this->router->pathFor('auth.show')); } return $next($request, $response); diff --git a/system/Middleware/RestrictApiAccess.php b/system/Middleware/RestrictApiAccess.php new file mode 100644 index 0000000..bc2e414 --- /dev/null +++ b/system/Middleware/RestrictApiAccess.php @@ -0,0 +1,26 @@ +router = $router; + } + + public function __invoke(Request $request, Response $response, $next) + { + if(!isset($_SESSION['login']) || !isset($_SESSION['role'])) + { + return $response->withJson(['errors' => ['access denied']], 403); + } + return $next($request, $response); + } +} \ No newline at end of file diff --git a/system/Models/Folder.php b/system/Models/Folder.php index fe70e18..df826d2 100644 --- a/system/Models/Folder.php +++ b/system/Models/Folder.php @@ -67,7 +67,7 @@ class Folder $item->urlRel = $fullSlugWithFolder . '/' . $item->slug; $item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug; $item->key = $iteration; - $item->keyPath = $keyPath ? $keyPath . '.' . $iteration : $iteration; + $item->keyPath = isset($keyPath) ? $keyPath . '.' . $iteration : $iteration; $item->keyPathArray = explode('.', $item->keyPath); $item->chapter = $chapter ? $chapter . '.' . $chapternr : $chapternr; @@ -150,7 +150,7 @@ class Folder if($item->elementType == 'folder') { /* get the first element in the folder */ - $item->nextItem = isset($item->folderContent[0]) ? $item->folderContent[0] : false; + $item->nextItem = isset($item->folderContent[0]) ? clone($item->folderContent[0]) : false; } if(!$item->nextItem) diff --git a/system/Models/Validation.php b/system/Models/Validation.php index 96b9449..3c26ef8 100644 --- a/system/Models/Validation.php +++ b/system/Models/Validation.php @@ -201,7 +201,7 @@ class Validation $v = new Validator($params); $v->rule('required', ['title', 'content', 'url']); - $v->rule('lengthBetween', 'title', 2, 40); + $v->rule('lengthBetween', 'title', 2, 100); $v->rule('noHTML', 'title'); $v->rule('markdownSecure', 'content'); diff --git a/system/Models/Write.php b/system/Models/Write.php index e9f65ca..3e65427 100644 --- a/system/Models/Write.php +++ b/system/Models/Write.php @@ -55,7 +55,12 @@ class Write if($this->checkPath($folder)) { $filePath = $this->basePath . $folder . DIRECTORY_SEPARATOR . $file; - $openFile = fopen($filePath, "w"); + $openFile = @fopen($filePath, "w"); + + if(!$openFile) + { + return false; + } fwrite($openFile, $data); fclose($openFile); diff --git a/system/Routes/Api.php b/system/Routes/Api.php index 0912789..598a287 100644 --- a/system/Routes/Api.php +++ b/system/Routes/Api.php @@ -2,9 +2,7 @@ use Typemill\Controllers\SettingsController; use Typemill\Controllers\ContentController; -use Typemill\Middleware\RedirectIfUnauthenticated; -use Typemill\Middleware\RedirectIfAuthenticated; -use Typemill\Middleware\RedirectIfNoAdmin; +use Typemill\Middleware\RestrictApiAccess; -$app->get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes'); -$app->put('/api/v1/article', ContentController::class . ':updateArticle')->setName('api.article.update')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); \ No newline at end of file +$app->get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes')->add(new RestrictApiAccess($container['router'])); +$app->put('/api/v1/article', ContentController::class . ':updateArticle')->setName('api.article.update')->add(new RestrictApiAccess($container['router'])); \ No newline at end of file diff --git a/system/author/content/content.twig b/system/author/content/content.twig index e6bae84..8b5b903 100644 --- a/system/author/content/content.twig +++ b/system/author/content/content.twig @@ -6,28 +6,33 @@
-
-
-
-
+
+ +
+
- + + ${ errors.title }
- -
+
- + + ${ errors.content }
+
- -
+ +
${ errors.message }
-
-
- +
+ +
+ + {{ csrf_field() | raw }} + diff --git a/system/author/js/vue-editor.js b/system/author/js/vue-editor.js index 24467be..2b822d3 100644 --- a/system/author/js/vue-editor.js +++ b/system/author/js/vue-editor.js @@ -1,5 +1,3 @@ -const root = document.getElementById("main").dataset.url; - Vue.component('resizable-textarea', { methods: { resizeTextarea (event) { @@ -22,115 +20,58 @@ Vue.component('resizable-textarea', { }, }); -new Vue({ +let app = new Vue({ + delimiters: ['${', '}'], el: '#editor', data: { - markdown: document.getElementById("origContent").value + form: { + title: document.getElementById("origTitle").value, + content: document.getElementById("origContent").value, + url: document.getElementById("path").value, + csrf_name: document.getElementById("csrf_name").value, + csrf_value: document.getElementById("csrf_value").value, + }, + root: document.getElementById("main").dataset.url, + errors:{ + title: false, + content: false, + message: false, + }, + bdisabled: false, + bresult: false, }, methods: { saveMarkdown: function(e){ - e.preventDefault(); + + var self = this; + self.errors = {title: false, content: false, message: false}, + self.bresult = ''; + self.bdisabled = "disabled"; + + var url = this.root + '/api/v1/article'; + var method = 'PUT'; - e.target.disabled = true; - e.target.classList.remove("success", "fail"); - - deleteErrors(); - - var getPost = 'PUT', - url = root + '/api/v1/article', - contentData = {'url': document.getElementById("url").value, 'title': document.getElementById("title").value, 'content': document.getElementById("content").value }; - sendJson(function(response, httpStatus) { if(response) { - e.target.disabled = false; + self.bdisabled = false; + var result = JSON.parse(response); if(result.errors) { - e.target.classList.add('fail'); - processErrors(result.errors, httpStatus); + self.bresult = 'fail'; + if(result.errors.title){ self.errors.title = result.errors.title[0] }; + if(result.errors.content){ self.errors.content = result.errors.content[0] }; + if(result.errors.message){ self.errors.message = result.errors.message }; } else { - e.target.classList.add('success'); + self.bresult = 'success'; } } - else - { - e.target.disabled = false; - e.target.classList.add('fail'); - console.info('no response'); - } - }, getPost, url, contentData ); + }, method, url, this.form ); } } -}) - -function processErrors(errors, httpStatus) -{ - if(errors.length == 0) return; - - var message = ''; - - if(httpStatus == "404") - { - message = errors[0]; - } - - if(httpStatus == "422") - { - var fields = ''; - - for (var key in errors) - { - fields = fields + ' "' + key + '"'; - - if(key == 'url' || !errors.hasOwnProperty(key)) continue; - - - var errorMessages = errors[key], - fieldElement = document.getElementById(key), - fieldMessage = document.createElement("span"), - fieldWrapper = fieldElement.parentElement; - - fieldWrapper.classList.add("error"); - - fieldMessage.className = "error"; - fieldMessage.innerHTML = errorMessages[0]; - fieldWrapper.classList.add("error"); - fieldWrapper.appendChild(fieldMessage); - } - - message = 'Please correct the errors in these Fields: ' + fields.toUpperCase() + '. '; - } - - var messageWrapper = document.getElementById("message"), - messageSpan = document.createElement("span"); - - messageSpan.className = "error"; - messageSpan.innerHTML = message; - messageWrapper.appendChild(messageSpan); -} - -function deleteErrors() -{ - var errors = document.querySelectorAll('.error'); - - if(errors.length == 0) return; - - for(var key in errors) - { - if(!errors.hasOwnProperty(key)) continue; - - if(errors[key].tagName == "SPAN") - { - errors[key].parentElement.removeChild(errors[key]); - } - else - { - errors[key].classList.remove("error"); - } - } -} \ No newline at end of file +}) \ No newline at end of file diff --git a/system/author/settings/system.twig b/system/author/settings/system.twig index 90e14bf..7816824 100644 --- a/system/author/settings/system.twig +++ b/system/author/settings/system.twig @@ -1,6 +1,7 @@ {% extends 'layouts/layout.twig' %} {% block title %}Setup{% endblock %} {% set startpage = old.settings.startpage ? old.settings.startpage : settings.startpage %} +{% set linebreaks = old.settings.linebreaks ? old.settings.linebreaks : settings.linebreaks %} {% set year = settings.year ? settings.year : "now"|date("Y") %} {% block content %} @@ -66,9 +67,8 @@ - + - diff --git a/system/system.php b/system/system.php index 529f1b9..3c11211 100644 --- a/system/system.php +++ b/system/system.php @@ -103,7 +103,7 @@ $container['assets'] = function($c) * DECIDE FOR SESSION * ************************/ -$session_segments = array('setup', 'tm/', '/setup', '/tm/'); +$session_segments = array('setup', 'tm/', 'api/', '/setup', '/tm/', '/api/'); $path = $container['request']->getUri()->getPath(); $container['flash'] = false; $container['csrf'] = false; @@ -112,7 +112,7 @@ foreach($session_segments as $segment) { if(substr( $path, 0, strlen($segment) ) === $segment) { - /* start a session */ + // configure session ini_set( 'session.cookie_httponly', 1 ); ini_set('session.use_strict_mode', 1); if($container['request']->getUri()->getScheme() == 'https') @@ -124,9 +124,8 @@ foreach($session_segments as $segment) { session_name('typemill-session'); } - session_start(); - /* add csrf-protection */ + // add csrf-protection $container['csrf'] = function ($c) { $guard = new \Slim\Csrf\Guard(); @@ -135,11 +134,14 @@ foreach($session_segments as $segment) return $guard; }; - /* add flash to container */ + // add flash to container $container['flash'] = function () { return new \Slim\Flash\Messages(); }; + + // start session + session_start(); } } diff --git a/themes/typemill/cover.twig b/themes/typemill/cover.twig index 19aee8f..9bd97ee 100644 --- a/themes/typemill/cover.twig +++ b/themes/typemill/cover.twig @@ -7,13 +7,14 @@
{{ content }} - - {{ settings.themes.typemill.start ? settings.themes.typemill.start : 'Start'}} - - {% if settings.setup %} - Setup - {% endif %} +
{% endblock %} \ No newline at end of file diff --git a/themes/typemill/css/style.css b/themes/typemill/css/style.css index 07e89bf..4b11db1 100644 --- a/themes/typemill/css/style.css +++ b/themes/typemill/css/style.css @@ -21,9 +21,11 @@ aside{ background: #f9f8f6; border-left: 30px solid #FFF; border-right: 30px sol .main-menu li a:focus, .main-menu li a:hover, .main-menu li a:active, .main-menu li.active.file a{ color: #e0474c; } article {background: #FFF; } article a, article a:link, article a:visited, -footer a, footer a:link, footer a:visited{ text-decoration: none; color: #e0474c; } +footer a, footer a:link, footer a:visited +.lead a, .lead a:link, .lead a:visited{ text-decoration: none; color: #e0474c; } article a:focus, article a:hover, article a:active, -footer a:focus, footer a:hover, footer a:active{ text-decoration: underline } +footer a:focus, footer a:hover, footer a:active +.lead a:focus, .lead a:hover, .lead a:active{ text-decoration: underline } article .breadcrumb,article .paging a{ background: #f9f8f6; } article .breadcrumb span a{ background: #e0474c; color: #f9f8f6; border: 1px solid #e0474c; } article .breadcrumb a:focus,article .breadcrumb a:hover,article .breadcrumb a:active { background: #f9f8f6; color: #e0474c; } @@ -36,9 +38,9 @@ header a, .cover{ color: #444; } footer{ background: #FFF; } .chapterNumber{ color: #bbb; } .chapter h1{ border-bottom: 2px solid #f9f8f6; } -.cover .lead a, .cover .lead a:link, .cover .lead a:visited, +.cover .actionLink a, .cover .actionLink a:link, .cover .actionLink a:visited, a.readMore, a.readMore:link, a.readMore:visited{ border: 2px solid #e0474c; background: #e0474c; color: #f9f8f6; } -.cover .lead a:focus, .cover .lead a:hover, .cover .lead a:active, +.cover .lead a:focus, .cover .actionLink a:hover, .cover .actionLink a:active, a.readMore:focus, a.readMore:hover, a.readMore:active{ border: 2px solid #e0474c; color: #444; @@ -204,7 +206,7 @@ header p{ font-size: 2.5em; font-weight: 700; } -.cover .lead a, a.readMore{ +.cover .actionLink a, a.readMore{ display: inline-block; min-width: 100px; padding: 5px 10px; @@ -319,6 +321,25 @@ article{ article img{ width: 100%; } +article img.left{ + width: auto; + max-width: 100%; + float: left; + margin: 10px 10px 10px 0; +} +article img.right{ + width: auto; + max-width: 100%; + float: right; + margin: 10px 0px 10px 10px; +} +article img.middle{ + display:block; + width: auto; + max-width: 100%; + margin: auto; +} + /************************ * PAGING / BREADCRUMB *