1
0
mirror of https://github.com/typemill/typemill.git synced 2025-01-16 13:00:26 +01:00

Version 1.5.2: Shortcode component for visual editor

This commit is contained in:
trendschau 2021-12-21 23:19:51 +01:00
parent e2fedd8878
commit 105680b1bc
7 changed files with 186 additions and 2 deletions

View File

@ -0,0 +1,22 @@
flatfilecms:
title: 'Flat File CMS for simple projects'
cover: media/live/cover-report.png
description: 'Another publication from cmsstash that will uncover the secrets of database-less web publishing. Read about all flat file cms and make an informed choice.'
downloadlabel: ''
downloadurl: ''
firstbuttonlabel: ''
firstbuttonurl: ''
secondbuttonlabel: ''
secondbuttonurl: ''
downloadlabel1: 'Download now'
downloadlabel2: 'Buy on amazon'
enterprisecms:
title: 'Das CMS Drupal: Open Source für Enterprise'
cover: ''
description: ''
downloadlabel: ''
downloadurl: ''
firstbuttonlabel: ''
firstbuttonurl: ''
secondbuttonlabel: ''
secondbuttonurl: ''

View File

@ -5,6 +5,7 @@ namespace Typemill\Controllers;
use Slim\Http\Request;
use Slim\Http\Response;
use Typemill\Extensions\ParsedownExtension;
use Typemill\Events\OnShortcodeFound;
class ControllerAuthorBlockApi extends ControllerAuthor
{
@ -570,4 +571,27 @@ class ControllerAuthorBlockApi extends ControllerAuthor
return $response->withJson(array('markdown' => $pageMarkdown, 'toc' => $toc, 'errors' => $errors));
}
public function getShortcodeData(Request $request, Response $response, $args)
{
/* get params from call */
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
$errors = false;
# minimum permission is that user is allowed to update his own content
if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
{
return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to delete this content.']), 403);
}
$shortcodeData = $this->c->dispatcher->dispatch('onShortcodeFound', new OnShortcodeFound(['name' => 'registershortcode', 'data' => []]))->getData();
if(empty($shortcodeData['data']))
{
return $response->withJson(array('shortcodedata' => false));
}
return $response->withJson(array('shortcodedata' => $shortcodeData['data']));
}
}

View File

@ -36,6 +36,7 @@ $app->post('/api/v1/block', ControllerAuthorBlockApi::class . ':addBlock')->setN
$app->put('/api/v1/block', ControllerAuthorBlockApi::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/block', ControllerAuthorBlockApi::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/moveblock', ControllerAuthorBlockApi::class . ':moveBlock')->setName('api.block.move')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/shortcodedata', ControllerAuthorBlockApi::class . ':getShortcodeData')->setName('api.shortcodedata.get')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/video', ControllerAuthorMediaApi::class . ':saveVideoImage')->setName('api.video.save')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/medialib/images', ControllerAuthorMediaApi::class . ':getMediaLibImages')->setName('api.medialibimg.get')->add(new RestrictApiAccess($container['router']));

View File

@ -82,7 +82,7 @@ class Settings
'userPath' => $rootPath . 'settings' . DIRECTORY_SEPARATOR . 'users',
'authorPath' => __DIR__ . DIRECTORY_SEPARATOR . 'author' . DIRECTORY_SEPARATOR,
'editor' => 'visual',
'formats' => ['markdown', 'headline', 'ulist', 'olist', 'table', 'quote', 'notice', 'image', 'video', 'file', 'toc', 'hr', 'definition', 'code'],
'formats' => ['markdown', 'headline', 'ulist', 'olist', 'table', 'quote', 'notice', 'image', 'video', 'file', 'toc', 'hr', 'definition', 'code', 'shortcode'],
'contentFolder' => 'content',
'version' => '1.5.1.1',
'setup' => true,

View File

