1
0
mirror of https://github.com/typemill/typemill.git synced 2025-04-30 08:38:21 +02:00

Version 1.2.7: Reorder Content

This commit is contained in:
Sebastian 2018-11-09 10:04:57 +01:00
parent cf737368c0
commit 2633ab69be
29 changed files with 389 additions and 85 deletions

2
.gitignore vendored
View File

@ -4,8 +4,8 @@ plugins/demo
plugins/disqus
plugins/download
plugins/finalwords
plugins/version
plugins/textadds
plugins/version
settings/settings.yaml
settings/users
system/vendor

2
cache/lastCache.txt vendored
View File

@ -1 +1 @@
1540919595
1541750318

View File

@ -0,0 +1,23 @@
# Setup the System
Typemil is a flat file cms that runs out of the box without a complicated installation process. You can create a user account with the [simple setup page](/setup) and then login to the author panel. In the author panel, you can configure your page, use plugins, choose a theme and edit your content.
## If it does not work
If you face any problems, then please make sure, that your system meets these requirements:
- PHP version 7+.
- Apache Server.
- The module `mod_rewrite` and `htaccess`.
If you run a linux-systems like Debian or Ubuntu, then please double check that `mod_rewrite` or `htaccess` are activated. Check this issue on GitHub for help.
Please make the following folders writable with permission 774 (you can use your ftp-software for it):
- Cache
- Content
- Media
- Settings
If you still get an error, then you can post an issue on [GitHub](https://github.com/trendschau/typemill).

View File

@ -0,0 +1 @@
["# demo page","Typemil is a flat file cms that runs out of the box without a complicated installation process. You can create a user account with the [simple setup page](\/setup) and then login to the author panel. In the author panel, you can configure your page, use plugins, choose a theme and edit your content.","## Headline","* This is\n* A list"]

View File

@ -0,0 +1 @@
["# Welcome to Typemill","Great that you give Typemill a try!! Typemill is a small open source cms and a project in work. You will probably miss some important features, but I am working hard to add everything that is needed for a really productive little writing-system.","Before you start, please read the short introduction about \"writing content\". Or simply play around, I hope that Typemill is already quite intuitive to use..."]

View File

@ -602,26 +602,32 @@ class ContentApiController extends ContentController
# if it is a new content-block
if($this->params['block_id'] == 99999)
{
# update the markdown block in the page content
$pageMarkdown[] = $blockMarkdown;
$id = (count($pageMarkdown)-1);
# 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']]))
{
# return error
# 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);
}
elseif($this->params['block_id'] == 0)
{
# update the markdown block in the page content
# if it is the title, then delete the "# " if it exists
$blockMarkdown = trim($blockMarkdown, "# ");
# store the markdown-headline in a separate variable
$blockMarkdownTitle = '# ' . $blockMarkdown;
$pageMarkdown[$this->params['block_id']] = $blockMarkdownTitle;
$id = $this->params['block_id'];
$blockId = $this->params['block_id'];
# add the markdown-headline to the page-markdown
$pageMarkdown[0] = $blockMarkdownTitle;
$id = 0;
$blockId = 0;
}
else
{
@ -635,7 +641,7 @@ class ContentApiController extends ContentController
$pageJson = json_encode($pageMarkdown);
# set path for the file (or folder)
$this->setItemPath('txt');
$this->setItemPath('txt');
/* update the file */
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
@ -666,6 +672,83 @@ class ContentApiController extends ContentController
return $response->withJson(array('content' => $blockHTML, 'markdown' => $blockMarkdown, 'blockId' => $blockId, 'id' => $id, 'errors' => false));
}
public function moveBlock(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;
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);
}
$oldIndex = ($this->params['old_index'] + 1);
$newIndex = ($this->params['new_index'] + 1);
if(!isset($pageMarkdown[$oldIndex]))
{
# 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);
}
$extract = array_splice($pageMarkdown, $oldIndex, 1);
array_splice($pageMarkdown, $newIndex, 0, $extract);
# 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);
}
# if it is the title, then delete the "# " if it exists
$pageMarkdown[0] = trim($pageMarkdown[0], "# ");
return $response->withJson(array('markdown' => $pageMarkdown, 'errors' => false));
}
public function deleteBlock(Request $request, Response $response, $args)
{
@ -713,10 +796,12 @@ class ContentApiController extends ContentController
unset($this->content[$this->params['block_id']]);
$this->content = array_values($this->content);
$pageMarkdown = $this->content;
# delete markdown from title
if(isset($this->content[0]))
if(isset($pageMarkdown[0]))
{
$this->content[0] = trim($this->content[0], "# ");
$pageMarkdown[0] = trim($pageMarkdown[0], "# ");
}
# encode the content into json
@ -735,6 +820,7 @@ class ContentApiController extends ContentController
{
return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
}
return $response->withJson(array('markdown' => $this->content, 'errors' => false));
return $response->withJson(array('markdown' => $pageMarkdown, 'errors' => false));
}
}

