mirror of
https://github.com/typemill/typemill.git
synced 2025-08-08 15:16:53 +02:00
finish raw editor
This commit is contained in:
@@ -154,6 +154,132 @@ class ControllerApiAuthorArticle extends Controller
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
public function updateDraft(Request $request, Response $response, $args)
|
||||
{
|
||||
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'mycontent', 'edit');
|
||||
if(!$validRights)
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'You do not have enough rights.',
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
|
||||
}
|
||||
|
||||
$params = $request->getParsedBody();
|
||||
$validate = new Validation();
|
||||
$validInput = $validate->articleUpdate($params);
|
||||
if($validInput !== true)
|
||||
{
|
||||
$errors = $validate->returnFirstValidationErrors($validInput);
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => reset($errors),
|
||||
'errors' => $errors
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
|
||||
}
|
||||
|
||||
$navigation = new Navigation();
|
||||
$urlinfo = $this->c->get('urlinfo');
|
||||
$item = $this->getItem($navigation, $params['url'], $urlinfo);
|
||||
if(!$item)
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'page not found',
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
|
||||
}
|
||||
|
||||
# save draft content
|
||||
$content = new Content($urlinfo['baseurl']);
|
||||
$markdown = $params['title'] . PHP_EOL . PHP_EOL . $params['body'];
|
||||
$markdownArray = $content->markdownTextToArray($markdown);
|
||||
$content->saveDraftMarkdown($item, $markdownArray);
|
||||
|
||||
# refresh navigation and item
|
||||
$navigation->clearNavigation();
|
||||
$draftNavigation = $navigation->getDraftNavigation($urlinfo, $this->settings['langattr']);
|
||||
$draftNavigation = $navigation->setActiveNaviItems($draftNavigation, $item->keyPathArray);
|
||||
$item = $navigation->getItemWithKeyPath($draftNavigation, $item->keyPathArray);
|
||||
|
||||
# refresh content
|
||||
$draftMarkdown = $content->getDraftMarkdown($item);
|
||||
$draftMarkdownHtml = $content->addDraftHtml($draftMarkdown);
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'item' => $item,
|
||||
'navigation' => $draftNavigation,
|
||||
'content' => $draftMarkdownHtml
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
public function publishDraft(Request $request, Response $response, $args)
|
||||
{
|
||||
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'mycontent', 'edit');
|
||||
if(!$validRights)
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'You do not have enough rights.',
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
|
||||
}
|
||||
|
||||
$params = $request->getParsedBody();
|
||||
$validate = new Validation();
|
||||
$validInput = $validate->articleUpdate($params);
|
||||
if($validInput !== true)
|
||||
{
|
||||
$errors = $validate->returnFirstValidationErrors($validInput);
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => reset($errors),
|
||||
'errors' => $errors
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
|
||||
}
|
||||
|
||||
$navigation = new Navigation();
|
||||
$urlinfo = $this->c->get('urlinfo');
|
||||
$item = $this->getItem($navigation, $params['url'], $urlinfo);
|
||||
if(!$item)
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'page not found',
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
|
||||
}
|
||||
|
||||
# save draft content
|
||||
$content = new Content($urlinfo['baseurl']);
|
||||
$markdown = $params['title'] . PHP_EOL . PHP_EOL . $params['body'];
|
||||
$markdownArray = $content->markdownTextToArray($markdown);
|
||||
$content->publishMarkdown($item, $markdownArray);
|
||||
|
||||
# refresh navigation and item
|
||||
$navigation->clearNavigation();
|
||||
$draftNavigation = $navigation->getDraftNavigation($urlinfo, $this->settings['langattr']);
|
||||
$draftNavigation = $navigation->setActiveNaviItems($draftNavigation, $item->keyPathArray);
|
||||
$item = $navigation->getItemWithKeyPath($draftNavigation, $item->keyPathArray);
|
||||
|
||||
# refresh content
|
||||
$draftMarkdown = $content->getDraftMarkdown($item);
|
||||
$draftMarkdownHtml = $content->addDraftHtml($draftMarkdown);
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'item' => $item,
|
||||
'navigation' => $draftNavigation,
|
||||
'content' => $draftMarkdownHtml
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
public function discardArticleChanges(Request $request, Response $response, $args)
|
||||
{
|
||||
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'mycontent', 'edit');
|
||||
@@ -381,6 +507,14 @@ class ControllerApiAuthorArticle extends Controller
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
|
||||
}
|
||||
|
||||
# set variables
|
||||
$urlinfo = $this->c->get('urlinfo');
|
||||
$langattr = $this->settings['langattr'] ?? 'en';
|
||||
|
||||
$itemKeyPath = explode('.', $params['item_id']);
|
||||
$parentKeyFrom = explode('.', $params['parent_id_from']);
|
||||
$parentKeyTo = explode('.', $params['parent_id_to']);
|
||||
|
||||
# get navigation
|
||||
$navigation = new Navigation();
|
||||
$draftNavigation = $navigation->getDraftNavigation($urlinfo, $langattr);
|
||||
@@ -396,10 +530,6 @@ class ControllerApiAuthorArticle extends Controller
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
|
||||
}
|
||||
|
||||
$itemKeyPath = explode('.', $params['item_id']);
|
||||
$parentKeyFrom = explode('.', $params['parent_id_from']);
|
||||
$parentKeyTo = explode('.', $params['parent_id_to']);
|
||||
|
||||
# if an item is moved to the first level
|
||||
if($params['parent_id_to'] == '')
|
||||
{
|
||||
|
@@ -27,10 +27,10 @@ class ControllerWebAuthor extends Controller
|
||||
$draftNavigation = $navigation->getDraftNavigation($urlinfo, $langattr);
|
||||
$home = $navigation->getHomepageItem($urlinfo['baseurl']);
|
||||
|
||||
|
||||
if($url == '/')
|
||||
{
|
||||
$item = $home;
|
||||
$item->active = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -65,17 +65,6 @@ class ControllerWebAuthor extends Controller
|
||||
|
||||
$draftMarkdownHtml = $content->addDraftHtml($draftMarkdown);
|
||||
|
||||
/*
|
||||
if(isset($draftHtml[0]))
|
||||
{
|
||||
$title = $draftHtml[0];
|
||||
unset($draftHtml[0]);
|
||||
}
|
||||
|
||||
echo '<pre>';
|
||||
print_r($draftHtml);
|
||||
die();
|
||||
*/
|
||||
return $this->c->get('view')->render($response, 'content/blox-editor.twig', [
|
||||
'settings' => $this->settings,
|
||||
'mainnavi' => $mainNavigation,
|
||||
@@ -83,82 +72,84 @@ class ControllerWebAuthor extends Controller
|
||||
'jsdata' => [
|
||||
'settings' => $this->settings,
|
||||
'urlinfo' => $urlinfo,
|
||||
'labels' => $this->c->get('translations'),
|
||||
'navigation' => $draftNavigation,
|
||||
'item' => $item,
|
||||
'home' => $home,
|
||||
'content' => $draftMarkdownHtml
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
|
||||
|
||||
# set information for homepage
|
||||
$this->setHomepage($args);
|
||||
# we have to check ownership here to use it for permission-check in templates
|
||||
$this->checkContentOwnership();
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
|
||||
if($content == '')
|
||||
{
|
||||
$content = [];
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension($this->uri->getBaseUrl());
|
||||
|
||||
# to fix footnote-logic in parsedown, set visual mode to true
|
||||
$parsedown->setVisualMode();
|
||||
|
||||
# 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);
|
||||
}
|
||||
|
||||
# extract title and delete from content array, array will start at 1 after that.
|
||||
$title = '# add title';
|
||||
if(isset($content[0]))
|
||||
{
|
||||
$title = $content[0];
|
||||
unset($content[0]);
|
||||
}
|
||||
|
||||
return $this->renderIntern($response, 'editor/editor-blox.twig', array(
|
||||
'acl' => $this->c->acl,
|
||||
'mycontent' => $this->mycontent,
|
||||
'navigation' => $this->structureDraft,
|
||||
'homepage' => $this->homepage,
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'item' => $this->item,
|
||||
'settings' => $this->settings
|
||||
));
|
||||
}
|
||||
|
||||
public function showContent(Request $request, Response $response, $args)
|
||||
public function showRaw(Request $request, Response $response, $args)
|
||||
{
|
||||
# get url for requested page
|
||||
$url = isset($args['route']) ? '/' . $args['route'] : '/';
|
||||
$urlinfo = $this->c->get('urlinfo');
|
||||
$fullUrl = $urlinfo['baseurl'] . $url;
|
||||
$langattr = $this->settings['langattr'];
|
||||
|
||||
$navigation = new Navigation();
|
||||
$draftNavigation = $navigation->getDraftNavigation($urlinfo, $langattr);
|
||||
$home = $navigation->getHomepageItem($urlinfo['baseurl']);
|
||||
|
||||
if($url == '/')
|
||||
{
|
||||
$item = $home;
|
||||
$item->active = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$extendedNavigation = $navigation->getExtendedNavigation($urlinfo, $langattr);
|
||||
|
||||
$pageinfo = $extendedNavigation[$url] ?? false;
|
||||
if(!$pageinfo)
|
||||
{
|
||||
return $this->c->get('view')->render($response->withStatus(404), '404.twig', [
|
||||
'title' => 'Typemill Author Area',
|
||||
'description' => 'Typemill Version 2 wird noch besser als Version 1.'
|
||||
]);
|
||||
}
|
||||
|
||||
$keyPathArray = explode(".", $pageinfo['keyPath']);
|
||||
|
||||
# extend : $request->getAttribute('c_userrole')
|
||||
$draftNavigation = $navigation->getDraftNavigation($urlinfo, $langattr);
|
||||
|
||||
$draftNavigation = $navigation->setActiveNaviItems($draftNavigation, $keyPathArray);
|
||||
|
||||
$item = $navigation->getItemWithKeyPath($draftNavigation, $keyPathArray);
|
||||
}
|
||||
|
||||
# $item->modified = ($item->published OR $item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
$mainNavigation = $navigation->getMainNavigation($request->getAttribute('c_userrole'), $this->c->get('acl'), $urlinfo, $this->settings['editor']);
|
||||
|
||||
$content = new Content($urlinfo['baseurl']);
|
||||
|
||||
$draftMarkdown = $content->getDraftMarkdown($item);
|
||||
|
||||
$draftMarkdownHtml = $content->addDraftHtml($draftMarkdown);
|
||||
|
||||
return $this->c->get('view')->render($response, 'content/raw-editor.twig', [
|
||||
'settings' => $this->settings,
|
||||
'mainnavi' => $mainNavigation,
|
||||
'content' => $draftMarkdownHtml,
|
||||
'jsdata' => [
|
||||
'settings' => $this->settings,
|
||||
'urlinfo' => $urlinfo,
|
||||
'labels' => $this->c->get('translations'),
|
||||
'navigation' => $draftNavigation,
|
||||
'item' => $item,
|
||||
'home' => $home,
|
||||
'content' => $draftMarkdownHtml,
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# get params from call
|
||||
# $this->uri = $request->getUri()->withUserInfo('');
|
||||
# $this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
|
||||
|
@@ -54,9 +54,9 @@ class Content
|
||||
return $markdownArray;
|
||||
}
|
||||
|
||||
public function saveDraftMarkdown($item, $markdown)
|
||||
public function saveDraftMarkdown($item, array $markdownArray)
|
||||
{
|
||||
$markdown = json_encode($markdown);
|
||||
$markdown = json_encode($markdownArray);
|
||||
|
||||
if($this->storage->writeFile('contentFolder', '', $item->pathWithoutType . '.txt', $markdown))
|
||||
{
|
||||
@@ -66,6 +66,16 @@ class Content
|
||||
return $this->storage->getError();
|
||||
}
|
||||
|
||||
public function markdownArrayToText(array $markdownArray)
|
||||
{
|
||||
return $this->parsedown->arrayBlocksToMarkdown($markdownArray);
|
||||
}
|
||||
|
||||
public function markdownTextToArray(string $markdown)
|
||||
{
|
||||
return $this->parsedown->markdownToArrayBlocks($markdown);
|
||||
}
|
||||
|
||||
public function publishMarkdown($item, array $markdownArray)
|
||||
{
|
||||
$markdown = $this->parsedown->arrayBlocksToMarkdown($markdownArray);
|
||||
|
@@ -365,7 +365,7 @@ class Navigation extends Folder
|
||||
$item->urlRel = '/';
|
||||
$item->urlRelWoF = '/';
|
||||
$item->urlAbs = $baseUrl;
|
||||
$item->active = true;
|
||||
$item->active = false;
|
||||
$item->activeParent = false;
|
||||
$item->hide = false;
|
||||
|
||||
|
@@ -434,6 +434,34 @@ class Validation
|
||||
}
|
||||
}
|
||||
|
||||
public function articleUpdate(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
# special conditions for startpage
|
||||
if(isset($params['item_id']) && $params['item_id'] == '')
|
||||
{
|
||||
$v->rule('required', ['url', 'title', 'body']);
|
||||
$v->rule('markdownSecure', 'title');
|
||||
$v->rule('markdownSecure', 'body');
|
||||
}
|
||||
else
|
||||
{
|
||||
$v->rule('required', ['item_id', 'url', 'title', 'body']);
|
||||
$v->rule('regex', 'item_id', '/^[0-9.]+$/i');
|
||||
$v->rule('markdownSecure', 'title');
|
||||
$v->rule('markdownSecure', 'body');
|
||||
}
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -22,13 +22,14 @@
|
||||
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue-blox-config.js?v={{ settings.version }}"></script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue-blox.js?v={{ settings.version }}"></script>
|
||||
<!-- <script src="{{ base_url() }}/system/typemill/author/js/vue-translate.js?v={{ settings.version }}"></script> -->
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue-blox-components.js?v={{ settings.version }}"></script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue-publisher.js?v={{ settings.version }}"></script>
|
||||
<script>
|
||||
|
||||
// add translation to global config here...
|
||||
bloxeditor.config.globalProperties.$filters = translatefilter;
|
||||
bloxeditor.mount('#editor');
|
||||
|
||||
publisher.config.globalProperties.$filters = translatefilter;
|
||||
publisher.mount('#publisher');
|
||||
|
||||
</script>
|
||||
|
28
system/typemill/author/content/raw-editor.twig
Normal file
28
system/typemill/author/content/raw-editor.twig
Normal file
@@ -0,0 +1,28 @@
|
||||
{% extends 'layouts/layoutContent.twig' %}
|
||||
{% block title %}{{ translate('Raw Editor') }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="editor" class="px-12 py-8 bg-stone-50 shadow-md mb-16" v-cloak></div>
|
||||
|
||||
<div id="publisher" class="fixed bottom-0 w-54rem bg-stone-100 border-t border-stone-200 shadow-md" v-cloak></div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block javascript %}
|
||||
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/highlight.min.js?v={{ settings.version }}"></script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue-raw.js?v={{ settings.version }}"></script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue-publisher.js?v={{ settings.version }}"></script>
|
||||
<script>
|
||||
|
||||
raweditor.config.globalProperties.$filters = translatefilter;
|
||||
raweditor.mount('#editor');
|
||||
|
||||
publisher.config.globalProperties.$filters = translatefilter;
|
||||
publisher.mount('#publisher');
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
@@ -55,7 +55,7 @@
|
||||
[data-el="editor"] {
|
||||
border-width: 1px;
|
||||
background:rgb(68 64 60);
|
||||
color: white;
|
||||
color: transparent;
|
||||
caret-color: white;
|
||||
white-space: break-spaces;
|
||||
word-break: break-word;
|
||||
|
@@ -794,6 +794,10 @@ video {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.mb-16 {
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
@@ -866,14 +870,6 @@ video {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.mb-12 {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.mb-16 {
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
@@ -1026,10 +1022,6 @@ video {
|
||||
width: 2.5rem;
|
||||
}
|
||||
|
||||
.w-11\/12 {
|
||||
width: 91.666667%;
|
||||
}
|
||||
|
||||
.w-3\/5 {
|
||||
width: 60%;
|
||||
}
|
||||
@@ -1038,14 +1030,18 @@ video {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.w-3\/4 {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.w-24 {
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
.w-11\/12 {
|
||||
width: 91.666667%;
|
||||
}
|
||||
|
||||
.w-3\/4 {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.max-w-md {
|
||||
max-width: 28rem;
|
||||
}
|
||||
@@ -1202,6 +1198,10 @@ video {
|
||||
border-right-width: 8px;
|
||||
}
|
||||
|
||||
.border-t {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.border-r-2 {
|
||||
border-right-width: 2px;
|
||||
}
|
||||
@@ -1242,10 +1242,6 @@ video {
|
||||
border-left-width: 2px;
|
||||
}
|
||||
|
||||
.border-t {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.border-solid {
|
||||
border-style: solid;
|
||||
}
|
||||
@@ -1259,6 +1255,11 @@ video {
|
||||
border-color: rgb(209 213 219 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-stone-200 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(231 229 228 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-stone-700 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(68 64 60 / var(--tw-border-opacity));
|
||||
@@ -1269,16 +1270,16 @@ video {
|
||||
border-color: rgb(214 211 209 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-stone-200 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(231 229 228 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-teal-500 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(20 184 166 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-yellow-400 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(250 204 21 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-stone-100 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(245 245 244 / var(--tw-border-opacity));
|
||||
@@ -1289,6 +1290,11 @@ video {
|
||||
border-color: rgb(250 250 249 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-rose-500 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(244 63 94 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-red-500 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(239 68 68 / var(--tw-border-opacity));
|
||||
@@ -1299,6 +1305,11 @@ video {
|
||||
border-color: rgb(255 255 255 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-yellow-500 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(234 179 8 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-rose-100 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(255 228 230 / var(--tw-border-opacity));
|
||||
@@ -1314,26 +1325,6 @@ video {
|
||||
border-color: rgb(226 232 240 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-rose-500 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(244 63 94 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-yellow-500 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(234 179 8 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-yellow-300 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(253 224 71 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-yellow-400 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(250 204 21 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-x-transparent {
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
@@ -1359,9 +1350,14 @@ video {
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-stone-300 {
|
||||
.bg-stone-50 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(214 211 209 / var(--tw-bg-opacity));
|
||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-stone-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(245 245 244 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-transparent {
|
||||
@@ -1378,11 +1374,6 @@ video {
|
||||
background-color: rgb(68 64 60 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-stone-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(245 245 244 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-teal-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(20 184 166 / var(--tw-bg-opacity));
|
||||
@@ -1408,26 +1399,11 @@ video {
|
||||
background-color: rgb(87 83 78 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-stone-50 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-yellow-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(234 179 8 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-yellow-400 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(250 204 21 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-yellow-300 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(253 224 71 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-opacity-90 {
|
||||
--tw-bg-opacity: 0.9;
|
||||
}
|
||||
@@ -1489,11 +1465,6 @@ video {
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.px-6 {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
.px-12 {
|
||||
padding-left: 3rem;
|
||||
padding-right: 3rem;
|
||||
@@ -1504,6 +1475,11 @@ video {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.px-6 {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
.px-2 {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
@@ -1519,11 +1495,6 @@ video {
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
|
||||
.px-4 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.py-10 {
|
||||
padding-top: 2.5rem;
|
||||
padding-bottom: 2.5rem;
|
||||
@@ -1539,6 +1510,11 @@ video {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.px-4 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.pr-6 {
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
@@ -1595,14 +1571,14 @@ video {
|
||||
padding-right: 0.75rem;
|
||||
}
|
||||
|
||||
.pt-2 {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.pb-4 {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.pt-2 {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.pt-4 {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
@@ -1660,11 +1636,6 @@ video {
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.text-2xl {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
@@ -1675,6 +1646,11 @@ video {
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.text-2xl {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.text-3xl {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.25rem;
|
||||
@@ -1768,11 +1744,6 @@ video {
|
||||
color: rgb(13 148 136 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-black {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(0 0 0 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-teal-300 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(94 234 212 / var(--tw-text-opacity));
|
||||
@@ -1788,6 +1759,11 @@ video {
|
||||
color: rgb(231 229 228 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-black {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(0 0 0 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-rose-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(244 63 94 / var(--tw-text-opacity));
|
||||
@@ -1798,11 +1774,6 @@ video {
|
||||
color: rgb(6 182 212 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-teal-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(15 118 110 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.underline {
|
||||
-webkit-text-decoration-line: underline;
|
||||
text-decoration-line: underline;
|
||||
@@ -1828,6 +1799,12 @@ video {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.shadow-md {
|
||||
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.shadow-lg {
|
||||
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
|
||||
@@ -1840,12 +1817,6 @@ video {
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.shadow-md {
|
||||
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.outline-none {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
@@ -1970,6 +1941,11 @@ video {
|
||||
background-color: rgb(15 118 110 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-yellow-600:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(202 138 4 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-rose-700:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(190 18 60 / var(--tw-bg-opacity));
|
||||
@@ -1985,11 +1961,6 @@ video {
|
||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-yellow-600:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(202 138 4 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:text-stone-50:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(250 250 249 / var(--tw-text-opacity));
|
||||
@@ -2061,10 +2032,6 @@ video {
|
||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.disabled\:cursor-default:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.disabled\:cursor-not-allowed:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
@@ -303,7 +303,7 @@ bloxeditor.component('code-component', {
|
||||
</svg>
|
||||
</div>
|
||||
<div class="w-full flex p-3 border-b-2 border-stone-700 bg-stone-200">
|
||||
<label class="pr-2 py-1" for="language">Language: </label>
|
||||
<label class="pr-2 py-1" for="language">{{ $filters.translate('Language') }}: </label>
|
||||
<input class="px-2 py-1 flex-grow text-stone-700 focus:outline-none" name="language" type="text" v-model="language" :disabled="disabled" @input="createlanguage">
|
||||
</div>
|
||||
<textarea class="font-mono text-sm opacity-1 w-full bg-transparent px-6 py-3 outline-none" ref="markdown" v-model="codeblock" :disabled="disabled" @input="createmarkdown"></textarea>
|
||||
@@ -621,9 +621,9 @@ bloxeditor.component('table-component', {
|
||||
class="border border-stone-300 text-center text-stone-500"
|
||||
>{{value}}
|
||||
<div v-if="columnbar === value" class="absolute z-20 w-32 text-left text-xs text-white bg-stone-700 transition-1">
|
||||
<div class="p-2 hover:bg-teal-500" @click="addleftcolumn($event, value)">add left column</div>
|
||||
<div class="p-2 hover:bg-teal-500" @click="addrightcolumn($event, value)">add right column</div>
|
||||
<div class="p-2 hover:bg-teal-500" @click="deletecolumn($event, value)">delete column</div>
|
||||
<div class="p-2 hover:bg-teal-500" @click="addleftcolumn($event, value)">{{ $filters.translate('add left column') }}</div>
|
||||
<div class="p-2 hover:bg-teal-500" @click="addrightcolumn($event, value)">{{ $filters.translate('add right column') }}</div>
|
||||
<div class="p-2 hover:bg-teal-500" @click="deletecolumn($event, value)">{{ $filters.translate('delete column') }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<th v-if="rowindex === 1" v-for="(value,colindex) in row"
|
||||
@@ -644,9 +644,9 @@ bloxeditor.component('table-component', {
|
||||
class="p-2 border border-stone-300"
|
||||
>
|
||||
<div v-if="colindex === 0 && rowbar === value" class="rowaction absolute z-20 left-12 w-32 text-left text-xs text-white bg-stone-700">
|
||||
<div class="actionline p-2 hover:bg-teal-500" @click="addaboverow($event, value)">add row above</div>
|
||||
<div class="actionline p-2 hover:bg-teal-500" @click="addbelowrow($event, value)">add row below</div>
|
||||
<div class="actionline p-2 hover:bg-teal-500" @click="deleterow($event, value)">delete row</div>
|
||||
<div class="actionline p-2 hover:bg-teal-500" @click="addaboverow($event, value)">{{ $filters.translate('add row above') }}</div>
|
||||
<div class="actionline p-2 hover:bg-teal-500" @click="addbelowrow($event, value)">{{ $filters.translate('add row below') }}</div>
|
||||
<div class="actionline p-2 hover:bg-teal-500" @click="deleterow($event, value)">{{ $filters.translate('delete row') }}</div>
|
||||
</div>
|
||||
{{ value }}
|
||||
</td>
|
||||
@@ -834,10 +834,16 @@ bloxeditor.component('definition-component', {
|
||||
<div class="flex mb-2" v-for="(description,ddindex) in element.descriptions">
|
||||
<svg class="icon icon-dots-two-vertical mt-3"><use xlink:href="#icon-dots-two-vertical"></use></svg>
|
||||
<textarea class="flex-grow p-2 focus:outline-none" :placeholder="description" v-html="element.descriptions[ddindex]" :disabled="disabled" @input="updatedescription($event, index, ddindex)" @keydown.13.prevent="enter" @blur="updateMarkdown"></textarea>
|
||||
<button title="delete description" class="text-white bg-stone-700 w-6 h-6 text-xs hover:bg-rose-500" @click.prevent="deleteItem($event,index,ddindex)"><svg class="icon icon-minus"><use xlink:href="#icon-minus"></use></svg></button>
|
||||
<button title="delete description" class="text-white bg-stone-700 w-6 h-6 text-xs hover:bg-rose-500" @click.prevent="deleteItem($event,index,ddindex)">
|
||||
<svg class="icon icon-minus">
|
||||
<use xlink:href="#icon-minus"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<button title="add description" class="text-white bg-stone-700 w-6 h-6 text-xs hover:bg-teal-500 ml-4 mr-2" @click.prevent="addItem($event,index)">
|
||||
<svg class="icon icon-plus"><use xlink:href="#icon-plus"></use></svg>
|
||||
<svg class="icon icon-plus">
|
||||
<use xlink:href="#icon-plus"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="text-sm">Add description</span>
|
||||
</div>
|
||||
@@ -849,7 +855,7 @@ bloxeditor.component('definition-component', {
|
||||
<button title="add definition" class="text-white bg-stone-700 w-6 h-6 text-xs hover:bg-teal-500 mr-2" @click.prevent="addDefinition">
|
||||
<svg class="icon icon-plus"><use xlink:href="#icon-plus"></use></svg>
|
||||
</button>
|
||||
<span class="text-sm">Add definition</span>
|
||||
<span class="text-sm">{{ $filters.translate('Add definition') }}</span>
|
||||
<div v-if="load" class="loadwrapper"><span class="load"></span></div>
|
||||
</div>
|
||||
</div>`,
|
||||
@@ -1056,7 +1062,6 @@ bloxeditor.component('inline-formats', {
|
||||
},
|
||||
mounted: function() {
|
||||
this.formatBar = document.getElementById('formatBar');
|
||||
console.info(this.formatBar);
|
||||
window.addEventListener('mouseup', this.onMouseup),
|
||||
window.addEventListener('mousedown', this.onMousedown)
|
||||
},
|
||||
@@ -1239,37 +1244,37 @@ bloxeditor.component('image-component', {
|
||||
<div v-if="load" class="loadwrapper"><span class="load"></span></div>
|
||||
<div class="imgmeta p-8" v-if="imgmeta">
|
||||
<div class="flex mb-2">
|
||||
<label class="w-1/5 py-2" for="imgalt">Alt-Text: </label>
|
||||
<label class="w-1/5 py-2" for="imgalt">{{ $filters.translate('Alt-Text') }}: </label>
|
||||
<input class="w-4/5 p-2" name="imgalt" type="text" placeholder="alt" @input="createmarkdown" v-model="imgalt" max="100" />
|
||||
</div>
|
||||
<div class="flex mb-2">
|
||||
<label class="w-1/5 py-2" for="imgtitle">Title: </label>
|
||||
<label class="w-1/5 py-2" for="imgtitle">{{ $filters.translate('Title') }}: </label>
|
||||
<input class="w-4/5 p-2" name="imgtitle" type="text" placeholder="title" v-model="imgtitle" @input="createmarkdown" max="64" />
|
||||
</div>
|
||||
<div class="flex mb-2">
|
||||
<label class="w-1/5 py-2" for="imgcaption">Caption: </label>
|
||||
<label class="w-1/5 py-2" for="imgcaption">{{ $filters.translate('Caption') }}: </label>
|
||||
<input class="w-4/5 p-2" title="imgcaption" type="text" placeholder="caption" v-model="imgcaption" @input="createmarkdown" max="140" />
|
||||
</div>
|
||||
<div class="flex mb-2">
|
||||
<label class="w-1/5 py-2" for="imgurl">Link: </label>
|
||||
<label class="w-1/5 py-2" for="imgurl">{{ $filters.translate('Link') }}: </label>
|
||||
<input class="w-4/5 p-2" title="imgurl" type="url" placeholder="url" v-model="imglink" @input="createmarkdown" />
|
||||
</div>
|
||||
<div class="flex mb-2">
|
||||
<label class="w-1/5 py-2" for="imgclass">Class: </label>
|
||||
<label class="w-1/5 py-2" for="imgclass">{{ $filters.translate('Class') }}: </label>
|
||||
<select class="w-4/5 p-2 bg-white" title="imgclass" v-model="imgclass" @change="createmarkdown">
|
||||
<option value="center">Center</option>
|
||||
<option value="left">Left</option>
|
||||
<option value="right">Right</option>
|
||||
<option value="center">{{ $filters.translate('Center') }}</option>
|
||||
<option value="left">{{ $filters.translate('Left') }}</option>
|
||||
<option value="right">{{ $filters.translate('Right') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex mb-2">
|
||||
<label class="w-1/5 py-2" for="imgsizes">width/height:</label>
|
||||
<label class="w-1/5 py-2" for="imgsizes">{{ $filters.translate('width/height') }}:</label>
|
||||
<input class="w-2/5 p-2 mr-1" title="imgwidth" type="text" :placeholder="originalwidth" v-model="imgwidth" @input="changewidth" max="6" />
|
||||
<input class="w-2/5 p-2 ml-1" title="imgheight" type="text" :placeholder="originalheight" v-model="imgheight" @input="changeheight" max="6" />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label v-if="showresize" for="saveoriginal" class="flex w-full">
|
||||
<span class="w-1/5">Do not resize:</span>
|
||||
<span class="w-1/5">{{ $filters.translate('Do not resize') }}:</span>
|
||||
<input type="checkbox" class="w-6 h-6" name="saveoriginal" v-model="noresize" @change="createmarkdown" />
|
||||
</label>
|
||||
</div>
|
||||
@@ -1446,7 +1451,7 @@ bloxeditor.component('image-component', {
|
||||
}
|
||||
else
|
||||
{
|
||||
errors = 'Maximum size of image alt-text is 100 characters';
|
||||
errors = this.$filters.translate('Maximum size of image alt-text is 100 characters');
|
||||
imgmarkdown = '![]';
|
||||
}
|
||||
|
||||
@@ -1458,7 +1463,7 @@ bloxeditor.component('image-component', {
|
||||
}
|
||||
else
|
||||
{
|
||||
errors = 'Maximum size of image title is 100 characters';
|
||||
errors = this.$filters.translate('Maximum size of image title is 100 characters');
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1476,7 +1481,7 @@ bloxeditor.component('image-component', {
|
||||
}
|
||||
else
|
||||
{
|
||||
errors = 'Maximum size of image id is 100 characters';
|
||||
errors = this.$filters.translate('Maximum size of image id is 100 characters');
|
||||
}
|
||||
}
|
||||
if(this.imgclass != '')
|
||||
@@ -1487,7 +1492,7 @@ bloxeditor.component('image-component', {
|
||||
}
|
||||
else
|
||||
{
|
||||
errors = 'Maximum size of image class is 100 characters';
|
||||
errors = this.$filters.translate('Maximum size of image class is 100 characters');
|
||||
}
|
||||
}
|
||||
if(this.imgloading != '')
|
||||
@@ -1519,7 +1524,7 @@ bloxeditor.component('image-component', {
|
||||
}
|
||||
else
|
||||
{
|
||||
errors = 'Maximum size of image link is 100 characters';
|
||||
errors = this.$filters.translate('Maximum size of image link is 100 characters');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1531,7 +1536,7 @@ bloxeditor.component('image-component', {
|
||||
}
|
||||
else
|
||||
{
|
||||
errors = 'Maximum size of image caption is 140 characters';
|
||||
errors = this.$filters.translate('Maximum size of image caption is 140 characters');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1770,14 +1775,14 @@ bloxeditor.component('file-component', {
|
||||
<svg class="icon icon-upload">
|
||||
<use xlink:href="#icon-upload"></use>
|
||||
</svg>
|
||||
upload file
|
||||
{{ $filters.translate('upload file') }}
|
||||
</p>
|
||||
</div>
|
||||
<button class="imageselect w-1/2 text-center p-6" @click.prevent="openmedialib()">
|
||||
<svg class="icon icon-paperclip baseline">
|
||||
<use xlink:href="#icon-paperclip"></use>
|
||||
</svg>
|
||||
select from medialib
|
||||
{{ $filters.translate('select from medialib') }}
|
||||
</button>
|
||||
</div>
|
||||
<!--
|
||||
@@ -1796,13 +1801,13 @@ bloxeditor.component('file-component', {
|
||||
<div class="imgmeta p-8" v-if="filemeta">
|
||||
<input title="fileid" type="hidden" placeholder="id" v-model="fileid" @input="createmarkdown" max="140" />
|
||||
<div class="flex mb-2">
|
||||
<label class="w-1/5 py-2" for="filetitle">Title: </label>
|
||||
<label class="w-1/5 py-2" for="filetitle">{{ $filters.translate('Title') }}: </label>
|
||||
<input class="w-4/5 p-2" name="filetitle" type="text" placeholder="Add a title for the download-link" v-model="filetitle" @input="createmarkdown" max="64" />
|
||||
</div>
|
||||
<div class="flex mb-2">
|
||||
<label class="w-1/5 py-2" for="filerestriction">Access for: </label>
|
||||
<select class="w-4/5 p-2 bg-white" name="filerestriction" v-model="selectedrole" @change="updaterestriction">
|
||||
<option disabled value="">Please select</option>
|
||||
<option disabled value="">{{ $filters.translate('Please select') }}</option>
|
||||
<option v-for="role in userroles">{{ role }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -2034,7 +2039,7 @@ bloxeditor.component('shortcode-component', {
|
||||
</div>
|
||||
<div v-if="shortcodedata" class="p-8 bg-stone-100" ref="markdown">
|
||||
<div class="flex mt-2 mb-2">
|
||||
<label class="w-1/5 py-2" for="shortcodename">Shortcode: </label>
|
||||
<label class="w-1/5 py-2" for="shortcodename">{{ $filters.translate('Shortcode') }}: </label>
|
||||
<select class="w-4/5 p-2 bg-white" title="shortcodename" v-model="shortcodename" @change="createmarkdown(shortcodename)"><option v-for="shortcode,name in shortcodedata" :value="name">{{ name }}</option></select>
|
||||
</div>
|
||||
<div class="flex mt-2 mb-2" v-for="key,attribute in shortcodedata[shortcodename]">
|
||||
@@ -2140,7 +2145,7 @@ bloxeditor.component('video-component', {
|
||||
</div>
|
||||
<div>{{ markdown }}</div>
|
||||
<div class="flex mt-2 mb-2">
|
||||
<label class="w-1/5 py-2" for="video">Link to youtube: </label>
|
||||
<label class="w-1/5 py-2" for="video">{{ $filters.translate('Link to youtube') }}: </label>
|
||||
<input class="w-4/5 p-2 bg-white" type="url" ref="markdown" placeholder="https://www.youtube.com/watch?v=" :value="markdown" :disabled="disabled" @input="updatemarkdown($event.target.value)">
|
||||
</div>
|
||||
</div>`,
|
||||
|
@@ -69,7 +69,7 @@ const bloxeditor = Vue.createApp({
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
publishController.errors.message = error.response.data.errors.message;
|
||||
eventBus.$emit('publishermessage', error.response.data.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -143,8 +143,8 @@ bloxeditor.component('new-block',{
|
||||
<div v-if="componentType" class="relative bg-stone-100">
|
||||
<component ref="activeComponent" :disabled="disabled" :markdown="newblockmarkdown" :index="index" @saveBlockEvent="saveNewBlock" @updateMarkdownEvent="updateMarkdownFunction" :is="componentType"></component>
|
||||
<div class="edit-buttons absolute -bottom-3 right-4 z-2 text-xs">
|
||||
<button class="cancel w-20 p-1 border-r border-stone-700 bg-stone-200 hover:bg-rose-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="closeComponent">cancel</button>
|
||||
<button class="save w-20 p-1 border-l border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="saveNewBlock()">save</button>
|
||||
<button class="cancel w-20 p-1 border-r border-stone-700 bg-stone-200 hover:bg-rose-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="closeComponent">{{ $filters.translate('cancel') }}</button>
|
||||
<button class="save w-20 p-1 border-l border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="saveNewBlock()">{{ $filters.translate('save') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -205,6 +205,14 @@ bloxeditor.component('new-block',{
|
||||
self.$root.$data.content = response.data.content;
|
||||
self.closeComponent();
|
||||
eventBus.$emit('closeComponents');
|
||||
if(response.data.navigation)
|
||||
{
|
||||
eventBus.$emit('navigation', response.data.navigation);
|
||||
}
|
||||
if(response.data.item)
|
||||
{
|
||||
eventBus.$emit('item', response.data.item);
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
@@ -225,14 +233,14 @@ bloxeditor.component('content-block', {
|
||||
<div v-if="newblock" class="bg-stone-100">
|
||||
<div class="w-full flex justify-between bg-stone-200">
|
||||
<p class="p-2 pl-4">Choose a content type</p>
|
||||
<button class="p-2 border-l border-stone-700 hover:text-white hover:bg-rose-500 transition-1" @click="closeNewBlock">close</button>
|
||||
<button class="p-2 border-l border-stone-700 hover:text-white hover:bg-rose-500 transition-1" @click="closeNewBlock">{{ $filters.translate('close') }}</button>
|
||||
</div>
|
||||
<new-block :index="index"></new-block>
|
||||
</div>
|
||||
<div class="relative blox-wrapper mb-1">
|
||||
<div v-if="index != 0" class="sideaction hidden absolute -top-3 left-1/2 -translate-x-1/2 z-10 text-xs">
|
||||
<button class="delete w-16 p-1 border-r border-stone-700 bg-stone-200 hover:bg-rose-500 hover:text-white transition-1" @mousedown.prevent="disableSort()" @click.prevent="deleteBlock">del</button>
|
||||
<button class="add w-16 p-1 border-l border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @mousedown.prevent="disableSort()" @click.prevent="openNewBlock">add</button>
|
||||
<button class="delete w-16 p-1 border-r border-stone-700 bg-stone-200 hover:bg-rose-500 hover:text-white transition-1" @mousedown.prevent="disableSort()" @click.prevent="deleteBlock">{{ $filters.translate('delete') }}</button>
|
||||
<button class="add w-16 p-1 border-l border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @mousedown.prevent="disableSort()" @click.prevent="openNewBlock">{{ $filters.translate('add') }}</button>
|
||||
</div>
|
||||
<div v-if="!edit" class="blox-preview px-6 py-3 hover:bg-stone-100 overflow-hidden transition-1" @click="showEditor" v-html="getHtml(element.html)"></div>
|
||||
<div v-else class="blox-editor bg-stone-100">
|
||||
@@ -244,8 +252,8 @@ bloxeditor.component('content-block', {
|
||||
</div>
|
||||
<component ref="activeComponent" :disabled="disabled" :markdown="updatedmarkdown" :index="index" @saveBlockEvent="saveBlock" @updateMarkdownEvent="updateMarkdownFunction" :is="componentType"></component>
|
||||
<div class="edit-buttons absolute -bottom-3 right-4 z-10 text-xs">
|
||||
<button class="cancel w-20 p-1 border-r border-stone-700 bg-stone-200 hover:bg-rose-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="closeEditor">cancel</button>
|
||||
<button class="save w-20 p-1 border-l border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="saveBlock">save</button>
|
||||
<button class="cancel w-20 p-1 border-r border-stone-700 bg-stone-200 hover:bg-rose-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="closeEditor">{{ $filters.translate('cancel') }}</button>
|
||||
<button class="save w-20 p-1 border-l border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="saveBlock">{{ $filters.translate('save') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -368,11 +376,8 @@ bloxeditor.component('content-block', {
|
||||
}
|
||||
if(response.data.item)
|
||||
{
|
||||
eventBus.$emit('item', response.data.item);
|
||||
eventBus.$emit('item', response.data.item);
|
||||
}
|
||||
|
||||
// update the navigation and mark navigation item as modified
|
||||
// navi.getNavi();
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
|
@@ -2,13 +2,13 @@ const navigation = Vue.createApp({
|
||||
template: `
|
||||
<div class="mr-3">
|
||||
<div class="flex w-100 mb-4">
|
||||
<button class="w-1/2 ml-1 hover:bg-stone-700 hover:text-stone-50 border border-stone-200 px-2 py-1 transition duration-100" @click.prevent="collapseNavigation()">collapse all</button>
|
||||
<button class="w-1/2 mr-1 hover:bg-stone-700 hover:text-stone-50 border border-stone-200 px-2 py-1 transition duration-100" @click.prevent="expandNavigation()">expand all</button>
|
||||
<button class="w-1/2 ml-1 hover:bg-stone-700 hover:text-stone-50 border border-stone-200 px-2 py-1 transition duration-100" @click.prevent="collapseNavigation()">{{ $filters.translate('collapse all') }}</button>
|
||||
<button class="w-1/2 mr-1 hover:bg-stone-700 hover:text-stone-50 border border-stone-200 px-2 py-1 transition duration-100" @click.prevent="expandNavigation()">{{ $filters.translate('expand all') }}</button>
|
||||
</div>
|
||||
<div class="flex w-full my-px border-y border-stone-200 font-bold">
|
||||
<div class="border-l-4" :class="getStatusClass(home.status)"></div>
|
||||
<a :href="getUrl(home.urlRelWoF)" class="flex-grow p-1 hover:bg-teal-500 hover:text-stone-50" :class="getNaviClass(home.active)">
|
||||
{{ home.name }}
|
||||
<a :href="getUrl(home.urlRelWoF)" class="flex-grow p-1 pl-3 border-l-2 border-stone-50 hover:bg-teal-500 hover:text-stone-50" :class="home.active ? 'text-stone-50 bg-teal-500' : ''">
|
||||
{{ $filters.translate(home.name) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="pl-2 pl-3 pl-4 pl-6 pl-8 pl-9 pl-10 pl-12 pl-15 text-stone-50"></div>
|
||||
@@ -54,6 +54,7 @@ const navigation = Vue.createApp({
|
||||
if(item.originalName == 'home')
|
||||
{
|
||||
this.home = item;
|
||||
this.home.active = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -69,15 +70,6 @@ const navigation = Vue.createApp({
|
||||
return "border-yellow-400";
|
||||
}
|
||||
},
|
||||
getNaviClass(home)
|
||||
{
|
||||
if(home.active)
|
||||
{
|
||||
return "text-stone-50 bg-teal-500";
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
getUrl()
|
||||
{
|
||||
return tmaxios.defaults.baseURL + '/tm/content/' + data.settings.editor;
|
||||
@@ -167,7 +159,7 @@ navigation.component('navilevel',{
|
||||
<li :class="element.elementType" :id="element.keyPath" :data-url="element.urlRelWoF" :data-active="element.active">
|
||||
<div class="flex w-full my-px border-b border-stone-200 relative" :class="element.elementType == 'folder' ? 'font-bold' : ''">
|
||||
<div class="border-l-4" :class="getStatusClass(element.status)"></div>
|
||||
<a :href="getUrl(element.urlRelWoF)" class="flex-grow p-1 hover:bg-teal-500 hover:text-stone-50" :class="getNaviClass(element.active, element.activeParent, element.keyPathArray)">
|
||||
<a :href="getUrl(element.urlRelWoF)" class="flex-grow border-l-2 border-stone-50 p-1 hover:bg-teal-500 hover:text-stone-50" :class="getNaviClass(element.active, element.activeParent, element.keyPathArray)">
|
||||
{{ element.name }}
|
||||
</a>
|
||||
<div v-if="load == element.keyPath" class="p-1 absolute right-0">
|
||||
@@ -432,6 +424,4 @@ navigation.component('navilevel',{
|
||||
this.$emit("input", value);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
navigation.mount('#contentNavigation');
|
||||
});
|
@@ -16,12 +16,12 @@ const app = Vue.createApp({
|
||||
<div class="w-full p-8">
|
||||
<div class="w-full">
|
||||
<h2 class="text-xl font-bold mb-3">{{plugin.name}}</h2>
|
||||
<div class="text-xs my-3">author: <a :href="plugin.homepage" class="hover:underline text-teal-500">{{plugin.author}}</a> | version: {{plugin.version}}</div>
|
||||
<div class="text-xs my-3">{{ $filters.translate('author') }}: <a :href="plugin.homepage" class="hover:underline text-teal-500">{{plugin.author}}</a> | {{ $filters.translate('version') }}: {{plugin.version}}</div>
|
||||
<p>{{plugin.description}}</p>
|
||||
</div>
|
||||
<div class="w-full mt-6 flex justify-between">
|
||||
<button @click="setCurrent(pluginname)" class="flex-1 flex items-center justify-center space-x-4 p-3 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">
|
||||
<span>Configure</span>
|
||||
<span>{{ $filters.translate('Configure') }}</span>
|
||||
<span :class="(current == pluginname) ? 'border-b-8 border-b-white' : 'border-t-8 border-t-white'" class="h-0 w-0 border-x-8 border-x-transparent"></span>
|
||||
</button>
|
||||
<a v-if="!checkLicense(license, plugin.license)" href="https://typemill.net/buy" target="_blank" class="flex-1 ml-3 p-3 py-4 text-center bg-teal-500 hover:bg-teal-600 text-white cursor-pointer transition duration-100">Buy a license</a>
|
||||
@@ -55,9 +55,9 @@ const app = Vue.createApp({
|
||||
<div class="my-5">
|
||||
<div :class="messageClass" class="block w-full h-8 px-3 py-1 my-1 text-white transition duration-100">{{ message }}</div>
|
||||
<div class="w-full mt-6 flex justify-between">
|
||||
<button type="submit" @click.prevent="save()" class="flex-1 p-3 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">Save</button>
|
||||
<a v-if="!checkLicense(license, plugin.license)" href="https://typemill.net/buy" target="_blank" class="flex-1 ml-3 p-3 py-4 text-center bg-teal-500 hover:bg-teal-600 text-white cursor-pointer transition duration-100">Buy a license</a>
|
||||
<a v-else-if="plugin.paypal" :href="plugin.paypal" target="_blank" class="flex-1 ml-3 p-3 py-4 text-center bg-teal-500 hover:bg-teal-600 text-white cursor-pointer transition duration-100">Donate {{plugin.amount}},-</a>
|
||||
<button type="submit" @click.prevent="save()" class="flex-1 p-3 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">{{ $filters.translate('Save') }}</button>
|
||||
<a v-if="!checkLicense(license, plugin.license)" href="https://typemill.net/buy" target="_blank" class="flex-1 ml-3 p-3 py-4 text-center bg-teal-500 hover:bg-teal-600 text-white cursor-pointer transition duration-100">{{ $filters.translate('Buy a license') }}</a>
|
||||
<a v-else-if="plugin.paypal" :href="plugin.paypal" target="_blank" class="flex-1 ml-3 p-3 py-4 text-center bg-teal-500 hover:bg-teal-600 text-white cursor-pointer transition duration-100">{{ $filters.translate('Donate') }} {{plugin.amount}},-</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -66,13 +66,13 @@ const app = Vue.createApp({
|
||||
<div class="my-5 text-center">
|
||||
<modal v-if="showModal" @close="showModal = false">
|
||||
<template #header>
|
||||
<h3>License required</h3>
|
||||
<h3>{{ $filters.translate('License required') }}</h3>
|
||||
</template>
|
||||
<template #body>
|
||||
<p>{{ modalMessage }}</p>
|
||||
<p>{{ $filters.translate(modalMessage) }}</p>
|
||||
</template>
|
||||
<template #button>
|
||||
<a :href="getLinkToLicense()" class="focus:outline-none px-4 p-3 mr-3 text-white bg-teal-500 hover:bg-teal-700 transition duration-100">Check your license</a>
|
||||
<a :href="getLinkToLicense()" class="focus:outline-none px-4 p-3 mr-3 text-white bg-teal-500 hover:bg-teal-700 transition duration-100">{{ $filters.translate('Check your license') }}</a>
|
||||
</template>
|
||||
</modal>
|
||||
</div>
|
||||
|
@@ -5,30 +5,41 @@ const publisher = Vue.createApp({
|
||||
<div class="flex justify-between px-6 py-3">
|
||||
<div class="flex">
|
||||
<div class="border-l-4 w-32 px-2 py-2" :class="getStatusClass(item.status)">
|
||||
{{ item.status }}
|
||||
{{ $filters.translate(item.status) }}
|
||||
</div>
|
||||
<button
|
||||
v-if="raw"
|
||||
@click.prevent="saveDraft"
|
||||
:disabled="isPublished"
|
||||
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white bg-teal-500 hover:bg-teal-600 disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
|
||||
:disabled="nochanges"
|
||||
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
|
||||
:class="publishClass"
|
||||
>
|
||||
save draft
|
||||
{{ $filters.translate('draft') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="raw"
|
||||
@click.prevent="publishDraft"
|
||||
:disabled="nopublish"
|
||||
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
|
||||
:class="publishClass"
|
||||
>
|
||||
{{ $filters.translate('publish') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="visual"
|
||||
@click.prevent="publishArticle"
|
||||
:disabled="isPublished"
|
||||
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
|
||||
:class="publishClass"
|
||||
>
|
||||
publish
|
||||
{{ $filters.translate('publish') }}
|
||||
</button>
|
||||
<button
|
||||
@click.prevent="showModal = 'discard'"
|
||||
:disabled="!isModified"
|
||||
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white bg-yellow-500 hover:bg-yellow-600 disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
|
||||
>
|
||||
discard
|
||||
{{ $filters.translate('discard') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="item.originalName != 'home'"
|
||||
@@ -36,31 +47,31 @@ const publisher = Vue.createApp({
|
||||
:disabled="isUnpublished"
|
||||
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white bg-teal-500 hover:bg-teal-600 disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
|
||||
>
|
||||
unpublish
|
||||
{{ $filters.translate('unpublish') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="item.originalName != 'home'"
|
||||
@click.prevent="showModal = 'delete'"
|
||||
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 bg-stone-50 hover:bg-rose-500 hover:text-white transition"
|
||||
>
|
||||
delete
|
||||
{{ $filters.translate('delete') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a
|
||||
v-if="visual"
|
||||
href="{{ base_url }}/tm/content/raw{{ itemurl }}"
|
||||
:href="rawUrl"
|
||||
class="px-4 py-2 border border-stone-200 bg-stone-50 hover:bg-stone-700 hover:text-white transition ml-1"
|
||||
>
|
||||
raw editor
|
||||
{{ $filters.translate('raw') }}
|
||||
</a>
|
||||
<a
|
||||
v-if="raw"
|
||||
href="{{ base_url }}/tm/content/visual{{ itemurl }}"
|
||||
:href="visualUrl"
|
||||
class="px-4 py-2 border border-stone-200 bg-stone-50 hover:bg-stone-700 hover:text-white transition ml-1"
|
||||
@click="checkUnsafedContent(event)"
|
||||
>
|
||||
visual editor
|
||||
{{ $filters.translate('visual') }}
|
||||
</a>
|
||||
<a
|
||||
href="{{ item.urlAbs }}"
|
||||
@@ -76,39 +87,46 @@ const publisher = Vue.createApp({
|
||||
<transition name="fade">
|
||||
<modal v-if="showModal == 'discard'" @close="showModal = false">
|
||||
<template #header>
|
||||
<h3>Discard changes</h3>
|
||||
<h3>{{ $filters.translate('Discard changes') }}</h3>
|
||||
</template>
|
||||
<template #body>
|
||||
<p>Do you want to discard your changes and set the content back to the live version?</p>
|
||||
<p>{{ $filters.translate('Do you want to discard your changes and set the content back to the live version') }}?</p>
|
||||
</template>
|
||||
<template #button>
|
||||
<button @click="discardChanges" class="focus:outline-none px-4 p-3 mr-3 text-white bg-rose-500 hover:bg-rose-700 transition duration-100">Discard changes</button>
|
||||
<button @click="discardChanges" class="focus:outline-none px-4 p-3 mr-3 text-white bg-rose-500 hover:bg-rose-700 transition duration-100">{{ $filters.translate('Discard changes') }}</button>
|
||||
</template>
|
||||
</modal>
|
||||
</transition>
|
||||
<transition name="fade">
|
||||
<modal v-if="showModal == 'delete'" @close="showModal = false">
|
||||
<template #header>
|
||||
<h3>Delete page</h3>
|
||||
<h3>{{ $filters.translate('Delete page') }}</h3>
|
||||
</template>
|
||||
<template #body>
|
||||
<p>Do you really want to delete this page? Please confirm.</p>
|
||||
<p>
|
||||
{{ $filters.translate('Do you really want to delete this page') }}?
|
||||
{{ $filters.translate('Please confirm') }}.
|
||||
</p>
|
||||
</template>
|
||||
<template #button>
|
||||
<button @click="deleteArticle" class="focus:outline-none px-4 p-3 mr-3 text-white bg-rose-500 hover:bg-rose-700 transition duration-100">Delete page</button>
|
||||
<button @click="deleteArticle" class="focus:outline-none px-4 p-3 mr-3 text-white bg-rose-500 hover:bg-rose-700 transition duration-100">{{ $filters.translate('Delete page') }}</button>
|
||||
</template>
|
||||
</modal>
|
||||
</transition>
|
||||
<transition name="fade">
|
||||
<modal v-if="showModal == 'unpublish'" @close="showModal = false">
|
||||
<template #header>
|
||||
<h3>Unpublish page</h3>
|
||||
<h3>{{ $filters.translate('Unpublish page') }}</h3>
|
||||
</template>
|
||||
<template #body>
|
||||
<p>This page has been modified. If you unpublish the page, then we will delete the published version and keep the modified version. Please confirm.</p>
|
||||
<p>
|
||||
{{ $filters.translate('This page has been modified') }}.
|
||||
{{ $filters.translate('If you unpublish the page, then we will delete the published version and keep the modified version') }}.
|
||||
{{ $filters.translate('Please confirm') }}.
|
||||
</p>
|
||||
</template>
|
||||
<template #button>
|
||||
<button @click="unpublishArticle" class="focus:outline-none px-4 p-3 mr-3 text-white bg-rose-500 hover:bg-rose-700 transition duration-100">Unpublish page</button>
|
||||
<button @click="unpublishArticle" class="focus:outline-none px-4 p-3 mr-3 text-white bg-rose-500 hover:bg-rose-700 transition duration-100">{{ $filters.translate('Unpublish page') }}</button>
|
||||
</template>
|
||||
</modal>
|
||||
</transition>
|
||||
@@ -121,6 +139,7 @@ const publisher = Vue.createApp({
|
||||
showModal: false,
|
||||
message: false,
|
||||
messageClass: '',
|
||||
nochanges: true,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@@ -147,6 +166,10 @@ const publisher = Vue.createApp({
|
||||
|
||||
eventBus.$on('publisherclear', this.clearPublisher);
|
||||
|
||||
eventBus.$on('editdraft', this.markChanges);
|
||||
|
||||
eventBus.$on('cleardraft', this.unmarkChanges);
|
||||
|
||||
},
|
||||
computed: {
|
||||
isPublished()
|
||||
@@ -167,10 +190,31 @@ const publisher = Vue.createApp({
|
||||
{
|
||||
return 'bg-teal-500 hover:bg-teal-600';
|
||||
}
|
||||
if(this.item.status == 'modified')
|
||||
else
|
||||
{
|
||||
return 'bg-yellow-500 hover:bg-yellow-600';
|
||||
}
|
||||
/*
|
||||
if(this.item.status == 'modified')
|
||||
{
|
||||
return 'bg-yellow-500 hover:bg-yellow-600';
|
||||
}*/
|
||||
},
|
||||
nopublish()
|
||||
{
|
||||
if(this.item.status != 'published')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return this.nochanges;
|
||||
},
|
||||
rawUrl()
|
||||
{
|
||||
return data.urlinfo.baseurl + '/tm/content/raw' + this.item.urlRelWoF;
|
||||
},
|
||||
visualUrl()
|
||||
{
|
||||
return data.urlinfo.baseurl + '/tm/content/visual' + this.item.urlRelWoF;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -180,6 +224,14 @@ const publisher = Vue.createApp({
|
||||
this.messageClass = false;
|
||||
this.showModal = false;
|
||||
},
|
||||
markChanges()
|
||||
{
|
||||
this.nochanges = false;
|
||||
},
|
||||
unmarkChanges()
|
||||
{
|
||||
this.nochanges = true;
|
||||
},
|
||||
getStatusClass(status)
|
||||
{
|
||||
if(status == 'published')
|
||||
@@ -280,20 +332,11 @@ const publisher = Vue.createApp({
|
||||
},
|
||||
saveDraft()
|
||||
{
|
||||
var self = this;
|
||||
tmaxios.put('/api/v1/article',{
|
||||
|
||||
})
|
||||
.then(function (response) {
|
||||
self.draftResult = 'success';
|
||||
navi.getNavi();
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
self.draftDisabled = false;
|
||||
self.draftResult = 'fail';
|
||||
self.handleErrors(error);
|
||||
});
|
||||
eventBus.$emit('savedraft');
|
||||
},
|
||||
publishDraft()
|
||||
{
|
||||
eventBus.$emit('publishdraft');
|
||||
},
|
||||
deleteArticle()
|
||||
{
|
||||
|
145
system/typemill/author/js/vue-raw.js
Normal file
145
system/typemill/author/js/vue-raw.js
Normal file
@@ -0,0 +1,145 @@
|
||||
const raweditor = Vue.createApp({
|
||||
template: `
|
||||
<fieldset>
|
||||
<div class="w-full px-6 py-3" :class="{'error' : errors.title}">
|
||||
<label class="block mb-1 font-medium" for="title">{{ $filters.translate('Title') }}*</label>
|
||||
<input
|
||||
name="title"
|
||||
type="text"
|
||||
class="w-full p-4 text-white bg-stone-700 text-3xl"
|
||||
v-model="title"
|
||||
@input="updateTitle"
|
||||
required
|
||||
/>
|
||||
<span class="error" v-if="errors.title">{{ errors.title }}</span>
|
||||
</div>
|
||||
<div class="w-full plain mt-5 mb-5 px-6 py-3">
|
||||
<label for="raweditor" class="block mb-1 font-medium">{{ $filters.translate('Markdown') }}</label>
|
||||
<div class="codearea">
|
||||
<textarea
|
||||
name="raweditor"
|
||||
data-el="editor"
|
||||
class="editor"
|
||||
ref="raweditor"
|
||||
v-model="content"
|
||||
@input="updateBody"
|
||||
>
|
||||
</textarea>
|
||||
<pre aria-hidden="true" class="highlight hljs"><code data-el="highlight" v-html="highlighted"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
title: 'loading',
|
||||
content: 'loading',
|
||||
item: data.item,
|
||||
highlighted: '',
|
||||
errors: false,
|
||||
freeze: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initializeContent(data.content)
|
||||
|
||||
eventBus.$on('savedraft', this.saveDraft);
|
||||
|
||||
eventBus.$on('publishdraft', this.publishDraft);
|
||||
|
||||
eventBus.$on('content', content => {
|
||||
this.initializeContent(content);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
initializeContent(contentArray)
|
||||
{
|
||||
let markdown = '';
|
||||
let title = contentArray.shift();
|
||||
|
||||
for(item in contentArray)
|
||||
{
|
||||
markdown += contentArray[item].markdown + '\n\n';
|
||||
}
|
||||
this.title = title.markdown;
|
||||
this.content = markdown;
|
||||
|
||||
this.highlight(this.content);
|
||||
this.resizeCodearea();
|
||||
},
|
||||
updateTitle()
|
||||
{
|
||||
eventBus.$emit('editdraft');
|
||||
},
|
||||
updateBody()
|
||||
{
|
||||
this.highlight(this.content);
|
||||
this.resizeCodearea();
|
||||
eventBus.$emit('editdraft');
|
||||
},
|
||||
resizeCodearea()
|
||||
{
|
||||
let codeeditor = this.$refs["raweditor"];
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
codeeditor.style.height = '200px';
|
||||
if (codeeditor.scrollHeight > 200)
|
||||
{
|
||||
codeeditor.style.height = `${codeeditor.scrollHeight + 2}px`;
|
||||
}
|
||||
});
|
||||
},
|
||||
highlight(code)
|
||||
{
|
||||
if(code === undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
highlighted = hljs.highlightAuto(code, ['markdown']).value;
|
||||
this.highlighted = highlighted;
|
||||
});
|
||||
},
|
||||
saveDraft()
|
||||
{
|
||||
var self = this;
|
||||
tmaxios.put('/api/v1/draft',{
|
||||
'url': data.urlinfo.route,
|
||||
'item_id': this.item.keyPath,
|
||||
'title': this.title,
|
||||
'body': this.content
|
||||
})
|
||||
.then(function (response) {
|
||||
self.item = response.data.item;
|
||||
eventBus.$emit('cleardraft');
|
||||
eventBus.$emit('item', response.data.item);
|
||||
eventBus.$emit('navigation', response.data.navigation);
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
|
||||
});
|
||||
},
|
||||
publishDraft()
|
||||
{
|
||||
var self = this;
|
||||
tmaxios.post('/api/v1/draft/publish',{
|
||||
'url': data.urlinfo.route,
|
||||
'item_id': this.item.keyPath,
|
||||
'title': this.title,
|
||||
'body': this.content
|
||||
})
|
||||
.then(function (response) {
|
||||
self.item = response.data.item;
|
||||
eventBus.$emit('cleardraft');
|
||||
eventBus.$emit('item', response.data.item);
|
||||
eventBus.$emit('navigation', response.data.navigation);
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
|
||||
});
|
||||
},
|
||||
},
|
||||
})
|
@@ -35,7 +35,7 @@ const modal = {
|
||||
const translatefilter = {
|
||||
translate(value)
|
||||
{
|
||||
if(typeof data.labels === 'undefined') return;
|
||||
if(typeof data.labels === 'undefined') return value;
|
||||
if (!value) return '';
|
||||
|
||||
translation_key = value.replace(/[ ]/g,"_").replace(/[.]/g, "_").replace(/[,]/g, "_").replace(/[-]/g, "_").replace(/[,]/g,"_").toUpperCase();
|
||||
|
@@ -3,7 +3,7 @@ const app = Vue.createApp({
|
||||
<form class="inline-block w-full">
|
||||
<ul class="flex mt-4 mb-4">
|
||||
<li v-for="tab in tabs" class="">
|
||||
<button class="px-2 py-2 border-b-2 border-stone-200 hover:border-b-4 hover:bg-stone-200 hover:border-stone-700 transition duration-100" :class="(tab == currentTab) ? 'border-b-4 border-stone-700 bg-stone-200' : ''" @click.prevent="activateTab(tab)">{{tab}}</button>
|
||||
<button class="px-2 py-2 border-b-2 border-stone-200 hover:border-b-4 hover:bg-stone-200 hover:border-stone-700 transition duration-100" :class="(tab == currentTab) ? 'border-b-4 border-stone-700 bg-stone-200' : ''" @click.prevent="activateTab(tab)">{{ $filters.translate(tab) }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-for="(fieldDefinition, fieldname) in formDefinitions">
|
||||
@@ -20,7 +20,7 @@ const app = Vue.createApp({
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="my-5">
|
||||
<div :class="messageClass" class="block w-full h-8 px-3 py-1 my-1 text-white transition duration-100">{{ message }}</div>
|
||||
<div :class="messageClass" class="block w-full h-8 px-3 py-1 my-1 text-white transition duration-100">{{ $filters.translate(message) }}</div>
|
||||
<input type="submit" @click.prevent="save()" value="save" class="w-full p-3 my-1 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">
|
||||
</div>
|
||||
</form>
|
||||
|
@@ -71,13 +71,13 @@ const app = Vue.createApp({
|
||||
<div class="my-5 text-center">
|
||||
<modal v-if="showModal" @close="showModal = false">
|
||||
<template #header>
|
||||
<h3>License required</h3>
|
||||
<h3>{{ $filters.translate('License required') }}</h3>
|
||||
</template>
|
||||
<template #body>
|
||||
<p>{{ modalMessage }}</p>
|
||||
<p>{{ $filters.translate(modalMessage) }}</p>
|
||||
</template>
|
||||
<template #button>
|
||||
<a :href="getLinkToLicense()" class="focus:outline-none px-4 p-3 mr-3 text-white bg-teal-500 hover:bg-teal-700 transition duration-100">Check your license</a>
|
||||
<a :href="getLinkToLicense()" class="focus:outline-none px-4 p-3 mr-3 text-white bg-teal-500 hover:bg-teal-700 transition duration-100">{{ $filters.translate('Check your license') }}</a>
|
||||
</template>
|
||||
</modal>
|
||||
</div>
|
||||
|
@@ -4,7 +4,7 @@ const app = Vue.createApp({
|
||||
<form class="w-full my-8">
|
||||
<div v-for="(fieldDefinition, fieldname) in formDefinitions">
|
||||
<fieldset class="flex flex-wrap justify-between border-2 border-stone-200 p-4 my-8" v-if="fieldDefinition.type == 'fieldset'">
|
||||
<legend class="text-lg font-medium">{{ fieldDefinition.legend }}</legend>
|
||||
<legend class="text-lg font-medium">{{ $filters.translate(fieldDefinition.legend) }}</legend>
|
||||
<component v-for="(subfieldDefinition, subfieldname) in fieldDefinition.fields"
|
||||
:key="subfieldname"
|
||||
:is="selectComponent(subfieldDefinition.type)"
|
||||
@@ -26,7 +26,7 @@ const app = Vue.createApp({
|
||||
</component>
|
||||
</div>
|
||||
<div class="my-5">
|
||||
<div :class="messageClass" class="block w-full h-8 px-3 py-1 my-1 text-white transition duration-100">{{ message }}</div>
|
||||
<div :class="messageClass" class="block w-full h-8 px-3 py-1 my-1 text-white transition duration-100">{{ $filters.translate(message) }}</div>
|
||||
<button type="submit" @click.prevent="save()" class="w-full p-3 my-1 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">{{ $filters.translate('Save') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -37,7 +37,7 @@ const app = Vue.createApp({
|
||||
<h3>{{ $filters.translate('Delete user') }}</h3>
|
||||
</template>
|
||||
<template #body>
|
||||
<p>Do you really want to delete this user?</p>
|
||||
<p>{{ $filters.translate('Do you really want to delete this user') }}?</p>
|
||||
</template>
|
||||
<template #button>
|
||||
<button @click="deleteuser()" class="focus:outline-none px-4 p-3 mr-3 text-white bg-rose-500 hover:bg-rose-700 transition duration-100">{{ $filters.translate('delete user') }}</button>
|
||||
|
@@ -35,8 +35,8 @@ const app = Vue.createApp({
|
||||
</component>
|
||||
</div>
|
||||
<div class="my-5">
|
||||
<div :class="messageClass" class="block w-full h-8 px-3 py-1 my-1 text-white transition duration-100">{{ message }}</div>
|
||||
<button type="submit" @click.prevent="save()" class="w-full p-3 my-1 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">Save</button>
|
||||
<div :class="messageClass" class="block w-full h-8 px-3 py-1 my-1 text-white transition duration-100">{{ $filters.translate(message) }}</div>
|
||||
<button type="submit" @click.prevent="save()" class="w-full p-3 my-1 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">{{ $filters.translate('Save') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@@ -173,9 +173,9 @@ app.component('searchbox', {
|
||||
},
|
||||
template: `<div>
|
||||
<div>
|
||||
<button @click.prevent="setFilter('username')" :class="checkActive('username')" class="px-2 py-2 border-b-4 hover:bg-stone-200 hover:border-stone-700 transition duration-100">username</button>
|
||||
<button @click.prevent="setFilter('userrole')" :class="checkActive('userrole')" class="px-2 py-2 border-b-4 hover:bg-stone-200 hover:border-stone-700 transition duration-100">userrole</button>
|
||||
<button @click.prevent="setFilter('usermail')" :class="checkActive('usermail')" class="px-2 py-2 border-b-4 hover:bg-stone-200 hover:border-stone-700 transition duration-100">e-mail</button>
|
||||
<button @click.prevent="setFilter('username')" :class="checkActive('username')" class="px-2 py-2 border-b-4 hover:bg-stone-200 hover:border-stone-700 transition duration-100">{{ $filters.translate('username') }}</button>
|
||||
<button @click.prevent="setFilter('userrole')" :class="checkActive('userrole')" class="px-2 py-2 border-b-4 hover:bg-stone-200 hover:border-stone-700 transition duration-100">{{ $filters.translate('userrole') }}</button>
|
||||
<button @click.prevent="setFilter('usermail')" :class="checkActive('usermail')" class="px-2 py-2 border-b-4 hover:bg-stone-200 hover:border-stone-700 transition duration-100">{{ $filters.translate('e-mail') }}</button>
|
||||
</div>
|
||||
<div class="w-100 flex">
|
||||
<select v-if="this.filter == 'userrole'" v-model="searchterm" class="w-3/4 h-12 px-2 py-3 border border-stone-300 bg-stone-200">
|
||||
@@ -183,12 +183,12 @@ app.component('searchbox', {
|
||||
</select>
|
||||
<input v-else type="text" v-model="searchterm" class="w-3/4 h-12 px-2 py-3 border border-stone-300 bg-stone-200">
|
||||
<div class="w-1/4 flex justify-around">
|
||||
<button class="w-half bg-stone-200 hover:bg-stone-100" @click.prevent="clearSearch()">Clear</button>
|
||||
<button class="w-half bg-stone-700 hover:bg-stone-900 text-white" @click.prevent="startSearch()">Search</button>
|
||||
<button class="w-half bg-stone-200 hover:bg-stone-100" @click.prevent="clearSearch()">{{ $filters.translate('Clear') }}</button>
|
||||
<button class="w-half bg-stone-700 hover:bg-stone-900 text-white" @click.prevent="startSearch()">{{ $filters.translate('Search') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="error" class="error pt1 f6">{{error}}</div>
|
||||
<div v-if="this.filter == \'usermail\'" class="text-xs">You can use the asterisk (*) wildcard to search for name@* or *@domain.com.</div>
|
||||
<div v-if="this.filter == \'usermail\'" class="text-xs">{{ $filters.translate('You can use the asterisk (*) wildcard to search for name@* or *@domain.com') }}.</div>
|
||||
</div>`,
|
||||
methods: {
|
||||
startSearch: function()
|
||||
@@ -235,22 +235,22 @@ app.component('usertable', {
|
||||
props: ['userdata'],
|
||||
template: `<table class="w-full mt-8" cellspacing="0">
|
||||
<tr>
|
||||
<th class="p-3 bg-stone-200 border-2 border-stone-50">Username</th>
|
||||
<th class="p-3 bg-stone-200 border-2 border-stone-50">Userrole</th>
|
||||
<th class="p-3 bg-stone-200 border-2 border-stone-50">E-Mail</th>
|
||||
<th class="p-3 bg-stone-200 border-2 border-stone-50">Edit</th>
|
||||
<th class="p-3 bg-stone-200 border-2 border-stone-50">{{ $filters.translate('Username') }}</th>
|
||||
<th class="p-3 bg-stone-200 border-2 border-stone-50">{{ $filters.translate('Userrole') }}</th>
|
||||
<th class="p-3 bg-stone-200 border-2 border-stone-50">{{ $filters.translate('E-Mail') }}</th>
|
||||
<th class="p-3 bg-stone-200 border-2 border-stone-50">{{ $filters.translate('Edit') }}</th>
|
||||
</tr>
|
||||
<tr v-for="user,index in userdata" key="username">
|
||||
<td class="p-3 bg-stone-100 border-2 border-white">{{ user.username }}</td>
|
||||
<td class="p-3 bg-stone-100 border-2 border-white">{{ user.userrole }}</td>
|
||||
<td class="p-3 bg-stone-100 border-2 border-white">{{ user.email }}</td>
|
||||
<td class="bg-stone-100 border-2 border-white text-center hover:bg-cyan-500 hover:text-white pointer transition duration-100"><a :href="getEditLink(user.username)" class="block w-full p-3">edit</a></td>
|
||||
<td class="bg-stone-100 border-2 border-white text-center hover:bg-teal-500 hover:text-white pointer transition duration-100"><a :href="getEditLink(user.username)" class="block w-full p-3">{{ $filters.translate('edit') }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="p-3 bg-stone-100 border-2 border-white"><a class="text-cyan-500 hover:underline" :href="getNewUserLink()">New user</a></td>
|
||||
<td class="p-3 bg-stone-100 border-2 border-white"><a class="text-teal-500 hover:underline" :href="getNewUserLink()">{{ $filters.translate('New user') }}</a></td>
|
||||
<td class="p-3 bg-stone-100 border-2 border-white"></td>
|
||||
<td class="p-3 bg-stone-100 border-2 border-white"></td>
|
||||
<td class="bg-stone-100 border-2 border-white text-center text-cyan-500 hover:bg-cyan-500 hover:text-white transition duration-100"><a class="block w-full p-3" :href="getNewUserLink()">add</a></td>
|
||||
<td class="bg-stone-100 border-2 border-white text-center text-teal-500 hover:bg-teal-500 hover:text-white transition duration-100"><a class="block w-full p-3" :href="getNewUserLink()">{{ $filters.translate('add') }}</a></td>
|
||||
</tr>
|
||||
</table>`,
|
||||
methods: {
|
||||
|
@@ -48,7 +48,6 @@
|
||||
<script>
|
||||
|
||||
const data = {{ jsdata | json_encode() | raw }};
|
||||
const labels = {{ translations|json_encode() }};
|
||||
|
||||
</script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/autosize.min.js?v={{ settings.version }}"></script>
|
||||
@@ -64,7 +63,10 @@
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/sortable.min.js?v={{ settings.version }}"></script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vuedraggable.umd.min.js?v={{ settings.version }}"></script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue-contentnavi.js?v={{ settings.version }}"></script>
|
||||
|
||||
<script>
|
||||
navigation.config.globalProperties.$filters = translatefilter;
|
||||
navigation.mount('#contentNavigation');
|
||||
</script>
|
||||
{% block javascript %}{% endblock %}
|
||||
|
||||
{{ assets.renderJS() }}
|
||||
|
@@ -52,8 +52,7 @@
|
||||
<script>
|
||||
|
||||
const data = {{ jsdata | json_encode() | raw }};
|
||||
const labels = {{ translations|json_encode() }};
|
||||
|
||||
|
||||
</script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/axios.min.js?v={{ settings.version }}"></script>
|
||||
<script>
|
||||
@@ -67,7 +66,7 @@
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue-eventbus.js?v={{ settings.version }}"></script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue-shared.js?v={{ settings.version }}"></script>
|
||||
<script>
|
||||
kixote.mount('#kixote');
|
||||
/* kixote.mount('#kixote'); */
|
||||
</script>
|
||||
{% block javascript %}{% endblock %}
|
||||
|
||||
|
@@ -55,6 +55,8 @@ $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) {
|
||||
# ARTICLE
|
||||
$group->post('/article/sort', ControllerApiAuthorArticle::class . ':sortArticle')->setName('api.article.sort')->add(new ApiAuthorization($acl, 'content', 'create')); # author
|
||||
$group->post('/article', ControllerApiAuthorArticle::class . ':createArticle')->setName('api.article.create')->add(new ApiAuthorization($acl, 'content', 'create')); # author
|
||||
$group->put('/draft', ControllerApiAuthorArticle::class . ':updateDraft')->setName('api.draft.update')->add(new ApiAuthorization($acl, 'content', 'create')); # author
|
||||
$group->post('/draft/publish', ControllerApiAuthorArticle::class . ':publishDraft')->setName('api.draft.publish')->add(new ApiAuthorization($acl, 'content', 'create')); # author
|
||||
$group->post('/article/publish', ControllerApiAuthorArticle::class . ':publishArticle')->setName('api.article.publish')->add(new ApiAuthorization($acl, 'content', 'publish'));
|
||||
$group->delete('/article/unpublish', ControllerApiAuthorArticle::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new ApiAuthorization($acl, 'content', 'unpublish'));
|
||||
$group->delete('/article/discard', ControllerApiAuthorArticle::class . ':discardArticleChanges')->setName('api.article.discard')->add(new ApiAuthorization($acl, 'content', 'edit'));
|
||||
|
@@ -34,6 +34,7 @@ $app->group('/tm', function (RouteCollectorProxy $group) use ($routeParser,$acl)
|
||||
|
||||
# Author Area
|
||||
$group->get('/content/visual[/{route:.*}]', ControllerWebAuthor::class . ':showBlox')->setName('content.visual')->add(new WebAuthorization($routeParser, $acl, 'mycontent', 'view'));
|
||||
$group->get('/content/raw[/{route:.*}]', ControllerWebAuthor::class . ':showRaw')->setName('content.raw')->add(new WebAuthorization($routeParser, $acl, 'mycontent', 'view'));
|
||||
|
||||
})->add(new WebRedirectIfUnauthenticated($routeParser));
|
||||
|
||||
|
Reference in New Issue
Block a user