@ -63,6 +63,13 @@ let determiner = {
}
return false;
},
shortcode: function(block,lines,firstChar,secondChar,thirdChar){
if( firstChar == '[' && secondChar == ':')
{
return "shortcode-component";
}
return false;
},
notice: function(block,lines,firstChar,secondChar,thirdChar){
if( firstChar == '!' && ( secondChar == '!' || secondChar == ' ') )
{
@ -94,4 +101,5 @@ let bloxFormats = {
hr: { label: '<svg class="icon icon-pagebreak"><use xlink:href="#icon-pagebreak"></use></svg>', title: 'Horizontal Line', component: 'hr-component' },
definition: { label: '<svg class="icon icon-dots-two-vertical"><use xlink:href="#icon-dots-two-vertical"></use></svg>', title: 'Definition List', component: 'definition-component' },
code: { label: '<svg class="icon icon-embed"><use xlink:href="#icon-embed"></use></svg>', title: 'Code', component: 'code-component' },
shortcode: { label: '<svg class="icon icon-square-brackets"><use xlink:href="#icon-square-brackets"></use></svg>', title: 'Shortcode', component: 'shortcode-component' },
};

View File

@ -781,6 +781,132 @@ const codeComponent = Vue.component('code-component', {
},
})
const shortcodeComponent = Vue.component('shortcode-component', {
props: ['compmarkdown', 'disabled'],
data: function(){
return {
shortcodedata: false,
shortcodename: '',
markdown: '',
searchresults: [],
}
},
template: '<div>' +
'<div class="contenttype"><svg class="icon icon-square-brackets"><use xlink:href="#icon-square-brackets"></use></svg></div>' +
'<div v-if="shortcodedata" class="pa4 bg-tm-gray" ref="markdown">' +
'<label class="w-20 dib" for="shortcodename">{{ \'Shortcode\'|translate }}: </label>' +
'<select class="w-80 dib bg-white" title="shortcodename" v-model="shortcodename"><option v-for="shortcode,name in shortcodedata" :value="name">{{ name }}</option></select>' +
'<div class="mt1 mb1" v-for="key,attribute in shortcodedata[shortcodename]">' +
'<label class="w-20 dib" for="key">{{ attribute }}: </label>' +
'<input class="w-80 dib bg-white" type="text" v-model="shortcodedata[shortcodename][attribute].value" @input="createmarkdown(shortcodename,attribute)">' +
'<div class="w-20 dib v-top tr"> </div>' +
'<div class="w-80 dib mb1 bg-white">' +
'<span class="db bt b--light-gray pa2 hover-bg-near-white pointer" v-for="item in searchresults" @click="selectsearch(item,attribute)">{{item}}</span>' +
'</div>' +
'</div>' +
'</div>' +
'<textarea v-else class="mdcontent" ref="markdown" placeholder="No shortcodes are registered" disabled></textarea>' +
'</div>',
mounted: function(){
var myself = this;
myaxios.get('/api/v1/shortcodedata',{
params: {
'url': document.getElementById("path").value,
'csrf_name': document.getElementById("csrf_name").value,
'csrf_value': document.getElementById("csrf_value").value,
}
})
.then(function (response) {
if(response.data.shortcodedata !== false)
{
myself.shortcodedata = response.data.shortcodedata;
myself.parseshortcode();
}
})
.catch(function (error)
{
if(error.response)
{
}
});
},
methods: {
parseshortcode: function()
{
if(this.compmarkdown)
{
var shortcodestring = this.compmarkdown.trim();
shortcodestring = shortcodestring.slice(2,-2);
this.shortcodename = shortcodestring.substr(0,shortcodestring.indexOf(' '));
var regexp = /(\w+)\s*=\s*("[^"]*"|\'[^\']*\'|[^"\'\\s>]*)/g;
var matches = shortcodestring.matchAll(regexp);
matches = Array.from(matches);
matchlength = matches.length;
if(matchlength > 0)
{
for(var i=0;i<matchlength;i++)
{
var attribute = matches[i][1];
var attributeValue = matches[i][2].replaceAll('"','');
this.shortcodedata[this.shortcodename][attribute].value = attributeValue;
}
}
}
},
createmarkdown: function(shortcodename,attribute)
{
/* search */
this.searchresults = [];
if(this.shortcodedata[shortcodename][attribute].content !== undefined && this.shortcodedata[shortcodename][attribute].value != '')
{
var searchterm = this.shortcodedata[shortcodename][attribute].value;
var searchitems = this.shortcodedata[shortcodename][attribute].content;
for(var item in searchitems)
{
if(searchitems[item].indexOf(searchterm) > -1 && searchitems[item] !== searchterm)
{
this.searchresults.push(searchitems[item]);
if(this.searchresults.length > 4){ break; }
}
}
}
/* markdown */
var attributes = '';
for (var attribute in this.shortcodedata[shortcodename])
{
if(this.shortcodedata[shortcodename].hasOwnProperty(attribute))
{
attributes += ' ' + attribute + '="' + this.shortcodedata[shortcodename][attribute].value + '"';
}
}
this.markdown = '[:' + shortcodename + attributes + ' :]';
this.$emit('updatedMarkdown', this.markdown);
},
selectsearch: function(item,attribute)
{
/* check if still reactive */
this.shortcodedata[this.shortcodename][attribute].value = item;
this.createmarkdown(this.shortcodename,attribute);
},
updatemarkdown: function(event)
{
this.$emit('updatedMarkdown', event.target.value);
},
},
})
const quoteComponent = Vue.component('quote-component', {
props: ['compmarkdown', 'disabled'],
template: '<div>' +

View File

@ -174,6 +174,9 @@
<symbol id="icon-shrink2" viewBox="0 0 32 32">
<path d="M14 18v13l-5-5-6 6-3-3 6-6-5-5zM32 3l-6 6 5 5h-13v-13l5 5 6-6z"></path>
</symbol>
<symbol id="icon-square-brackets" viewBox="0 0 21 21">
<path d="M 4.791 18.885 L 4.791 20.518 L 0 20.518 L 0 0 L 4.791 0 L 4.791 1.622 L 2.052 1.622 L 2.052 18.885 L 4.791 18.885 Z M 20.958 0 L 20.958 20.518 L 16.178 20.518 L 16.178 18.885 L 18.906 18.885 L 18.906 1.622 L 16.178 1.622 L 16.178 0 L 20.958 0 Z M 6.542 4.952 A 1.326 1.326 0 0 0 6.404 5.11 Q 6.102 5.516 6.102 6.166 A 2.167 2.167 0 0 0 6.15 6.638 A 1.453 1.453 0 0 0 6.553 7.38 A 1.472 1.472 0 0 0 7.616 7.82 A 1.702 1.702 0 0 0 7.626 7.82 A 1.445 1.445 0 0 0 8.669 7.385 Q 9.109 6.95 9.109 6.166 A 2.149 2.149 0 0 0 9.058 5.685 A 1.429 1.429 0 0 0 8.658 4.958 A 1.482 1.482 0 0 0 7.595 4.522 Q 6.982 4.522 6.542 4.952 Z M 12.311 4.952 A 1.326 1.326 0 0 0 12.173 5.11 Q 11.87 5.516 11.87 6.166 A 2.167 2.167 0 0 0 11.919 6.638 A 1.453 1.453 0 0 0 12.321 7.38 A 1.472 1.472 0 0 0 13.385 7.82 A 1.702 1.702 0 0 0 13.394 7.82 A 1.445 1.445 0 0 0 14.437 7.385 Q 14.878 6.95 14.878 6.166 A 2.149 2.149 0 0 0 14.827 5.685 A 1.429 1.429 0 0 0 14.427 4.958 A 1.482 1.482 0 0 0 13.363 4.522 Q 12.751 4.522 12.311 4.952 Z M 9.06 14.192 A 1.427 1.427 0 0 0 8.653 13.455 Q 8.196 13.02 7.584 13.02 A 1.442 1.442 0 0 0 6.542 13.449 A 1.326 1.326 0 0 0 6.404 13.607 Q 6.102 14.013 6.102 14.663 A 2.679 2.679 0 0 0 6.102 14.698 Q 6.105 14.959 6.16 15.18 A 1.407 1.407 0 0 0 6.542 15.866 A 1.455 1.455 0 0 0 7.595 16.296 Q 8.207 16.296 8.658 15.866 A 1.405 1.405 0 0 0 9.056 15.154 A 2.131 2.131 0 0 0 9.109 14.663 A 2.134 2.134 0 0 0 9.06 14.192 Z M 14.829 14.192 A 1.427 1.427 0 0 0 14.421 13.455 Q 13.965 13.02 13.353 13.02 A 1.442 1.442 0 0 0 12.311 13.449 A 1.326 1.326 0 0 0 12.173 13.607 Q 11.87 14.013 11.87 14.663 A 2.679 2.679 0 0 0 11.87 14.698 Q 11.874 14.959 11.928 15.18 A 1.407 1.407 0 0 0 12.311 15.866 A 1.455 1.455 0 0 0 13.363 16.296 Q 13.976 16.296 14.427 15.866 A 1.405 1.405 0 0 0 14.825 15.154 A 2.131 2.131 0 0 0 14.878 14.663 A 2.134 2.134 0 0 0 14.829 14.192 Z" />
</symbol>
{{ assets.renderSvg() }}
</defs>
<defs>
</svg>

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 27 KiB