diff --git a/cache/lastCache.txt b/cache/lastCache.txt
index 8fde579..304e95c 100644
--- a/cache/lastCache.txt
+++ b/cache/lastCache.txt
@@ -1 +1 @@
-1550594676
\ No newline at end of file
+1555084348
\ No newline at end of file
diff --git a/content/00-Welcome/03-Markdown-Test.md b/content/00-Welcome/03-Markdown-Test.md
index b313363..c563a09 100644
--- a/content/00-Welcome/03-Markdown-Test.md
+++ b/content/00-Welcome/03-Markdown-Test.md
@@ -6,7 +6,7 @@ Developers love markdown, because it is much cleaner and saver than HTML. And th
If you develop a theme for TYPEMILL, please take care that all elements on this page are designed properly.
-##Table of Contents
+## Table of Contents
To create a table of contents, simply write `[TOC]` in a separate line. It will be replaced with a table of contents like this automatically.
@@ -291,4 +291,5 @@ x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)
````
[^1]: Thank you for scrolling.
-[^2]: This is the end of the page.
\ No newline at end of file
+[^2]: This is the end of the page.
+
diff --git a/content/index.md b/content/index.md
index 072647b..2c8a836 100644
--- a/content/index.md
+++ b/content/index.md
@@ -1,6 +1,4 @@
# Typemill
-**MODERN WEB PUBLISHING FOR WRITERS**
-
*Typemill is a user-friendly and lightweight open source CMS for publishing text-works like prosa, lyrics, manuals, documentations, studies and more. Just download and start.*
diff --git a/media/live/5c4ccd43e5ac8-live.png b/media/live/5c4ccd43e5ac8-live.png
deleted file mode 100644
index 2fc9519..0000000
Binary files a/media/live/5c4ccd43e5ac8-live.png and /dev/null differ
diff --git a/media/live/5c4ccd43e5ac8-mlibrary.png b/media/live/5c4ccd43e5ac8-mlibrary.png
deleted file mode 100644
index 44d48c1..0000000
Binary files a/media/live/5c4ccd43e5ac8-mlibrary.png and /dev/null differ
diff --git a/media/original/5c4ccd43e5ac8-original.png b/media/original/5c4ccd43e5ac8-original.png
deleted file mode 100644
index e57df89..0000000
Binary files a/media/original/5c4ccd43e5ac8-original.png and /dev/null differ
diff --git a/system/Controllers/AuthController.php b/system/Controllers/AuthController.php
index 260c46e..6a9284e 100644
--- a/system/Controllers/AuthController.php
+++ b/system/Controllers/AuthController.php
@@ -63,7 +63,7 @@ class AuthController extends Controller
}
}
- $this->render($response, '/auth/login.twig', $data);
+ return $this->render($response, '/auth/login.twig', $data);
}
/**
diff --git a/system/Controllers/ContentApiController.php b/system/Controllers/ContentApiController.php
index 6f69285..c1b8c17 100644
--- a/system/Controllers/ContentApiController.php
+++ b/system/Controllers/ContentApiController.php
@@ -550,7 +550,60 @@ class ContentApiController extends ContentController
return $response->withJson(array('data' => $content, 'errors' => false));
}
- public function updateBlock(Request $request, Response $response, $args)
+ public function getArticleHtml(Request $request, Response $response, $args)
+ {
+ /* get params from call */
+ $this->params = $request->getParams();
+ $this->uri = $request->getUri();
+
+ # set structure
+ if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
+
+ /* set item */
+ if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
+
+ # set the status for published and drafted
+ $this->setPublishStatus();
+
+ # set path
+ $this->setItemPath($this->item->fileType);
+
+ # read content from file
+ if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
+
+ $content = $this->content;
+
+ if($content == '')
+ {
+ $content = [];
+ }
+
+ # initialize parsedown extension
+ $parsedown = new ParsedownExtension();
+
+ # if content is not an array, then transform it
+ if(!is_array($content))
+ {
+ # turn markdown into an array of markdown-blocks
+ $content = $parsedown->markdownToArrayBlocks($content);
+ }
+
+ # needed for ToC links
+ $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
+
+ foreach($content as $key => $block)
+ {
+ /* parse markdown-file to content-array */
+ $contentArray = $parsedown->text($block);
+
+ /* parse markdown-content-array to content-string */
+ $content[$key] = $parsedown->markup($contentArray, $relurl);
+ }
+
+ return $response->withJson(array('data' => $content, 'errors' => false));
+ }
+
+ public function addBlock(Request $request, Response $response, $args)
{
/* get params from call */
$this->params = $request->getParams();
@@ -605,14 +658,105 @@ class ContentApiController extends ContentController
{
# set the id of the markdown-block (it will be one more than the actual array, so count is perfect)
$id = count($pageMarkdown);
-
- # set the id with prefix "blox-"
- $blockId = 'blox-' . $id;
-
+
# add the new markdown block to the page content
$pageMarkdown[] = $blockMarkdown;
}
- elseif(!isset($pageMarkdown[$this->params['block_id']]))
+ elseif(($this->params['block_id'] == 0) OR !isset($pageMarkdown[$this->params['block_id']]))
+ {
+ # if the block does not exists, return an error
+ return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404);
+ }
+ else
+ {
+ # insert new markdown block
+ array_splice( $pageMarkdown, $this->params['block_id'], 0, $blockMarkdown );
+ $id = $this->params['block_id'];
+ }
+
+ # encode the content into json
+ $pageJson = json_encode($pageMarkdown);
+
+ # set path for the file (or folder)
+ $this->setItemPath('txt');
+
+ /* update the file */
+ if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
+ {
+ # update the internal structure
+ $this->setStructure($draft = true, $cache = false);
+ }
+ else
+ {
+ return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
+ }
+
+ /* set safe mode to escape javascript and html in markdown */
+ $parsedown->setSafeMode(true);
+
+ /* parse markdown-file to content-array */
+ $blockArray = $parsedown->text($blockMarkdown);
+
+ # needed for ToC links
+ $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
+
+ /* parse markdown-content-array to content-string */
+ $blockHTML = $parsedown->markup($blockArray, $relurl);
+
+ return $response->withJson(array('content' => $blockHTML, 'markdown' => $blockMarkdown, 'id' => $id, 'errors' => false));
+ }
+
+ public function updateBlock(Request $request, Response $response, $args)
+ {
+ /* get params from call */
+ $this->params = $request->getParams();
+ $this->uri = $request->getUri();
+
+ /* validate input */
+ if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); }
+
+ # set structure
+ if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
+
+ /* set item */
+ if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
+
+ # set the status for published and drafted
+ $this->setPublishStatus();
+
+ # set path
+ $this->setItemPath($this->item->fileType);
+
+ # read content from file
+ if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
+
+ # make it more clear which content we have
+ $pageMarkdown = $this->content;
+
+ $blockMarkdown = $this->params['markdown'];
+
+ # standardize line breaks
+ $blockMarkdown = str_replace(array("\r\n", "\r"), "\n", $blockMarkdown);
+
+ # remove surrounding line breaks
+ $blockMarkdown = trim($blockMarkdown, "\n");
+
+ if($pageMarkdown == '')
+ {
+ $pageMarkdown = [];
+ }
+
+ # initialize parsedown extension
+ $parsedown = new ParsedownExtension();
+
+ # if content is not an array, then transform it
+ if(!is_array($pageMarkdown))
+ {
+ # turn markdown into an array of markdown-blocks
+ $pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown);
+ }
+
+ if(!isset($pageMarkdown[$this->params['block_id']]))
{
# if the block does not exists, return an error
return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404);
@@ -628,14 +772,12 @@ class ContentApiController extends ContentController
# add the markdown-headline to the page-markdown
$pageMarkdown[0] = $blockMarkdownTitle;
$id = 0;
- $blockId = 0;
}
else
{
# update the markdown block in the page content
$pageMarkdown[$this->params['block_id']] = $blockMarkdown;
$id = $this->params['block_id'];
- $blockId = $this->params['block_id'];
}
# encode the content into json
@@ -674,7 +816,7 @@ class ContentApiController extends ContentController
/* parse markdown-content-array to content-string */
$blockHTML = $parsedown->markup($blockArray, $relurl);
- return $response->withJson(array('content' => $blockHTML, 'markdown' => $blockMarkdown, 'blockId' => $blockId, 'id' => $id, 'errors' => false));
+ return $response->withJson(array('content' => $blockHTML, 'markdown' => $blockMarkdown, 'id' => $id, 'errors' => false));
}
public function moveBlock(Request $request, Response $response, $args)
@@ -877,10 +1019,10 @@ class ContentApiController extends ContentController
$request = $request->withParsedBody($params);
- return $this->updateBlock($request, $response, $args);
+ return $this->addBlock($request, $response, $args);
}
- return $response->withJson(array('errors' => 'could not store image to temporary folder'));
+ return $response->withJson(array('errors' => 'could not store image to media folder'));
}
public function saveVideoImage(Request $request, Response $response, $args)
@@ -945,9 +1087,9 @@ class ContentApiController extends ContentController
$request = $request->withParsedBody($this->params);
- return $this->updateBlock($request, $response, $args);
+ return $this->addBlock($request, $response, $args);
}
-
+
return $response->withJson(array('errors' => 'could not store the preview image'));
}
}
\ No newline at end of file
diff --git a/system/Controllers/ContentController.php b/system/Controllers/ContentController.php
index 0a06495..c4d6cc0 100644
--- a/system/Controllers/ContentController.php
+++ b/system/Controllers/ContentController.php
@@ -62,12 +62,15 @@ abstract class ContentController
{
unset($_SESSION['old']);
}
+
+ $response = $response->withoutHeader('Server');
+ $response = $response->withoutHeader('X-Powered-By');
if($this->c->request->getUri()->getScheme() == 'https')
{
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
}
-
+
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
diff --git a/system/Controllers/Controller.php b/system/Controllers/Controller.php
index 30a77b8..f9bd274 100644
--- a/system/Controllers/Controller.php
+++ b/system/Controllers/Controller.php
@@ -3,6 +3,9 @@
namespace Typemill\Controllers;
/* Use the slim-container */
+use Slim\Http\Request;
+use Slim\Http\Response;
+use Slim\Views\Twig;
use Interop\Container\ContainerInterface;
use Typemill\Events\OnPageReady;
@@ -17,18 +20,21 @@ abstract class Controller
protected function render($response, $route, $data)
{
- $data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
+ // $data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
if(isset($_SESSION['old']))
{
unset($_SESSION['old']);
}
+ $response = $response->withoutHeader('Server');
+ $response = $response->withoutHeader('X-Powered-By');
+
if($this->c->request->getUri()->getScheme() == 'https')
{
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
}
-
+
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
diff --git a/system/Controllers/SettingsController.php b/system/Controllers/SettingsController.php
index 2f86e07..7fc9b4e 100644
--- a/system/Controllers/SettingsController.php
+++ b/system/Controllers/SettingsController.php
@@ -23,7 +23,7 @@ class SettingsController extends Controller
$users = $user->getUsers();
$route = $request->getAttribute('route');
- $this->render($response, 'settings/system.twig', array('settings' => $settings, 'copyright' => $copyright, 'languages' => $languages, 'locale' => $locale, 'users' => $users, 'route' => $route->getName() ));
+ return $this->render($response, 'settings/system.twig', array('settings' => $settings, 'copyright' => $copyright, 'languages' => $languages, 'locale' => $locale, 'users' => $users, 'route' => $route->getName() ));
}
public function saveSettings($request, $response, $args)
@@ -132,7 +132,7 @@ class SettingsController extends Controller
$users = $user->getUsers();
$route = $request->getAttribute('route');
- $this->render($response, 'settings/themes.twig', array('settings' => $userSettings, 'themes' => $themedata, 'users' => $users, 'route' => $route->getName() ));
+ return $this->render($response, 'settings/themes.twig', array('settings' => $userSettings, 'themes' => $themedata, 'users' => $users, 'route' => $route->getName() ));
}
public function showPlugins($request, $response, $args)
@@ -201,7 +201,7 @@ class SettingsController extends Controller
$users = $user->getUsers();
$route = $request->getAttribute('route');
- $this->render($response, 'settings/plugins.twig', array('settings' => $userSettings, 'plugins' => $plugins, 'users' => $users, 'route' => $route->getName() ));
+ return $this->render($response, 'settings/plugins.twig', array('settings' => $userSettings, 'plugins' => $plugins, 'users' => $users, 'route' => $route->getName() ));
}
/*************************************
@@ -420,7 +420,7 @@ class SettingsController extends Controller
$userdata[] = $user->getUser($username);
}
- $this->render($response, 'settings/userlist.twig', array('settings' => $settings, 'users' => $users, 'userdata' => $userdata, 'route' => $route->getName() ));
+ return $this->render($response, 'settings/userlist.twig', array('settings' => $settings, 'users' => $users, 'userdata' => $userdata, 'route' => $route->getName() ));
}
public function newUser($request, $response, $args)
@@ -431,7 +431,7 @@ class SettingsController extends Controller
$route = $request->getAttribute('route');
$settings = $this->c->get('settings');
- $this->render($response, 'settings/usernew.twig', array('settings' => $settings, 'users' => $users, 'userrole' => $userrole, 'route' => $route->getName() ));
+ return $this->render($response, 'settings/usernew.twig', array('settings' => $settings, 'users' => $users, 'userrole' => $userrole, 'route' => $route->getName() ));
}
public function createUser($request, $response, $args)
diff --git a/system/Models/Field.php b/system/Models/Field.php
index b4ddb89..409aaa0 100644
--- a/system/Models/Field.php
+++ b/system/Models/Field.php
@@ -67,6 +67,8 @@ class Field
'size',
'rows',
'cols',
+ 'min',
+ 'max',
'class',
'pattern'
);
diff --git a/system/Routes/Api.php b/system/Routes/Api.php
index b4e090e..a7743c8 100644
--- a/system/Routes/Api.php
+++ b/system/Routes/Api.php
@@ -8,6 +8,7 @@ use Typemill\Middleware\RestrictApiAccess;
$app->get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/markdown', ContentApiController::class . ':getArticleMarkdown')->setName('api.article.markdown')->add(new RestrictApiAccess($container['router']));
+$app->post('/api/v1/article/html', ContentApiController::class . ':getArticleHtml')->setName('api.article.html')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/publish', ContentApiController::class . ':publishArticle')->setName('api.article.publish')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/article/unpublish', ContentApiController::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article', ContentApiController::class . ':createArticle')->setName('api.article.create')->add(new RestrictApiAccess($container['router']));
@@ -16,6 +17,7 @@ $app->delete('/api/v1/article', ContentApiController::class . ':deleteArticle')-
$app->post('/api/v1/article/sort', ContentApiController::class . ':sortArticle')->setName('api.article.sort')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/basefolder', ContentApiController::class . ':createBaseFolder')->setName('api.basefolder.create')->add(new RestrictApiAccess($container['router']));
+$app->post('/api/v1/block', ContentApiController::class . ':addBlock')->setName('api.block.add')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/block', ContentApiController::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/block', ContentApiController::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/moveblock', ContentApiController::class . ':moveBlock')->setName('api.block.move')->add(new RestrictApiAccess($container['router']));
diff --git a/system/author/auth/login.twig b/system/author/auth/login.twig
index 0bb6d57..edfe42e 100644
--- a/system/author/auth/login.twig
+++ b/system/author/auth/login.twig
@@ -17,7 +17,7 @@
-
+
{% if errors.password %}
{{ errors.password | first }}
{% endif %}
diff --git a/system/author/auth/setup.twig b/system/author/auth/setup.twig
index fcbae24..436cd44 100644
--- a/system/author/auth/setup.twig
+++ b/system/author/auth/setup.twig
@@ -24,7 +24,7 @@
-
+
{% if errors.password %}
{{ errors.password | first }}
{% endif %}
diff --git a/system/author/auth/welcome.twig b/system/author/auth/welcome.twig
index 9b2c429..5b1df93 100644
--- a/system/author/auth/welcome.twig
+++ b/system/author/auth/welcome.twig
@@ -11,7 +11,7 @@
Hurra!
Your account has been created and you are logged in now.
Next step: Visit the author panel and setup your new website. You can configure the system, choose themes and add plugins.
-
New: Markdown is cool. But editing tables with markdown is a nightmare! With Typemill it becomes easy again, because we recently added a visual table editor. Enjoy!!
+
New:With this latest version you can add a new content block with the visual editor everywhere on a page, not just at the end of the text. Sounds natural, but was pretty hard to code :)
Get help: If you have any questions, please consult the docs or open a new issue on github.
{% endif %}
diff --git a/themes/typemill/typemill.yaml b/themes/typemill/typemill.yaml
index ffe0eaf..3c0513f 100644
--- a/themes/typemill/typemill.yaml
+++ b/themes/typemill/typemill.yaml
@@ -1,5 +1,5 @@
name: Typemill Theme
-version: 1.1.4
+version: 1.1.5
description: The standard theme for Typemill. Responsive, minimal and without any dependencies. It uses the system fonts Calibri and Helvetica. No JavaScript is used.
author: Sebastian Schürmanns
homepage: https://typemill.net