View File

@ -111,7 +111,6 @@ class ContentBackendController extends ContentController
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
$content = $this->content;
$title = false;
if($content == '')
{
@ -136,7 +135,11 @@ class ContentBackendController extends ContentController
/* parse markdown-content-array to content-string */
$content[$key] = $parsedown->markup($contentArray);
}
# extract title and delete from content array, array will start at 1 after that.
$title = $content[0];
unset($content[0]);
return $this->render($response, 'editor/editor-blox.twig', array('navigation' => $this->structure, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
}

View File

@ -132,7 +132,7 @@ class PageController extends Controller
/* get the first image from content array */
$firstImage = $this->getFirstImage($contentArray);
/* parse markdown-content-array to content-string */
$contentHTML = $parsedown->markup($contentArray);
$contentHTML = $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData();
@ -216,11 +216,11 @@ class PageController extends Controller
foreach($contentBlocks as $block)
{
/* is it a paragraph? */
if(isset($block['element']['name']) && $block['element']['name'] == 'p')
if(isset($block['name']) && $block['name'] == 'p')
{
if(isset($block['element']['handler']['argument']) && substr($block['element']['handler']['argument'], 0, 2) == '![' )
if(isset($block['handler']['argument']) && substr($block['handler']['argument'], 0, 2) == '![' )
{
return $block['element']['handler']['argument'];
return $block['handler']['argument'];
}
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace Typemill\Extensions;
use Typemill\Extensions\ParsedownExtension;
class TwigMarkdownExtension extends \Twig_Extension
{
public function getFunctions()
{
return [
new \Twig_SimpleFunction('markdown', array($this, 'renderMarkdown' ))
];
}
public function renderMarkdown($markdown)
{
$parsedown = new ParsedownExtension();
$markdownArray = $parsedown->text($markdown);
return $parsedown->markup($markdownArray);
}
}

View File

@ -17,4 +17,5 @@ $app->post('/api/v1/article/sort', ContentApiController::class . ':sortArticle')
$app->post('/api/v1/basefolder', ContentApiController::class . ':createBaseFolder')->setName('api.basefolder.create')->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->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']));

View File

@ -6,12 +6,6 @@
"units_per_em": 1000,
"ascent": 850,
"glyphs": [
{
"uid": "c5845105a87df2ee1999826d90622f6a",
"css": "paragraph",
"code": 61917,
"src": "fontawesome"
},
{
"uid": "f9cbf7508cd04145ade2800169959eef",
"css": "font",
@ -60,6 +54,12 @@
"code": 61618,
"src": "fontawesome"
},
{
"uid": "5408be43f7c42bccee419c6be53fdef5",
"css": "doc-text",
"code": 61686,
"src": "fontawesome"
},
{
"uid": "b091a8bd0fdade174951f17d936f51e4",
"css": "folder-empty",
@ -67,9 +67,21 @@
"src": "fontawesome"
},
{
"uid": "5408be43f7c42bccee419c6be53fdef5",
"css": "doc-text",
"code": 61686,
"uid": "d3b3f17bc3eb7cd809a07bbd4d178bee",
"css": "resize-vertical",
"code": 59398,
"src": "fontawesome"
},
{
"uid": "6605ee6441bf499ffa3c63d3c7409471",
"css": "move",
"code": 61511,
"src": "fontawesome"
},
{
"uid": "5211af474d3a9848f67f945e2ccaf143",
"css": "cancel",
"code": 59399,
"src": "fontawesome"
}
]

View File

@ -5,8 +5,10 @@
.icon-off:before { content: '\e803'; } /* '' */
.icon-home:before { content: '\e804'; } /* '' */
.icon-plus:before { content: '\e805'; } /* '' */
.icon-resize-vertical:before { content: '\e806'; } /* '' */
.icon-cancel:before { content: '\e807'; } /* '' */
.icon-move:before { content: '\f047'; } /* '' */
.icon-link-ext:before { content: '\f08e'; } /* '' */
.icon-resize-full-alt:before { content: '\f0b2'; } /* '' */
.icon-doc-text:before { content: '\f0f6'; } /* '' */
.icon-folder-empty:before { content: '\f114'; } /* '' */
.icon-paragraph:before { content: '\f1dd'; } /* '' */
.icon-folder-empty:before { content: '\f114'; } /* '' */

File diff suppressed because one or more lines are too long

View File

@ -5,8 +5,10 @@
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
.icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe807;&nbsp;'); }
.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf047;&nbsp;'); }
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf08e;&nbsp;'); }
.icon-resize-full-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0b2;&nbsp;'); }
.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0f6;&nbsp;'); }
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf114;&nbsp;'); }
.icon-paragraph { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf1dd;&nbsp;'); }
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf114;&nbsp;'); }

View File

@ -16,8 +16,10 @@
.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
.icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe807;&nbsp;'); }
.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf047;&nbsp;'); }
.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf08e;&nbsp;'); }
.icon-resize-full-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0b2;&nbsp;'); }
.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0f6;&nbsp;'); }
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf114;&nbsp;'); }
.icon-paragraph { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf1dd;&nbsp;'); }
.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf114;&nbsp;'); }

View File

@ -1,11 +1,11 @@
@font-face {
font-family: 'fontello';
src: url('../font/fontello.eot?94639617');
src: url('../font/fontello.eot?94639617#iefix') format('embedded-opentype'),
url('../font/fontello.woff2?94639617') format('woff2'),
url('../font/fontello.woff?94639617') format('woff'),
url('../font/fontello.ttf?94639617') format('truetype'),
url('../font/fontello.svg?94639617#fontello') format('svg');
src: url('../font/fontello.eot?89525311');
src: url('../font/fontello.eot?89525311#iefix') format('embedded-opentype'),
url('../font/fontello.woff2?89525311') format('woff2'),
url('../font/fontello.woff?89525311') format('woff'),
url('../font/fontello.ttf?89525311') format('truetype'),
url('../font/fontello.svg?89525311#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@ -15,7 +15,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
src: url('../font/fontello.svg?94639617#fontello') format('svg');
src: url('../font/fontello.svg?89525311#fontello') format('svg');
}
}
*/
@ -61,8 +61,10 @@
.icon-off:before { content: '\e803'; } /* '' */
.icon-home:before { content: '\e804'; } /* '' */
.icon-plus:before { content: '\e805'; } /* '' */
.icon-resize-vertical:before { content: '\e806'; } /* '' */
.icon-cancel:before { content: '\e807'; } /* '' */
.icon-move:before { content: '\f047'; } /* '' */
.icon-link-ext:before { content: '\f08e'; } /* '' */
.icon-resize-full-alt:before { content: '\f0b2'; } /* '' */
.icon-doc-text:before { content: '\f0f6'; } /* '' */
.icon-folder-empty:before { content: '\f114'; } /* '' */
.icon-paragraph:before { content: '\f1dd'; } /* '' */
.icon-folder-empty:before { content: '\f114'; } /* '' */

View File

@ -229,11 +229,11 @@ body {
}
@font-face {
font-family: 'fontello';
src: url('./font/fontello.eot?32756715');
src: url('./font/fontello.eot?32756715#iefix') format('embedded-opentype'),
url('./font/fontello.woff?32756715') format('woff'),
url('./font/fontello.ttf?32756715') format('truetype'),
url('./font/fontello.svg?32756715#fontello') format('svg');
src: url('./font/fontello.eot?63828600');
src: url('./font/fontello.eot?63828600#iefix') format('embedded-opentype'),
url('./font/fontello.woff?63828600') format('woff'),
url('./font/fontello.ttf?63828600') format('truetype'),
url('./font/fontello.svg?63828600#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@ -306,13 +306,17 @@ body {
<div class="row">
<div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-home">&#xe804;</i> <span class="i-name">icon-home</span><span class="i-code">0xe804</span></div>
<div class="the-icons span3" title="Code: 0xe805"><i class="demo-icon icon-plus">&#xe805;</i> <span class="i-name">icon-plus</span><span class="i-code">0xe805</span></div>
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext">&#xf08e;</i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
<div class="the-icons span3" title="Code: 0xf0b2"><i class="demo-icon icon-resize-full-alt">&#xf0b2;</i> <span class="i-name">icon-resize-full-alt</span><span class="i-code">0xf0b2</span></div>
<div class="the-icons span3" title="Code: 0xe806"><i class="demo-icon icon-resize-vertical">&#xe806;</i> <span class="i-name">icon-resize-vertical</span><span class="i-code">0xe806</span></div>
<div class="the-icons span3" title="Code: 0xe807"><i class="demo-icon icon-cancel">&#xe807;</i> <span class="i-name">icon-cancel</span><span class="i-code">0xe807</span></div>
</div>
<div class="row">
<div class="the-icons span3" title="Code: 0xf047"><i class="demo-icon icon-move">&#xf047;</i> <span class="i-name">icon-move</span><span class="i-code">0xf047</span></div>
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext">&#xf08e;</i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
<div class="the-icons span3" title="Code: 0xf0b2"><i class="demo-icon icon-resize-full-alt">&#xf0b2;</i> <span class="i-name">icon-resize-full-alt</span><span class="i-code">0xf0b2</span></div>
<div class="the-icons span3" title="Code: 0xf0f6"><i class="demo-icon icon-doc-text">&#xf0f6;</i> <span class="i-name">icon-doc-text</span><span class="i-code">0xf0f6</span></div>
</div>
<div class="row">
<div class="the-icons span3" title="Code: 0xf114"><i class="demo-icon icon-folder-empty">&#xf114;</i> <span class="i-name">icon-folder-empty</span><span class="i-code">0xf114</span></div>
<div class="the-icons span3" title="Code: 0xf1dd"><i class="demo-icon icon-paragraph">&#xf1dd;</i> <span class="i-name">icon-paragraph</span><span class="i-code">0xf1dd</span></div>
</div>
</div>
<div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>

View File

@ -18,6 +18,12 @@
<glyph glyph-name="plus" unicode="&#xe805;" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
<glyph glyph-name="resize-vertical" unicode="&#xe806;" d="M393 671q0-14-11-25t-25-10h-71v-572h71q15 0 25-10t11-25-11-25l-143-143q-10-11-25-11t-25 11l-143 143q-10 10-10 25t10 25 25 10h72v572h-72q-14 0-25 10t-10 25 10 26l143 142q11 11 25 11t25-11l143-142q11-11 11-26z" horiz-adv-x="428.6" />
<glyph glyph-name="cancel" unicode="&#xe807;" d="M724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
<glyph glyph-name="move" unicode="&#xf047;" d="M1000 350q0-14-11-25l-142-143q-11-11-26-11t-25 11-10 25v72h-215v-215h72q14 0 25-10t11-25-11-25l-143-143q-10-11-25-11t-25 11l-143 143q-11 10-11 25t11 25 25 10h72v215h-215v-72q0-14-10-25t-25-11-25 11l-143 143q-11 11-11 25t11 25l143 143q10 11 25 11t25-11 10-25v-72h215v215h-72q-14 0-25 10t-11 25 11 26l143 142q11 11 25 11t25-11l143-142q11-11 11-26t-11-25-25-10h-72v-215h215v72q0 14 10 25t25 11 26-11l142-143q11-10 11-25z" horiz-adv-x="1000" />
<glyph glyph-name="link-ext" unicode="&#xf08e;" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
<glyph glyph-name="resize-full-alt" unicode="&#xf0b2;" d="M716 548l-198-198 198-198 80 80q17 18 39 8 22-9 22-33v-250q0-14-10-25t-26-11h-250q-23 0-32 23-10 21 7 38l81 81-198 198-198-198 80-81q17-17 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l80-80 198 198-198 198-80-80q-11-11-25-11-7 0-14 3-22 9-22 33v250q0 14 11 25t25 11h250q23 0 33-23 9-21-8-38l-80-81 198-198 198 198-81 81q-17 17-7 38 9 23 32 23h250q15 0 26-11t10-25v-250q0-24-22-33-7-3-14-3-14 0-25 11z" horiz-adv-x="857.1" />
@ -25,8 +31,6 @@
<glyph glyph-name="doc-text" unicode="&#xf0f6;" d="M819 638q16-16 27-42t11-50v-642q0-23-15-38t-38-16h-750q-23 0-38 16t-16 38v892q0 23 16 38t38 16h500q22 0 49-11t42-27z m-248 136v-210h210q-5 17-12 23l-175 175q-6 7-23 12z m215-853v572h-232q-23 0-38 16t-16 37v233h-429v-858h715z m-572 483q0 7 5 12t13 5h393q8 0 13-5t5-12v-36q0-8-5-13t-13-5h-393q-8 0-13 5t-5 13v36z m411-125q8 0 13-5t5-13v-36q0-8-5-13t-13-5h-393q-8 0-13 5t-5 13v36q0 8 5 13t13 5h393z m0-143q8 0 13-5t5-13v-36q0-8-5-13t-13-5h-393q-8 0-13 5t-5 13v36q0 8 5 13t13 5h393z" horiz-adv-x="857.1" />
<glyph glyph-name="folder-empty" unicode="&#xf114;" d="M857 118v393q0 22-15 38t-38 15h-393q-23 0-38 16t-16 38v36q0 22-15 38t-38 15h-179q-22 0-38-15t-16-38v-536q0-22 16-38t38-16h679q22 0 38 16t15 38z m72 393v-393q0-51-37-88t-88-37h-679q-51 0-88 37t-37 88v536q0 51 37 88t88 37h179q51 0 88-37t37-88v-18h375q51 0 88-37t37-88z" horiz-adv-x="928.6" />
<glyph glyph-name="paragraph" unicode="&#xf1dd;" d="M713 745v-41q0-16-10-34t-24-18q-28 0-30-1-14-3-18-17-1-6-1-36v-643q0-14-11-24t-24-10h-60q-14 0-24 10t-10 24v680h-80v-680q0-14-9-24t-25-10h-60q-14 0-24 10t-10 24v277q-82 7-137 33-70 33-107 100-36 65-36 145 0 92 50 159 49 66 116 89 62 21 233 21h267q14 0 24-10t10-24z" horiz-adv-x="714.3" />
</font>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -1394,22 +1394,32 @@ label .help, .label .help{
border: 1px solid #e0474c;
color: #eee;
}
.blox-editor button.delete{
.blox-editor .sideaction{
position: absolute;
right: 1px;
top: 1px;
font-weight: 700;
right: -22px;
top: 0px;
}
.blox-editor button.delete, .blox-editor .icon-resize-full-alt{
display: block;
font-weight: 300;
font-size: 0.9em;
background: #fff;
color: #fff;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
padding: 0px;
margin: 0px 0px 1px;
border: 0px;
border-radius: 2px;
padding: 2px 4px;
border-radius: 1px;
}
.blox-editor:hover button.delete{
background: #e0474c;
color: #fff;
}
.blox-editor button.delete:hover{
cursor: pointer;
background: #cc4146;
}
.blox-editor button.edit:disabled, .blox-editor button.cancel:disabled{

View File

@ -6,24 +6,24 @@
<div class="formWrapper">
<section id="blox">
<div class="blox-title"></div>
<div class="blox-body">
<content-block class="title" :body="false">
<div class="blox title" @click="setData( $event, 'text-markdown')" data-id="0" id="blox-0">{{ title }}</div>
</content-block>
<div id="sortblox">
{% for id, block in content %}
{% if loop.first %}
<content-block class="title" :body="false">
<div class="blox title" @click="setData( $event, 'text-markdown')" data-id="{{ id }}" id="blox-{{id}}">{{block}}</div>
</content-block>
{% else %}
{% for id, block in content %}
<content-block :body="true">
<div class="blox" @click="setData( $event, 'textarea-markdown' )" data-id="{{ id }}" id="blox-{{id}}">{{block}}</div>
</content-block>
{% endif %}
{% endfor %}
{% endfor %}
<content-block :body="true" v-for="newBlock in newBlocks"><div class="blox" @click="setData( $event, 'textarea-markdown' )" :data-id="newBlock.id" :id="newBlock.blockId" v-html="newBlock.content"></div></content-block>
<content-block :body="true" v-for="newBlock in newBlocks"><div class="blox" @click="setData( $event, 'textarea-markdown' )" :data-id="newBlock.id" :id="newBlock.blockId" v-html="newBlock.content"></div></content-block>
</div>
<div class="format-bar">
<content-block :body="false">

View File

@ -301,4 +301,26 @@
})(thisTarget);
}
}
/**
* Element.closest() polyfill
* https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
*/
if (!Element.prototype.closest) {
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
}
Element.prototype.closest = function (s) {
var el = this;
var ancestor = this;
if (!document.documentElement.contains(el)) return null;
do {
if (ancestor.matches(s)) return ancestor;
ancestor = ancestor.parentElement;
} while (ancestor !== null);
return null;
};
}

View File

@ -11,7 +11,7 @@ const contentComponent = Vue.component('content-block', {
},
methods: {
getData: function()
{
{
self = this;
if(self.$root.$data.freeze == false && self.$root.$data.blockType != '')
@ -21,7 +21,25 @@ const contentComponent = Vue.component('content-block', {
this.edit = true;
this.compmarkdown = self.$root.$data.blockMarkdown;
this.componentType = self.$root.$data.blockType;
self.$root.sortable.option("disabled",true);
}
/*
window.addEventListener('click', function(e)
{
if (!e.target.closest('.editactive'))
{
console.info('not found');
publishController.errors.message = false;
this.preview = 'visible';
this.edit = false;
this.compmarkdown = '';
self.componentType = false;
self.$root.$data.freeze = false;
self.$root.sortable.option("disabled",false);
}
});
*/
},
cancelBlock: function()
{
@ -32,6 +50,7 @@ const contentComponent = Vue.component('content-block', {
this.componentType = false;
self = this;
self.$root.$data.freeze = false;
self.$root.sortable.option("disabled",false);
},
submitBlock: function(e){
var emptyline = /^\s*$(?:\r\n?|\n)/gm;
@ -47,9 +66,11 @@ const contentComponent = Vue.component('content-block', {
this.componentType = false;
self = this;
self.$root.$data.freeze = false;
self.$root.sortable.option("disabled",false);
}
else
{
self.$root.sortable.option("disabled",false);
this.saveBlock();
}
}
@ -129,8 +150,9 @@ const contentComponent = Vue.component('content-block', {
}, method, url, params);
},
deleteBlock: function(event)
{
var bloxeditor = event.target.parentElement;
{
var bloxeditor = event.target.parentElement.parentElement;
console.info(bloxeditor);
var bloxid = bloxeditor.getElementsByClassName('blox')[0].dataset.id;
bloxeditor.id = "delete-"+bloxid;
@ -181,6 +203,7 @@ const contentComponent = Vue.component('content-block', {
var length = blox.length;
for (var i = 0; i < length; i++ ) {
blox[i].id = "blox-" + i;
blox[i].dataset.id = i;
}
self.$root.$data.freeze = false;
@ -195,7 +218,26 @@ const contentComponent = Vue.component('content-block', {
}, method, url, params);
},
},
template: '<div class="blox-editor"><div><div @keyup.enter="submitBlock" @click="getData"><transition name="fade-editor"><component :disabled="disabled" :compmarkdown="compmarkdown" @updatedMarkdown="compmarkdown = $event" :is="componentType"></component></transition><div :class="preview"><slot></slot></div></div><div class="blox-buttons" v-if="edit"><button class="edit" :disabled="disabled" @click.prevent="saveBlock">save</button><button class="cancel" :disabled="disabled" @click.prevent="cancelBlock">cancel</button></div><button v-if="body" class="delete" :disabled="disabled" title="delete content-block" @click.prevent="deleteBlock($event)">x</button></div></div>',
/*
mounted: function() {
var self = this;
self.sortable = new Sortable(sortblox, {
animation: 150,
onEnd: function (evt) {
var params = {
'url': document.getElementById("path").value,
'old_index': evt.oldIndex,
'new_index': evt.newIndex,
'csrf_name': document.getElementById("csrf_name").value,
'csrf_value': document.getElementById("csrf_value").value,
};
self.moveBlock(params);
},
});
},
*/
template: '<div class="blox-editor"><div :class="{ editactive: edit }"><div @keyup.enter="submitBlock" @click="getData"><transition name="fade-editor"><component :disabled="disabled" :compmarkdown="compmarkdown" @updatedMarkdown="compmarkdown = $event" :is="componentType"></component></transition><div :class="preview"><slot></slot></div></div><div class="blox-buttons" v-if="edit"><button class="edit" :disabled="disabled" @click.prevent="saveBlock">save</button><button class="cancel" :disabled="disabled" @click.prevent="cancelBlock">cancel</button></div><div class="sideaction" v-if="body"><button class="delete" :disabled="disabled" title="delete content-block" @click.prevent="deleteBlock($event)"><i class="icon-cancel"></i></button></div></div></div>',
})
const textareaComponent = Vue.component('textarea-markdown', {
@ -291,6 +333,20 @@ let editor = new Vue({
}
}
}, method, url, params);
self.sortable = new Sortable(sortblox, {
animation: 150,
onEnd: function (evt) {
var params = {
'url': document.getElementById("path").value,
'old_index': evt.oldIndex,
'new_index': evt.newIndex,
'csrf_name': document.getElementById("csrf_name").value,
'csrf_value': document.getElementById("csrf_value").value,
};
self.moveBlock(params);
},
});
},
methods: {
setData: function(event, blocktype, body)
@ -299,5 +355,51 @@ let editor = new Vue({
this.blockType = blocktype;
this.blockMarkdown = this.markdown[this.blockId];
},
moveBlock: function(params)
{
publishController.errors.message = false;
var url = this.root + '/api/v1/moveblock';
var self = this;
var method = 'PUT';
sendJson(function(response, httpStatus)
{
if(httpStatus == 400)
{
}
if(response)
{
var result = JSON.parse(response);
if(result.errors)
{
publishController.errors.message = result.errors;
publishController.publishDisabled = false;
return false;
}
else
{
var blox = document.getElementsByClassName("blox");
var length = blox.length;
for (var i = 0; i < length; i++ ) {
blox[i].id = "blox-" + i;
blox[i].dataset.id = i;
}
self.freeze = false;
self.markdown = result.markdown;
self.blockMarkdown = '';
self.blockType = '';
publishController.publishDisabled = false;
publishController.publishResult = "";
}
}
}, method, url, params);
},
}
});

View File

@ -164,6 +164,7 @@ $container['view'] = function ($container)
$view->addExtension(new Slim\Views\TwigExtension($container['router'], $basePath));
$view->addExtension(new Twig_Extension_Debug());
$view->addExtension(new Typemill\Extensions\TwigUserExtension());
$view->addExtension(new Typemill\Extensions\TwigMarkdownExtension());
/* use {{ base_url() }} in twig templates */
$view['base_url'] = $container['request']->getUri()->getBaseUrl();