mirror of
https://github.com/typemill/typemill.git
synced 2025-08-05 05:37:45 +02:00
Navigation nearly ready
This commit is contained in:
335
system/typemill/Controllers/ControllerApiAuthorArticle.php
Normal file
335
system/typemill/Controllers/ControllerApiAuthorArticle.php
Normal file
@@ -0,0 +1,335 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Slim\Routing\RouteContext;
|
||||
|
||||
use Typemill\Models\Navigation;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\StorageWrapper;
|
||||
|
||||
class ControllerApiAuthorArticle extends Controller
|
||||
{
|
||||
public function sortArticle(Request $request, Response $response, $args)
|
||||
{
|
||||
$params = $request->getParsedBody();
|
||||
|
||||
# input validation
|
||||
$validate = new Validation();
|
||||
$result = $validate->navigationSort($params);
|
||||
if(!$result)
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'Data not valid. Please refresh the page and try again.',
|
||||
'errors' => $result
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
|
||||
}
|
||||
|
||||
# set variables
|
||||
$itemKeyPath = explode('.', $params['item_id']);
|
||||
$parentKeyFrom = explode('.', $params['parent_id_from']);
|
||||
$parentKeyTo = explode('.', $params['parent_id_to']);
|
||||
$urlinfo = $this->c->get('urlinfo');
|
||||
$langattr = $this->settings['langattr'];
|
||||
|
||||
# get navigation
|
||||
$navigation = new Navigation();
|
||||
$draftNavigation = $navigation->getDraftNavigation($urlinfo, $langattr);
|
||||
|
||||
# validate user rights
|
||||
$acl = $this->c->get('acl');
|
||||
|
||||
# if user has no right to update content from others (eg admin or editor)
|
||||
if(!$acl->isAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
|
||||
{
|
||||
# check ownership. This code should nearly never run, because there is no button/interface to trigger it.
|
||||
if(!$this->checkContentOwnership())
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'You are not allowed to move that content.',
|
||||
'navigation' => $draftNavigation
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
|
||||
}
|
||||
}
|
||||
|
||||
$item = $navigation->getItemWithKeyPath($draftNavigation, $itemKeyPath);
|
||||
|
||||
$extendedNavigation = $navigation->getExtendedNavigation($urlinfo, $langattr);
|
||||
|
||||
$pageinfo = $extendedNavigation[$params['url']] ?? false;
|
||||
|
||||
if(!$pageinfo)
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'page not found',
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
|
||||
}
|
||||
|
||||
# if an item is moved to the first level
|
||||
if($parentKeyTo == '')
|
||||
{
|
||||
# create empty and default values so that the logic below still works
|
||||
$newFolder = new \stdClass();
|
||||
$newFolder->path = '';
|
||||
$folderContent = $draftNavigation;
|
||||
}
|
||||
else
|
||||
{
|
||||
# get the target folder from navigation
|
||||
$newFolder = $navigation->getItemWithKeyPath($draftNavigation, $parentKeyTo);
|
||||
|
||||
# get the content of the target folder
|
||||
$folderContent = $newFolder->folderContent;
|
||||
}
|
||||
|
||||
# if the item has been moved within the same folder
|
||||
if($parentKeyFrom == $parentKeyTo)
|
||||
{
|
||||
# no need to ping search engines
|
||||
$ping = false;
|
||||
|
||||
# get key of item
|
||||
$itemKey = end($itemKeyPath);
|
||||
reset($itemKeyPath);
|
||||
|
||||
# delete item from folderContent
|
||||
unset($folderContent[$itemKey]);
|
||||
}
|
||||
else
|
||||
{
|
||||
# let us ping search engines
|
||||
$ping = true;
|
||||
|
||||
# rename links in extended file
|
||||
#$navigation->renameExtended($item, $newFolder);
|
||||
|
||||
# an active file has been moved to another folder, so send new url with response
|
||||
if($params['active'] == 'active')
|
||||
{
|
||||
$url = $urlinfo['baseurl'] . '/tm/content/' . $this->settings['editor'] . $newFolder->urlRelWoF . '/' . $item->slug;
|
||||
}
|
||||
}
|
||||
|
||||
# add item to newFolder
|
||||
array_splice($folderContent, $params['index_new'], 0, array($item));
|
||||
|
||||
# move and rename files in the new folder
|
||||
$index = 0;
|
||||
$writeError = false;
|
||||
$storage = new StorageWrapper('\Typemill\Models\Storage');
|
||||
foreach($folderContent as $folderItem)
|
||||
{
|
||||
if(!$storage->moveFile($folderItem, $newFolder->path, $index))
|
||||
{
|
||||
$writeError = true;
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
if($writeError)
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.',
|
||||
'navigation' => $draftNavigation,
|
||||
'url' => false
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
|
||||
}
|
||||
|
||||
# if everything worked, we have to recreate the navigation
|
||||
$navigation->clearNavigation();
|
||||
|
||||
/*
|
||||
# get item for url and set it active again
|
||||
if(isset($this->params['url']))
|
||||
{
|
||||
$activeItem = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl());
|
||||
}
|
||||
|
||||
# update the structure for website
|
||||
$this->setFreshStructureLive();
|
||||
|
||||
# update the navigation
|
||||
$this->setFreshNavigation();
|
||||
|
||||
# update the sitemap
|
||||
$this->updateSitemap($ping);
|
||||
|
||||
# dispatch event
|
||||
$this->c->dispatcher->dispatch('onPageSorted', new OnPageSorted($this->params));
|
||||
*/
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'navigation' => $navigation->getDraftNavigation($urlinfo, $langattr),
|
||||
'message' => '',
|
||||
'url' => false
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
public function createArticle(Request $request, Response $response, $args)
|
||||
{
|
||||
# validate user rights
|
||||
$acl = $this->c->get('acl');
|
||||
|
||||
# if user has no right to update content from others (eg admin or editor)
|
||||
if(!$acl->isAllowed($request->getAttribute('c_userrole'), 'mycontent', 'create'))
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'You are not allowed to create content.',
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
|
||||
}
|
||||
|
||||
$params = $request->getParsedBody();
|
||||
|
||||
# input validation
|
||||
$validate = new Validation();
|
||||
$result = $validate->validateNaviItem($params);
|
||||
if(!$result)
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'Input not valid.',
|
||||
'errors' => $result
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
|
||||
}
|
||||
|
||||
# set variables
|
||||
$urlinfo = $this->c->get('urlinfo');
|
||||
$langattr = $this->settings['langattr'];
|
||||
|
||||
# get navigation
|
||||
$navigation = new Navigation();
|
||||
$draftNavigation = $navigation->getDraftNavigation($urlinfo, $langattr);
|
||||
|
||||
if($params['folder_id'] == 'root')
|
||||
{
|
||||
$folderContent = $draftNavigation;
|
||||
}
|
||||
else
|
||||
{
|
||||
# get the ids (key path) for item, old folder and new folder
|
||||
$folderKeyPath = explode('.', $params['folder_id']);
|
||||
|
||||
# get the item from structure
|
||||
$folder = $navigation->getItemWithKeyPath($draftNavigation, $folderKeyPath);
|
||||
|
||||
if(!$folder)
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'We could not find this page. Please refresh and try again.'
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
|
||||
}
|
||||
|
||||
$folderContent = $folder->folderContent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$name = $params['item_name'];
|
||||
$slug = Folder::createSlug($this->params['item_name'], $this->settings);
|
||||
|
||||
# initialize index
|
||||
$index = 0;
|
||||
|
||||
# iterate through the whole content of the new folder
|
||||
$writeError = false;
|
||||
$folderPath = isset($folder) ? $folder->path : '';
|
||||
|
||||
foreach($folderContent as $folderItem)
|
||||
{
|
||||
# check, if the same name as new item, then return an error
|
||||
if($folderItem->slug == $slug)
|
||||
{
|
||||
return $response->withJson(array('navigation' => $draftNavigation, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404);
|
||||
}
|
||||
|
||||
if(!$writeYaml->moveElement($folderItem, $folderPath, $index))
|
||||
{
|
||||
$writeError = true;
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
|
||||
if($writeError)
|
||||
{
|
||||
return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# add prefix number to the name
|
||||
$namePath = $index > 9 ? $index . '-' . $slug : '0' . $index . '-' . $slug;
|
||||
$folderPath = 'content' . $folder->path;
|
||||
|
||||
# create default content
|
||||
$content = json_encode(['# ' . $name, 'Content']);
|
||||
|
||||
if($this->params['type'] == 'file')
|
||||
{
|
||||
if(!$writeYaml->writeFile($folderPath, $namePath . '.txt', $content))
|
||||
{
|
||||
return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
|
||||
}
|
||||
}
|
||||
elseif($this->params['type'] == 'folder')
|
||||
{
|
||||
if(!$writeYaml->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath))
|
||||
{
|
||||
return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
|
||||
}
|
||||
$this->writeCache->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content);
|
||||
|
||||
# always redirect to a folder
|
||||
$url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $folder->urlRelWoF . '/' . $slug;
|
||||
|
||||
}
|
||||
|
||||
# get extended structure
|
||||
$extended = $writeYaml->getYaml('cache', 'structure-extended.yaml');
|
||||
|
||||
# create the url for the item
|
||||
$urlWoF = $folder->urlRelWoF . '/' . $slug;
|
||||
# $urlWoF = '/' . $slug;
|
||||
|
||||
# add the navigation name to the item htmlspecialchars needed for french language
|
||||
$extended[$urlWoF] = ['hide' => false, 'navtitle' => $name];
|
||||
|
||||
# store the extended structure
|
||||
$writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended);
|
||||
|
||||
# update the structure for editor
|
||||
$this->setFreshStructureDraft();
|
||||
|
||||
# get item for url and set it active again
|
||||
if(isset($this->params['url']))
|
||||
{
|
||||
$activeItem = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl());
|
||||
}
|
||||
|
||||
# activate this if you want to redirect after creating the page...
|
||||
# $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $folder->urlRelWoF . '/' . $slug;
|
||||
|
||||
return $response->withJson(array('data' => $this->structureDraft, 'errors' => false, 'url' => $url));
|
||||
}
|
||||
}
|
@@ -40,6 +40,35 @@ class Navigation
|
||||
$this->extendedNaviName = 'navi-extended.txt';
|
||||
}
|
||||
|
||||
public function clearNavigation(array $deleteitems = NULL )
|
||||
{
|
||||
# clear cache
|
||||
$this->extendedNavigation = false;
|
||||
$this->draftNavigation = false;
|
||||
$this->basicDraftNavigation = false;
|
||||
$this->liveNavigation = false;
|
||||
$this->basicLiveNavigation = false;
|
||||
|
||||
# clear files
|
||||
$navifiles = [
|
||||
'extended' => $this->extendedNaviName,
|
||||
'draft' => $this->draftNaviName,
|
||||
'live' => $this->liveNaviName
|
||||
];
|
||||
|
||||
if($deleteitems)
|
||||
{
|
||||
$navifiles = array_intersect_key($navifiles, $deleteitems);
|
||||
}
|
||||
|
||||
foreach($navifiles as $navifile)
|
||||
{
|
||||
$result = $this->storage->deleteFile($this->naviFolder, $navifile);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getMainNavigation($userrole, $acl, $urlinfo, $editor)
|
||||
{
|
||||
$mainnavi = $this->storage->getYaml('system/typemill/settings', 'mainnavi.yaml');
|
||||
@@ -76,7 +105,6 @@ class Navigation
|
||||
return $allowedmainnavi;
|
||||
}
|
||||
|
||||
|
||||
# get the navigation with draft files for author environment
|
||||
public function getDraftNavigation($urlinfo, $language, $userrole = null, $username = null)
|
||||
{
|
||||
|
@@ -192,9 +192,52 @@ class Storage
|
||||
return false;
|
||||
}
|
||||
|
||||
public function moveFile()
|
||||
# used to sort the navigation / files
|
||||
public function moveFile($item, $folderPath, $index, $date = null)
|
||||
{
|
||||
$filetypes = array('md', 'txt', 'yaml');
|
||||
|
||||
# set new order as string
|
||||
$newOrder = ($index < 10) ? '0' . $index : $index;
|
||||
|
||||
$newPath = $this->contentFolder . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . $item->slug;
|
||||
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
$oldPath = $this->basePath . 'content' . $item->path;
|
||||
if(@rename($oldPath, $newPath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
# create old path but without filetype
|
||||
$oldPath = substr($item->path, 0, strpos($item->path, "."));
|
||||
$oldPath = $this->contentFolder . $oldPath;
|
||||
|
||||
$result = true;
|
||||
|
||||
foreach($filetypes as $filetype)
|
||||
{
|
||||
$oldFilePath = $oldPath . '.' . $filetype;
|
||||
$newFilePath = $newPath . '.' . $filetype;
|
||||
|
||||
#check if file with filetype exists and rename
|
||||
if($oldFilePath != $newFilePath && file_exists($oldFilePath))
|
||||
{
|
||||
if(@rename($oldFilePath, $newFilePath))
|
||||
{
|
||||
$result = $result;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -498,54 +541,5 @@ class Storage
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function moveElement($item, $folderPath, $index, $date = null)
|
||||
{
|
||||
$filetypes = array('md', 'txt', 'yaml');
|
||||
|
||||
# set new order as string
|
||||
$newOrder = ($index < 10) ? '0' . $index : $index;
|
||||
|
||||
# create new path with foldername or filename but without file-type
|
||||
# $newPath = $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . str_replace(" ", "-", $item->name);
|
||||
|
||||
$newPath = $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . $item->slug;
|
||||
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
$oldPath = $this->basePath . 'content' . $item->path;
|
||||
if(@rename($oldPath, $newPath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
# create old path but without filetype
|
||||
$oldPath = substr($item->path, 0, strpos($item->path, "."));
|
||||
$oldPath = $this->basePath . 'content' . $oldPath;
|
||||
|
||||
$result = true;
|
||||
|
||||
foreach($filetypes as $filetype)
|
||||
{
|
||||
$oldFilePath = $oldPath . '.' . $filetype;
|
||||
$newFilePath = $newPath . '.' . $filetype;
|
||||
|
||||
#check if file with filetype exists and rename
|
||||
if($oldFilePath != $newFilePath && file_exists($oldFilePath))
|
||||
{
|
||||
if(@rename($oldFilePath, $newFilePath))
|
||||
{
|
||||
$result = $result;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
*/
|
||||
}
|
@@ -382,6 +382,66 @@ class Validation
|
||||
return $v->errors();
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for resort navigation
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return true or $v->errors with array of errors to use in json-response
|
||||
*/
|
||||
|
||||
public function navigationSort(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['item_id', 'parent_id_from', 'parent_id_to']);
|
||||
$v->rule('regex', 'item_id', '/^[0-9.]+$/i');
|
||||
$v->rule('regex', 'parent_id_from', '/^[a-zA-Z0-9.]+$/i');
|
||||
$v->rule('regex', 'parent_id_to', '/^[a-zA-Z0-9.]+$/i');
|
||||
$v->rule('integer', 'index_new');
|
||||
$v->rule('integer', 'index_old');
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return $v->errors();
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for new navigation items
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return true or $v->errors with array of errors to use in json-response
|
||||
*/
|
||||
|
||||
public function navigationItem(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['folder_id', 'item_name', 'type', 'url']);
|
||||
$v->rule('regex', 'folder_id', '/^(root)|([0-9.]+)$/i');
|
||||
$v->rule('navigation', 'item_name');
|
||||
$v->rule('lengthBetween', 'item_name', 1, 60);
|
||||
$v->rule('in', 'type', ['file', 'folder']);
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* validation for system settings
|
||||
@@ -463,80 +523,7 @@ class Validation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for resort navigation
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return true or $v->errors with array of errors to use in json-response
|
||||
*/
|
||||
|
||||
public function navigationSort(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['item_id', 'parent_id_from', 'parent_id_to']);
|
||||
$v->rule('regex', 'item_id', '/^[0-9.]+$/i');
|
||||
$v->rule('regex', 'parent_id_from', '/^[a-zA-Z0-9.]+$/i');
|
||||
$v->rule('regex', 'parent_id_to', '/^[a-zA-Z0-9.]+$/i');
|
||||
$v->rule('integer', 'index_new');
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for new navigation items
|
||||
*
|
||||
* @param array $params with form data.
|
||||
* @return true or $v->errors with array of errors to use in json-response
|
||||
*/
|
||||
|
||||
public function navigationItem(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['folder_id', 'item_name', 'type', 'url']);
|
||||
$v->rule('regex', 'folder_id', '/^[0-9.]+$/i');
|
||||
# $v->rule('noSpecialChars', 'item_name');
|
||||
$v->rule('navigation', 'item_name');
|
||||
$v->rule('lengthBetween', 'item_name', 1, 60);
|
||||
$v->rule('in', 'type', ['file', 'folder']);
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
public function navigationBaseItem(array $params)
|
||||
{
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['item_name', 'type', 'url']);
|
||||
# $v->rule('noSpecialChars', 'item_name');
|
||||
$v->rule('navigation', 'item_name');
|
||||
$v->rule('lengthBetween', 'item_name', 1, 40);
|
||||
$v->rule('in', 'type', ['file', 'folder']);
|
||||
|
||||
if($v->validate())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $v->errors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validation for dynamic fields ( settings for themes and plugins)
|
||||
|
@@ -5,18 +5,10 @@
|
||||
|
||||
<h1 class="text-3xl font-bold mb-4">{{ translate('Visual Editor') }} </h1>
|
||||
|
||||
<div id="veditor" v-cloak></div>
|
||||
<div id="editor" 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-system.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-shared.js?v={{ settings.version }}"></script>
|
||||
<script>
|
||||
app.mount('#veditor');
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
@@ -620,6 +620,14 @@ video {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.static {
|
||||
position: static;
|
||||
}
|
||||
@@ -636,6 +644,11 @@ video {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sticky {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
.inset-0 {
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
@@ -688,6 +701,10 @@ video {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.m-1 {
|
||||
margin: 0.25rem;
|
||||
}
|
||||
|
||||
.my-2 {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
@@ -723,28 +740,45 @@ video {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.mx-1 {
|
||||
margin-left: 0.25rem;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.mt-6 {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.mt-3 {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mr-3 {
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.mr-1 {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.mr-4 {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.mt-3 {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.mb-3 {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.mr-3 {
|
||||
margin-right: 0.75rem;
|
||||
.ml-3 {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
.mt-5 {
|
||||
@@ -771,10 +805,6 @@ video {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.mt-auto {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.mt-8 {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
@@ -783,16 +813,16 @@ video {
|
||||
margin-top: 1.75rem;
|
||||
}
|
||||
|
||||
.mr-4 {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.ml-3 {
|
||||
margin-left: 0.75rem;
|
||||
.ml-1 {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.-ml-1 {
|
||||
margin-left: -0.25rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
@@ -839,6 +869,10 @@ video {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.h-0 {
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
.h-12 {
|
||||
height: 3rem;
|
||||
}
|
||||
@@ -851,14 +885,6 @@ video {
|
||||
height: 20rem;
|
||||
}
|
||||
|
||||
.h-64 {
|
||||
height: 16rem;
|
||||
}
|
||||
|
||||
.h-0 {
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
.h-48 {
|
||||
height: 12rem;
|
||||
}
|
||||
@@ -879,6 +905,10 @@ video {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.w-1\/4 {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.w-2\/5 {
|
||||
width: 40%;
|
||||
}
|
||||
@@ -895,6 +925,10 @@ video {
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.w-0 {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.w-5 {
|
||||
width: 1.25rem;
|
||||
}
|
||||
@@ -931,14 +965,6 @@ video {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.w-1\/4 {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.w-0 {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.max-w-md {
|
||||
max-width: 28rem;
|
||||
}
|
||||
@@ -975,6 +1001,23 @@ video {
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-spin {
|
||||
-webkit-animation: spin 1s linear infinite;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -983,10 +1026,8 @@ video {
|
||||
resize: both;
|
||||
}
|
||||
|
||||
.appearance-none {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
.list-none {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
@@ -1025,12 +1066,6 @@ video {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.space-x-8 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-right: calc(2rem * var(--tw-space-x-reverse));
|
||||
margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
|
||||
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-right: calc(1rem * var(--tw-space-x-reverse));
|
||||
@@ -1076,10 +1111,22 @@ video {
|
||||
border-right-width: 8px;
|
||||
}
|
||||
|
||||
.border-l-4 {
|
||||
border-left-width: 4px;
|
||||
}
|
||||
|
||||
.border-b {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.border-b-8 {
|
||||
border-bottom-width: 8px;
|
||||
}
|
||||
|
||||
.border-t-8 {
|
||||
border-top-width: 8px;
|
||||
}
|
||||
|
||||
.border-b-2 {
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
@@ -1096,18 +1143,6 @@ video {
|
||||
border-right-width: 2px;
|
||||
}
|
||||
|
||||
.border-l-4 {
|
||||
border-left-width: 4px;
|
||||
}
|
||||
|
||||
.border-b-8 {
|
||||
border-bottom-width: 8px;
|
||||
}
|
||||
|
||||
.border-t-8 {
|
||||
border-top-width: 8px;
|
||||
}
|
||||
|
||||
.border-solid {
|
||||
border-style: solid;
|
||||
}
|
||||
@@ -1117,6 +1152,11 @@ video {
|
||||
border-color: rgb(209 213 219 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-teal-500 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(20 184 166 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-stone-200 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(231 229 228 / var(--tw-border-opacity));
|
||||
@@ -1137,11 +1177,6 @@ video {
|
||||
border-color: rgb(214 211 209 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-teal-500 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(20 184 166 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-stone-700 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(68 64 60 / var(--tw-border-opacity));
|
||||
@@ -1177,11 +1212,6 @@ video {
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
.border-b-blue-600 {
|
||||
--tw-border-opacity: 1;
|
||||
border-bottom-color: rgb(37 99 235 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.border-b-white {
|
||||
--tw-border-opacity: 1;
|
||||
border-bottom-color: rgb(255 255 255 / var(--tw-border-opacity));
|
||||
@@ -1202,16 +1232,6 @@ video {
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-rose-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(244 63 94 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-teal-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(20 184 166 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-stone-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(245 245 244 / var(--tw-bg-opacity));
|
||||
@@ -1222,16 +1242,26 @@ video {
|
||||
background-color: rgb(68 64 60 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-red-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(254 226 226 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-stone-200 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(231 229 228 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-rose-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(244 63 94 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-teal-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(20 184 166 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-red-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(254 226 226 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-stone-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(28 25 23 / var(--tw-bg-opacity));
|
||||
@@ -1242,6 +1272,10 @@ video {
|
||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.bg-opacity-90 {
|
||||
--tw-bg-opacity: 0.9;
|
||||
}
|
||||
@@ -1254,6 +1288,10 @@ video {
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.p-1 {
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
@@ -1274,10 +1312,6 @@ video {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.p-1 {
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.py-5 {
|
||||
padding-top: 1.25rem;
|
||||
padding-bottom: 1.25rem;
|
||||
@@ -1333,6 +1367,10 @@ video {
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
|
||||
.pl-2 {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
.pl-3 {
|
||||
padding-left: 0.75rem;
|
||||
}
|
||||
@@ -1361,8 +1399,8 @@ video {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.pl-8 {
|
||||
padding-left: 2rem;
|
||||
.pb-4 {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.pt-4 {
|
||||
@@ -1373,12 +1411,16 @@ video {
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.pr-8 {
|
||||
padding-right: 2rem;
|
||||
.pl-4 {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.pb-4 {
|
||||
padding-bottom: 1rem;
|
||||
.pl-8 {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.pl-12 {
|
||||
padding-left: 3rem;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
@@ -1416,6 +1458,16 @@ video {
|
||||
line-height: 1rem;
|
||||
}
|
||||
|
||||
.text-3xl {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.25rem;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
@@ -1426,21 +1478,11 @@ video {
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.text-2xl {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.text-3xl {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.25rem;
|
||||
}
|
||||
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
@@ -1519,27 +1561,42 @@ video {
|
||||
color: rgb(6 182 212 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-stone-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(68 64 60 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-stone-200 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(231 229 228 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-stone-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(120 113 108 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.underline {
|
||||
-webkit-text-decoration-line: underline;
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
|
||||
.accent-pink-300 {
|
||||
accent-color: #f9a8d4;
|
||||
}
|
||||
|
||||
.accent-white {
|
||||
accent-color: #fff;
|
||||
}
|
||||
|
||||
.accent-teal-500 {
|
||||
accent-color: #14b8a6;
|
||||
}
|
||||
|
||||
.opacity-0 {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.opacity-25 {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.opacity-75 {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.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);
|
||||
@@ -1598,26 +1655,6 @@ video {
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.checked\:bg-white:checked {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.checked\:bg-blue-500:checked {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(59 130 246 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.checked\:bg-teal-500:checked {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(20 184 166 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.checked\:text-white:checked {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:border-b-4:hover {
|
||||
border-bottom-width: 4px;
|
||||
}
|
||||
@@ -1637,11 +1674,21 @@ video {
|
||||
border-color: rgb(20 184 166 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.hover\:border-stone-50:hover {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(250 250 249 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-gray-200:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-stone-50:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-stone-900:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(28 25 23 / var(--tw-bg-opacity));
|
||||
@@ -1687,9 +1734,19 @@ video {
|
||||
background-color: rgb(6 182 212 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-stone-50:hover {
|
||||
.hover\:bg-stone-700:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||
background-color: rgb(68 64 60 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-stone-500:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(120 113 108 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-teal-500:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(20 184 166 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:text-white:hover {
|
||||
@@ -1697,6 +1754,16 @@ video {
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:text-stone-50:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(250 250 249 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:text-stone-100:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(245 245 244 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:underline:hover {
|
||||
-webkit-text-decoration-line: underline;
|
||||
text-decoration-line: underline;
|
||||
@@ -1707,6 +1774,11 @@ video {
|
||||
border-color: rgb(37 99 235 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.focus\:border-stone-100:focus {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(245 245 244 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.focus\:bg-white:focus {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
@@ -1717,6 +1789,11 @@ video {
|
||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.focus\:bg-stone-100:focus {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(245 245 244 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.focus\:text-gray-700:focus {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(55 65 81 / var(--tw-text-opacity));
|
||||
@@ -1738,6 +1815,10 @@ video {
|
||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.group:hover .group-hover\:visible {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.dark\:text-gray-400 {
|
||||
--tw-text-opacity: 1;
|
||||
|
2
system/typemill/author/js/sortable.min.js
vendored
Normal file
2
system/typemill/author/js/sortable.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
635
system/typemill/author/js/vue-contentnavi.js
Normal file
635
system/typemill/author/js/vue-contentnavi.js
Normal file
@@ -0,0 +1,635 @@
|
||||
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>
|
||||
</div>
|
||||
<div class="flex w-full mb-1 font-bold">
|
||||
<div class="border-l-4 border-teal-500 published"></div>
|
||||
<a class="flex-grow p-1 hover:bg-teal-500 hover:text-stone-50 pl-2 text-bold transition duration-100" :href="getHomeUrl()">Home</a>
|
||||
</div>
|
||||
<div class="pl-2 pl-4 pl-8 pl-12"></div>
|
||||
<navilevel :navigation="navigation" />
|
||||
</div>`,
|
||||
data: function () {
|
||||
return {
|
||||
navigation: data.navigation,
|
||||
isExpended: false,
|
||||
expanded: [],
|
||||
}
|
||||
},
|
||||
mounted: function(){
|
||||
var expanded = localStorage.getItem('expanded');
|
||||
if(expanded !== null)
|
||||
{
|
||||
var expandedArray = expanded.split(',');
|
||||
var expandedLength = expandedArray.length;
|
||||
var cleanExpandedArray = [];
|
||||
for(var i = 0; i < expandedLength; i++)
|
||||
{
|
||||
if(typeof expandedArray[i] === 'string' && expandedArray[i] != '')
|
||||
{
|
||||
cleanExpandedArray.push(expandedArray[i]);
|
||||
}
|
||||
}
|
||||
this.expanded = expanded.split(',');
|
||||
}
|
||||
eventBus.$on('toggleFolder', this.toggleFolder);
|
||||
},
|
||||
methods: {
|
||||
getHomeUrl()
|
||||
{
|
||||
return tmaxios.defaults.baseURL + '/tm/content/visual';
|
||||
},
|
||||
toggleFolder: function(name)
|
||||
{
|
||||
var index = this.expanded.indexOf(name);
|
||||
if (index > -1)
|
||||
{
|
||||
this.expanded.splice(index, 1);
|
||||
// this.expandNavigation = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.expanded.push(name);
|
||||
}
|
||||
localStorage.setItem("expanded", this.expanded.toString());
|
||||
},
|
||||
expandNavigation()
|
||||
{
|
||||
this.expanded = this.getFolderNames(this.navigation, []);
|
||||
localStorage.setItem("expanded", this.expanded.toString());
|
||||
},
|
||||
collapseNavigation()
|
||||
{
|
||||
this.expanded = [];
|
||||
localStorage.removeItem('expanded');
|
||||
},
|
||||
getFolderNames(navigation, result)
|
||||
{
|
||||
for (const item of navigation)
|
||||
{
|
||||
if (item.elementType == 'folder')
|
||||
{
|
||||
result.push(item.name);
|
||||
this.getFolderNames(item.folderContent, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
navigation.component('draggable', vuedraggable);
|
||||
|
||||
navigation.component('navilevel',{
|
||||
template: `
|
||||
<draggable
|
||||
@start="onStart"
|
||||
@end="onEnd"
|
||||
:move="checkMove"
|
||||
:list="navigation"
|
||||
v-bind="dragOptions"
|
||||
class="dragArea"
|
||||
tag="ul"
|
||||
item-key="keyPath"
|
||||
:component-data="{
|
||||
id: parentId ? parentId : false
|
||||
}"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<li :class="element.elementType" :id="element.keyPath" :data-url="element.urlRelWoF" :data-active="element.active">
|
||||
<div class="flex w-full mb-1 relative" :class="element.elementType == 'folder' ? 'font-bold' : ''">
|
||||
<div class="border-l-4 border-teal-500" :class="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)">
|
||||
{{ element.name }}
|
||||
</a>
|
||||
<div v-if="load == element.keyPath" class="p-1 absolute right-0">
|
||||
<svg class="animate-spin h-5 w-5 text-stone-700" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div v-if="element.elementType == 'folder'" class=" p-1 bg-transparent absolute right-0" @click="callToggle(element.name)">
|
||||
<svg v-if="isExpanded(element.name)" class="icon icon-cheveron-up">
|
||||
<use xlink:href="#icon-cheveron-up"></use>
|
||||
</svg>
|
||||
<svg v-else class="icon icon-cheveron-down">
|
||||
<use xlink:href="#icon-cheveron-down"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<navilevel v-show="isExpanded(element.name)" v-if="element.elementType == 'folder'" :list="element.folderContent" :navigation="element.folderContent" :parentId="element.keyPath" />
|
||||
</li>
|
||||
</template>
|
||||
<template #footer>
|
||||
<li>
|
||||
<div class="flex w-full mb-1 group">
|
||||
<div class="border-l-4 border-stone-200"></div>
|
||||
<div class="flex-grow">
|
||||
<input :class="navilevel" class="w-full p-1 bg-stone-50 border-2 border-stone-50 focus:outline-none" placeholder="..." v-model="newItem">
|
||||
</div>
|
||||
<!-- <div class="w-1/4 invisible group-hover:visible"> -->
|
||||
<div class="flex">
|
||||
<button title="add a file" @click="addItem('file', parentId)" class="text-stone-500 bg-stone-100 hover:text-stone-100 hover:bg-stone-700 p-1 border-2 border-stone-50 transition duration-100">
|
||||
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 28">
|
||||
<path fill="currentColor" d="M22.937 5.938c0.578 0.578 1.062 1.734 1.062 2.562v18c0 0.828-0.672 1.5-1.5 1.5h-21c-0.828 0-1.5-0.672-1.5-1.5v-25c0-0.828 0.672-1.5 1.5-1.5h14c0.828 0 1.984 0.484 2.562 1.062zM16 2.125v5.875h5.875c-0.094-0.266-0.234-0.531-0.344-0.641l-4.891-4.891c-0.109-0.109-0.375-0.25-0.641-0.344zM22 26v-16h-6.5c-0.828 0-1.5-0.672-1.5-1.5v-6.5h-12v24h20zM6 12.5c0-0.281 0.219-0.5 0.5-0.5h11c0.281 0 0.5 0.219 0.5 0.5v1c0 0.281-0.219 0.5-0.5 0.5h-11c-0.281 0-0.5-0.219-0.5-0.5v-1zM17.5 16c0.281 0 0.5 0.219 0.5 0.5v1c0 0.281-0.219 0.5-0.5 0.5h-11c-0.281 0-0.5-0.219-0.5-0.5v-1c0-0.281 0.219-0.5 0.5-0.5h11zM17.5 20c0.281 0 0.5 0.219 0.5 0.5v1c0 0.281-0.219 0.5-0.5 0.5h-11c-0.281 0-0.5-0.219-0.5-0.5v-1c0-0.281 0.219-0.5 0.5-0.5h11z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button title="add a folder" @click="addItem('folder', parentId)" class="text-stone-500 bg-stone-100 hover:text-stone-100 hover:bg-stone-700 p-1 border-2 border-stone-50 transition duration-100">
|
||||
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 28">
|
||||
<path fill="currentColor" d="M24 20.5v-11c0-0.828-0.672-1.5-1.5-1.5h-11c-0.828 0-1.5-0.672-1.5-1.5v-1c0-0.828-0.672-1.5-1.5-1.5h-5c-0.828 0-1.5 0.672-1.5 1.5v15c0 0.828 0.672 1.5 1.5 1.5h19c0.828 0 1.5-0.672 1.5-1.5zM26 9.5v11c0 1.922-1.578 3.5-3.5 3.5h-19c-1.922 0-3.5-1.578-3.5-3.5v-15c0-1.922 1.578-3.5 3.5-3.5h5c1.922 0 3.5 1.578 3.5 3.5v0.5h10.5c1.922 0 3.5 1.578 3.5 3.5z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<!-- <button title="add a link" @click="addItem('link', parentId)" class="text-stone-500 bg-stone-100 hover:text-stone-100 hover:bg-stone-700 p-1 border-2 border-stone-50 transition duration-100">
|
||||
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32">
|
||||
<path fill="currentColor" d="M13.757 19.868c-0.416 0-0.832-0.159-1.149-0.476-2.973-2.973-2.973-7.81 0-10.783l6-6c1.44-1.44 3.355-2.233 5.392-2.233s3.951 0.793 5.392 2.233c2.973 2.973 2.973 7.81 0 10.783l-2.743 2.743c-0.635 0.635-1.663 0.635-2.298 0s-0.635-1.663 0-2.298l2.743-2.743c1.706-1.706 1.706-4.481 0-6.187-0.826-0.826-1.925-1.281-3.094-1.281s-2.267 0.455-3.094 1.281l-6 6c-1.706 1.706-1.706 4.481 0 6.187 0.635 0.635 0.635 1.663 0 2.298-0.317 0.317-0.733 0.476-1.149 0.476z"></path>
|
||||
<path fill="currentColor" d="M8 31.625c-2.037 0-3.952-0.793-5.392-2.233-2.973-2.973-2.973-7.81 0-10.783l2.743-2.743c0.635-0.635 1.664-0.635 2.298 0s0.635 1.663 0 2.298l-2.743 2.743c-1.706 1.706-1.706 4.481 0 6.187 0.826 0.826 1.925 1.281 3.094 1.281s2.267-0.455 3.094-1.281l6-6c1.706-1.706 1.706-4.481 0-6.187-0.635-0.635-0.635-1.663 0-2.298s1.663-0.635 2.298 0c2.973 2.973 2.973 7.81 0 10.783l-6 6c-1.44 1.44-3.355 2.233-5.392 2.233z"></path>
|
||||
</svg>
|
||||
</button> -->
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
</draggable>`,
|
||||
props: {
|
||||
navigation: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
parentId: {
|
||||
default: 'root'
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
navilevel: '',
|
||||
load: '?',
|
||||
freeze: false,
|
||||
newItem: '',
|
||||
format: /[@#*()=\[\]{};:"\\|,.<>\/]/,
|
||||
}
|
||||
},
|
||||
computed:
|
||||
{
|
||||
dragOptions()
|
||||
{
|
||||
return {
|
||||
animation: 150,
|
||||
group: "file",
|
||||
disabled: this.freeze,
|
||||
ghostClass: "ghost",
|
||||
};
|
||||
},
|
||||
|
||||
// this.value when input = v-model
|
||||
// this.list when input != v-model
|
||||
realValue()
|
||||
{
|
||||
return this.value ? this.value : this.list;
|
||||
}
|
||||
},
|
||||
methods:
|
||||
{
|
||||
getNaviClass(active, activeParent, keyPathArray)
|
||||
{
|
||||
var naviclass = 'pl-' + (keyPathArray.length * 2);
|
||||
this.navilevel = naviclass;
|
||||
if(active){ naviclass += " active" }
|
||||
if(activeParent){ naviclass += " activeParent" }
|
||||
|
||||
return naviclass;
|
||||
},
|
||||
getUrl(segment)
|
||||
{
|
||||
return tmaxios.defaults.baseURL + '/tm/content/visual' + segment;
|
||||
},
|
||||
callToggle(name)
|
||||
{
|
||||
eventBus.$emit('toggleFolder', name);
|
||||
},
|
||||
isExpanded(name)
|
||||
{
|
||||
if(this.$root.expanded.indexOf(name) > -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onStart(evt)
|
||||
{
|
||||
/* delete error messages if exist */
|
||||
// publishController.errors.message = false;
|
||||
},
|
||||
checkMove(evt)
|
||||
{
|
||||
/* do we want to keep that restriction, no folder into folders? */
|
||||
if(evt.dragged.classList.contains('folder') && evt.from.parentNode.id != evt.to.parentNode.id)
|
||||
{
|
||||
console.info("moved folder to another folder");
|
||||
return false;
|
||||
}
|
||||
if(evt.dragged.dataset.active == 'active' && !editor.draftDisabled)
|
||||
{
|
||||
console.info("moved page is active, save your changes first");
|
||||
// publishController.errors.message = "Please save your changes before you move the file";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onEnd(evt)
|
||||
{
|
||||
if(evt.from.parentNode.id == evt.to.parentNode.id && evt.oldIndex == evt.newIndex)
|
||||
{
|
||||
return
|
||||
}
|
||||
this.freeze = true;
|
||||
this.load = evt.item.id;
|
||||
|
||||
var self = this;
|
||||
|
||||
// self.errors = {title: false, content: false, message: false};
|
||||
|
||||
tmaxios.post('/api/v1/article/sort',{
|
||||
'item_id': evt.item.id,
|
||||
'parent_id_from': evt.from.parentNode.id,
|
||||
'parent_id_to': evt.to.parentNode.id,
|
||||
'index_old': evt.oldIndex,
|
||||
'index_new': evt.newIndex,
|
||||
'active': evt.item.dataset.active,
|
||||
'url': evt.item.dataset.url,
|
||||
// 'url': document.getElementById("path").value,
|
||||
// 'csrf_name': document.getElementById("csrf_name").value,
|
||||
// 'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response)
|
||||
{
|
||||
self.load = '?';
|
||||
self.freeze = false;
|
||||
|
||||
if(response.data.url)
|
||||
{
|
||||
window.location.replace(response.data.url);
|
||||
}
|
||||
if(response.data.navigation)
|
||||
{
|
||||
self.$root.$data.navigation = response.data.navigation;
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response.data.errors.message)
|
||||
{
|
||||
// publishController.errors.message = error.response.data.errors;
|
||||
}
|
||||
});
|
||||
},
|
||||
addItem(type, parent)
|
||||
{
|
||||
// publishController.errors.message = false;
|
||||
if(this.format.test(this.newItem) || !this.newItem || this.newItem.length > 40)
|
||||
{
|
||||
// publishController.errors.message = 'Special Characters are not allowed. Length between 1 and 40.';
|
||||
return;
|
||||
}
|
||||
|
||||
self = this;
|
||||
|
||||
self.freeze = true;
|
||||
// self.errors = {title: false, content: false, message: false};
|
||||
|
||||
tmaxios.post('/api/v1/article',{
|
||||
'item_name': this.newItem,
|
||||
'folder_id': parent,
|
||||
'type': type,
|
||||
// 'url': document.getElementById("path").value,
|
||||
// 'csrf_name': document.getElementById("csrf_name").value,
|
||||
// 'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
self.freeze = false;
|
||||
|
||||
if(response.data.url)
|
||||
{
|
||||
window.location.replace(response.data.url);
|
||||
}
|
||||
if(response.data.navigation)
|
||||
{
|
||||
self.items = response.data.navigation;
|
||||
self.newItem = '';
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
// publishController.errors.message = error.response.data.errors;
|
||||
});
|
||||
},
|
||||
emitter(value) {
|
||||
this.$emit("input", value);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
navigation.mount('#contentNavigation');
|
||||
|
||||
/*
|
||||
data: function () {
|
||||
return {
|
||||
title: "Navigation",
|
||||
navigation: data.navigation,
|
||||
homepage: false,
|
||||
editormode: 'visual',
|
||||
freeze: false,
|
||||
modalWindow: false,
|
||||
format: /[@#*()=\[\]{};:"\\|,.<>\/]/,
|
||||
folderName: '',
|
||||
showForm: false,
|
||||
newItem: '',
|
||||
collapse: [],
|
||||
}
|
||||
},
|
||||
|
||||
<draggable class="navi-list list-none" tag="ul"
|
||||
@start="onStart"
|
||||
@end="onEnd"
|
||||
:list="items"
|
||||
:move="checkMove"
|
||||
group="file"
|
||||
animation="150"
|
||||
:disabled="freeze"
|
||||
item-key="items.length">
|
||||
<navilevel
|
||||
v-for="item in items"
|
||||
ref="draggit"
|
||||
:freeze="freeze"
|
||||
:name="item.name"
|
||||
:hide="item.hide"
|
||||
:active="item.active"
|
||||
:parent="item.activeParent"
|
||||
:level="item.keyPath"
|
||||
:root="root"
|
||||
:url="item.urlRelWoF"
|
||||
:id="item.keyPath"
|
||||
:key="item.keyPath"
|
||||
:elementtype="item.elementType"
|
||||
:contains="item.contains"
|
||||
:filetype="item.fileType"
|
||||
:status="item.status"
|
||||
:folder="item.folderContent"
|
||||
:collapse="collapse"
|
||||
></navilevel>
|
||||
</draggable>
|
||||
data: function () {
|
||||
return {
|
||||
title: "Navigation",
|
||||
items: data.navigation,
|
||||
homepage: false,
|
||||
editormode: 'visual',
|
||||
freeze: false,
|
||||
modalWindow: false,
|
||||
format: /[@#*()=\[\]{};:"\\|,.<>\/]/,
|
||||
folderName: '',
|
||||
showForm: false,
|
||||
newItem: '',
|
||||
collapse: [],
|
||||
}
|
||||
},
|
||||
checkMove: function(evt){
|
||||
/* this.$refs.draggit[0].checkMove(evt); *
|
||||
if(evt.dragged.classList.contains('folder') && evt.from.parentNode.id != evt.to.parentNode.id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(evt.dragged.firstChild.className == 'active' && !editor.draftDisabled)
|
||||
{
|
||||
publishController.errors.message = "Please save your changes before you move the file";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onStart: function(evt){
|
||||
this.$refs.draggit[0].onStart(evt);
|
||||
},
|
||||
onEnd: function(evt){
|
||||
this.$refs.draggit[0].onEnd(evt);
|
||||
},
|
||||
addFile : function(type)
|
||||
{
|
||||
publishController.errors.message = false;
|
||||
|
||||
if(this.format.test(this.newItem) || !this.newItem || this.newItem.length > 40)
|
||||
{
|
||||
publishController.errors.message = 'Special Characters are not allowed. Length between 1 and 40.';
|
||||
return;
|
||||
}
|
||||
|
||||
self = this;
|
||||
|
||||
self.freeze = true;
|
||||
self.errors = {title: false, content: false, message: false};
|
||||
|
||||
myaxios.post('/api/v1/baseitem',{
|
||||
'item_name': this.newItem,
|
||||
'type': type,
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
self.freeze = false;
|
||||
|
||||
if(response.data.url)
|
||||
{
|
||||
window.location.replace(response.data.url);
|
||||
}
|
||||
if(response.data.data)
|
||||
{
|
||||
self.items = response.data.data;
|
||||
self.newItem = '';
|
||||
self.showForm = false;
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
publishController.errors.message = error.response.data.errors;
|
||||
});
|
||||
},
|
||||
getNavi: function()
|
||||
{
|
||||
publishController.errors.message = false;
|
||||
|
||||
var self = this;
|
||||
|
||||
self.freeze = true;
|
||||
self.errors = {title: false, content: false, message: false};
|
||||
|
||||
var activeItem = document.getElementById("path").value;
|
||||
|
||||
var url = this.root + '/api/v1/navigation?url=' + activeItem;
|
||||
var method = 'GET';
|
||||
|
||||
myaxios.get('/api/v1/navigation',{
|
||||
params: {
|
||||
'url': activeItem,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
self.freeze = false;
|
||||
if(response.data.data)
|
||||
{
|
||||
self.items = response.data.data;
|
||||
self.newItem = '';
|
||||
self.homepage = response.data.homepage;
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response.data.errors)
|
||||
{
|
||||
publishController.errors.message = error.response.data.errors;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
checkMove : function(evt)
|
||||
{
|
||||
if(evt.dragged.classList.contains('folder') && evt.from.parentNode.id != evt.to.parentNode.id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(evt.dragged.firstChild.className == 'active' && !editor.draftDisabled)
|
||||
{
|
||||
publishController.errors.message = "Please save your changes before you move the file";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onStart : function(evt)
|
||||
{
|
||||
/* delete error messages if exist *
|
||||
publishController.errors.message = false;
|
||||
},
|
||||
getUrl : function(root, url)
|
||||
{
|
||||
return root + '/tm/content/' + this.$root.$data.editormode + url;
|
||||
},
|
||||
checkActive : function(active,parent)
|
||||
{
|
||||
if(active && !parent)
|
||||
{
|
||||
return 'active';
|
||||
}
|
||||
return 'inactive';
|
||||
},
|
||||
|
||||
checkActive : function(active,parent)
|
||||
{
|
||||
if(active && !parent)
|
||||
{
|
||||
return 'active';
|
||||
}
|
||||
return 'inactive';
|
||||
},
|
||||
addFile : function(type)
|
||||
{
|
||||
publishController.errors.message = false;
|
||||
|
||||
if(this.format.test(this.newItem) || !this.newItem || this.newItem.length > 40)
|
||||
{
|
||||
publishController.errors.message = 'Special Characters are not allowed. Length between 1 and 40.';
|
||||
return;
|
||||
}
|
||||
|
||||
self = this;
|
||||
|
||||
self.freeze = true;
|
||||
self.errors = {title: false, content: false, message: false};
|
||||
|
||||
myaxios.post('/api/v1/baseitem',{
|
||||
'item_name': this.newItem,
|
||||
'type': type,
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
self.freeze = false;
|
||||
|
||||
if(response.data.url)
|
||||
{
|
||||
window.location.replace(response.data.url);
|
||||
}
|
||||
if(response.data.data)
|
||||
{
|
||||
self.items = response.data.data;
|
||||
self.newItem = '';
|
||||
self.showForm = false;
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
publishController.errors.message = error.response.data.errors;
|
||||
});
|
||||
},
|
||||
|
||||
addFile : function(type)
|
||||
{
|
||||
publishController.errors.message = false;
|
||||
|
||||
if(this.$root.$data.format.test(this.newItem) || !this.newItem || this.newItem.length > 60)
|
||||
{
|
||||
publishController.errors.message = 'Special Characters are not allowed. Length between 1 and 60.';
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
self.$root.$data.freeze = true;
|
||||
self.errors = {title: false, content: false, message: false};
|
||||
|
||||
myaxios.post('/api/v1/article',{
|
||||
'folder_id': this.$el.id,
|
||||
'item_name': this.newItem,
|
||||
'type': type,
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
self.$root.$data.freeze = false;
|
||||
|
||||
if(response.data.url)
|
||||
{
|
||||
window.location.replace(response.data.url);
|
||||
}
|
||||
if(response.data.data)
|
||||
{
|
||||
// evt.item.classList.remove("load");
|
||||
self.$root.$data.items = response.data.data;
|
||||
self.newItem = '';
|
||||
self.showForm = false;
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response.data.errors)
|
||||
{
|
||||
publishController.errors.message = error.response.data.errors;
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
navigation.mount('#contentNavigation');
|
||||
*/
|
2
system/typemill/author/js/vuedraggable.umd.min.js
vendored
Normal file
2
system/typemill/author/js/vuedraggable.umd.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -36,7 +36,7 @@
|
||||
|
||||
<div class="max-w-6xl m-auto mt-7 flex justify-between" id="main" data-url="{{ base_url() }}">
|
||||
<aside class="w-1/4">
|
||||
{% include 'partials/contentNavi.twig' %}
|
||||
<div id="contentNavigation" v-cloak></div>
|
||||
</aside>
|
||||
<article class="w-3/4 bg-stone-50 shadow-md p-8">
|
||||
{% block content %}{% endblock %}
|
||||
@@ -59,6 +59,9 @@
|
||||
</script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue.js?v={{ settings.version }}"></script>
|
||||
<script src="{{ base_url() }}/system/typemill/author/js/vue-eventbus.js?v={{ settings.version }}"></script>
|
||||
<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 type="module" src="{{ base_url() }}/system/typemill/author/js/vue-contentnavi.js?v={{ settings.version }}"></script>
|
||||
|
||||
{% block javascript %}{% endblock %}
|
||||
|
||||
|
@@ -177,6 +177,12 @@
|
||||
<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>
|
||||
<symbol id="icon-cheveron-down" viewBox="0 0 20 20">
|
||||
<path d="M9.293 12.95l0.707 0.707 5.657-5.657-1.414-1.414-4.243 4.242-4.243-4.242-1.414 1.414z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-cheveron-up" viewBox="0 0 20 20">
|
||||
<path d="M10.707 7.050l-0.707-0.707-5.657 5.657 1.414 1.414 4.243-4.242 4.243 4.242 1.414-1.414z"></path>
|
||||
</symbol>
|
||||
{{ assets.renderSvg() }}
|
||||
<defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
@@ -12,6 +12,7 @@ use Typemill\Controllers\ControllerApiSystemExtensions;
|
||||
use Typemill\Controllers\ControllerApiSystemLicense;
|
||||
use Typemill\Controllers\ControllerApiSystemUsers;
|
||||
use Typemill\Controllers\ControllerApiImage;
|
||||
use Typemill\Controllers\ControllerApiAuthorArticle;
|
||||
|
||||
$app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) {
|
||||
|
||||
@@ -40,6 +41,10 @@ $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) {
|
||||
$group->put('/image', ControllerApiMedia::class . ':publishImage')->setName('api.image.publish');
|
||||
$group->delete('/image', ControllerApiMedia::class . ':deleteImage')->setName('api.image.delete');
|
||||
|
||||
# ARTICLE
|
||||
$group->post('/article/sort', ControllerApiAuthorArticle::class . ':sortArticle')->setName('api.article.sort')->add(new ApiAuthorization($acl, 'content', 'view')); # author
|
||||
$group->post('/article', ControllerApiAuthorArticle::class . ':createArticle')->setName('api.article.create')->add(new ApiAuthorization($acl, 'content', 'view')); # author
|
||||
|
||||
})->add(new ApiAuthentication());
|
||||
|
||||
|
||||
|
@@ -6,14 +6,18 @@ member:
|
||||
- 'view'
|
||||
- 'update'
|
||||
- 'delete'
|
||||
author:
|
||||
name: author
|
||||
contributor:
|
||||
name: contributor
|
||||
inherits: member
|
||||
permissions:
|
||||
mycontent:
|
||||
- 'view'
|
||||
- 'create'
|
||||
- 'update'
|
||||
author:
|
||||
name: author
|
||||
inherits: contributor
|
||||
permissions:
|
||||
content:
|
||||
- 'view'
|
||||
editor:
|
||||
|
Reference in New Issue
Block a user