mirror of
https://github.com/typemill/typemill.git
synced 2025-10-17 15:46:28 +02:00
Version 1.3.3: Pages and Posts
This commit is contained in:
@@ -13,6 +13,8 @@ use Typemill\Events\OnPagePublished;
|
||||
use Typemill\Events\OnPageUnpublished;
|
||||
use Typemill\Events\OnPageDeleted;
|
||||
use Typemill\Events\OnPageSorted;
|
||||
use \URLify;
|
||||
|
||||
|
||||
class ContentApiController extends ContentController
|
||||
{
|
||||
@@ -412,13 +414,14 @@ class ContentApiController extends ContentController
|
||||
|
||||
return $response->withJson(array('data' => $internalStructure, 'errors' => false, 'url' => $url));
|
||||
}
|
||||
|
||||
public function createArticle(Request $request, Response $response, $args)
|
||||
|
||||
|
||||
public function createPost(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->params = $request->getParams();
|
||||
$this->uri = $request->getUri();
|
||||
|
||||
|
||||
# url is only needed, if an active page is moved
|
||||
$url = false;
|
||||
|
||||
@@ -426,7 +429,76 @@ class ContentApiController extends ContentController
|
||||
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
|
||||
|
||||
# validate input
|
||||
if(!$this->validateNaviItem()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Special Characters not allowed. Length between 1 and 20 chars.', 'url' => $url), 422); }
|
||||
if(!$this->validateNaviItem()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Special Characters not allowed. Length between 1 and 60 chars.', 'url' => $url), 422); }
|
||||
|
||||
# get the ids (key path) for item, old folder and new folder
|
||||
$folderKeyPath = explode('.', $this->params['folder_id']);
|
||||
|
||||
# get the item from structure
|
||||
$folder = Folder::getItemWithKeyPath($this->structure, $folderKeyPath);
|
||||
|
||||
if(!$folder){ return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); }
|
||||
|
||||
$name = $this->params['item_name'];
|
||||
$slug = URLify::filter(iconv(mb_detect_encoding($this->params['item_name'], mb_detect_order(), true), "UTF-8", $this->params['item_name']));
|
||||
$namePath = date("YmdHi") . '-' . $slug;
|
||||
$folderPath = 'content' . $folder->path;
|
||||
$content = json_encode(['# ' . $name, 'Content']);
|
||||
|
||||
# initialise write object
|
||||
$write = new WriteYaml();
|
||||
|
||||
# check, if name exists
|
||||
if($write->checkFile($folderPath, $namePath . '.txt') OR $write->checkFile($folderPath, $namePath . '.md'))
|
||||
{
|
||||
return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404);
|
||||
}
|
||||
|
||||
if(!$write->writeFile($folderPath, $namePath . '.txt', $content))
|
||||
{
|
||||
return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
|
||||
}
|
||||
|
||||
|
||||
# get extended structure
|
||||
$extended = $write->getYaml('cache', 'structure-extended.yaml');
|
||||
|
||||
# create the url for the item
|
||||
$urlWoF = $folder->urlRelWoF . '/' . $slug;
|
||||
|
||||
# add the navigation name to the item htmlspecialchars needed for frensh language
|
||||
$extended[$urlWoF] = ['hide' => false, 'navtitle' => $name];
|
||||
|
||||
# store the extended structure
|
||||
$write->updateYaml('cache', 'structure-extended.yaml', $extended);
|
||||
|
||||
|
||||
# update the structure for editor
|
||||
$this->setStructure($draft = true, $cache = false);
|
||||
|
||||
$folder = Folder::getItemWithKeyPath($this->structure, $folderKeyPath);
|
||||
|
||||
# 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('posts' => $folder, $this->structure, 'errors' => false, 'url' => $url));
|
||||
}
|
||||
|
||||
|
||||
public function createArticle(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->params = $request->getParams();
|
||||
$this->uri = $request->getUri();
|
||||
|
||||
# url is only needed, if an active page is moved
|
||||
$url = false;
|
||||
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
|
||||
|
||||
# validate input
|
||||
if(!$this->validateNaviItem()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Special Characters not allowed. Length between 1 and 60 chars.', 'url' => $url), 422); }
|
||||
|
||||
# get the ids (key path) for item, old folder and new folder
|
||||
$folderKeyPath = explode('.', $this->params['folder_id']);
|
||||
@@ -440,16 +512,19 @@ class ContentApiController extends ContentController
|
||||
# get the content of the target folder
|
||||
$folderContent = $folder->folderContent;
|
||||
|
||||
$name = $this->params['item_name'];
|
||||
$slug = URLify::filter(iconv(mb_detect_encoding($this->params['item_name'], mb_detect_order(), true), "UTF-8", $this->params['item_name']));
|
||||
|
||||
# create the name for the new item
|
||||
$nameParts = Folder::getStringParts($this->params['item_name']);
|
||||
$name = implode("-", $nameParts);
|
||||
$slug = $name;
|
||||
# $nameParts = Folder::getStringParts($this->params['item_name']);
|
||||
# $name = implode("-", $nameParts);
|
||||
# $slug = $name;
|
||||
|
||||
# initialize index
|
||||
$index = 0;
|
||||
|
||||
# initialise write object
|
||||
$write = new Write();
|
||||
$write = new WriteYaml();
|
||||
|
||||
# iterate through the whole content of the new folder
|
||||
$writeError = false;
|
||||
@@ -472,13 +547,15 @@ class ContentApiController extends ContentController
|
||||
if($writeError){ return $response->withJson(array('data' => $this->structure, '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 . '-' . $name : '0' . $index . '-' . $name;
|
||||
# $namePath = $index > 9 ? $index . '-' . $name : '0' . $index . '-' . $name;
|
||||
$namePath = $index > 9 ? $index . '-' . $slug : '0' . $index . '-' . $slug;
|
||||
$folderPath = 'content' . $folder->path;
|
||||
|
||||
$title = implode(" ", $nameParts);
|
||||
# $title = implode(" ", $nameParts);
|
||||
|
||||
# create default content
|
||||
$content = json_encode(['# ' . $title, 'Content']);
|
||||
# $content = json_encode(['# ' . $title, 'Content']);
|
||||
$content = json_encode(['# ' . $name, 'Content']);
|
||||
|
||||
if($this->params['type'] == 'file')
|
||||
{
|
||||
@@ -494,8 +571,27 @@ class ContentApiController extends ContentController
|
||||
return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
|
||||
}
|
||||
$write->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 = $write->getYaml('cache', 'structure-extended.yaml');
|
||||
|
||||
# create the url for the item
|
||||
$urlWoF = $folder->urlRelWoF . '/' . $slug;
|
||||
|
||||
# add the navigation name to the item htmlspecialchars needed for frensh language
|
||||
$extended[$urlWoF] = ['hide' => false, 'navtitle' => $name];
|
||||
|
||||
# store the extended structure
|
||||
$write->updateYaml('cache', 'structure-extended.yaml', $extended);
|
||||
|
||||
|
||||
|
||||
# update the structure for editor
|
||||
$this->setStructure($draft = true, $cache = false);
|
||||
|
||||
@@ -506,7 +602,7 @@ class ContentApiController extends ContentController
|
||||
}
|
||||
|
||||
# activate this if you want to redirect after creating the page...
|
||||
# $url = $this->uri->getBaseUrl() . '/tm/content' . $folder->urlRelWoF . '/' . $name;
|
||||
# $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $folder->urlRelWoF . '/' . $slug;
|
||||
|
||||
return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url));
|
||||
}
|
||||
@@ -527,15 +623,18 @@ class ContentApiController extends ContentController
|
||||
if(!$this->validateBaseNaviItem()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Special Characters not allowed. Length between 1 and 20 chars.', 'url' => $url), 422); }
|
||||
|
||||
# create the name for the new item
|
||||
$nameParts = Folder::getStringParts($this->params['item_name']);
|
||||
$name = implode("-", $nameParts);
|
||||
$slug = $name;
|
||||
# $nameParts = Folder::getStringParts($this->params['item_name']);
|
||||
# $name = implode("-", $nameParts);
|
||||
# $slug = $name;
|
||||
|
||||
$name = $this->params['item_name'];
|
||||
$slug = URLify::filter(iconv(mb_detect_encoding($this->params['item_name'], mb_detect_order(), true), "UTF-8", $this->params['item_name']));
|
||||
|
||||
# initialize index
|
||||
$index = 0;
|
||||
|
||||
# initialise write object
|
||||
$write = new Write();
|
||||
$write = new WriteYaml();
|
||||
|
||||
# iterate through the whole content of the new folder
|
||||
$writeError = false;
|
||||
@@ -558,11 +657,12 @@ class ContentApiController extends ContentController
|
||||
if($writeError){ return $response->withJson(array('data' => $this->structure, '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 . '-' . $name : '0' . $index . '-' . $name;
|
||||
$namePath = $index > 9 ? $index . '-' . $slug : '0' . $index . '-' . $slug;
|
||||
$folderPath = 'content';
|
||||
|
||||
# create default content
|
||||
$content = json_encode(['# Add Title', 'Add Content']);
|
||||
# $content = json_encode(['# Add Title', 'Add Content']);
|
||||
$content = json_encode(['# ' . $name, 'Content']);
|
||||
|
||||
if($this->params['type'] == 'file')
|
||||
{
|
||||
@@ -578,8 +678,25 @@ class ContentApiController extends ContentController
|
||||
return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
|
||||
}
|
||||
$write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content);
|
||||
|
||||
# activate this if you want to redirect after creating the page...
|
||||
$url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . '/' . $slug;
|
||||
}
|
||||
|
||||
|
||||
# get extended structure
|
||||
$extended = $write->getYaml('cache', 'structure-extended.yaml');
|
||||
|
||||
# create the url for the item
|
||||
$urlWoF = '/' . $slug;
|
||||
|
||||
# add the navigation name to the item htmlspecialchars needed for frensh language
|
||||
$extended[$urlWoF] = ['hide' => false, 'navtitle' => $name];
|
||||
|
||||
# store the extended structure
|
||||
$write->updateYaml('cache', 'structure-extended.yaml', $extended);
|
||||
|
||||
|
||||
# update the structure for editor
|
||||
$this->setStructure($draft = true, $cache = false);
|
||||
|
||||
@@ -1006,8 +1123,6 @@ class ContentApiController extends ContentController
|
||||
return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
|
||||
}
|
||||
|
||||
/* set safe mode to escape javascript and html in markdown */
|
||||
$parsedown->setSafeMode(true);
|
||||
|
||||
/* parse markdown-file to content-array, if title parse title. */
|
||||
if($this->params['block_id'] == 0)
|
||||
@@ -1016,6 +1131,9 @@ class ContentApiController extends ContentController
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set safe mode to escape javascript and html in markdown */
|
||||
$parsedown->setSafeMode(true);
|
||||
|
||||
$blockArray = $parsedown->text($blockMarkdown);
|
||||
}
|
||||
|
||||
|
@@ -377,13 +377,18 @@ abstract class ContentController
|
||||
{
|
||||
$files = array_diff(scandir($path), array('.', '..'));
|
||||
|
||||
# check if there are folders first, then stop the operation
|
||||
# check if there are published pages or folders inside, then stop the operation
|
||||
foreach ($files as $file)
|
||||
{
|
||||
if(is_dir(realpath($path) . DIRECTORY_SEPARATOR . $file))
|
||||
{
|
||||
$this->errors = ['message' => 'Please delete the sub-folder first.'];
|
||||
}
|
||||
|
||||
if(substr($file, -3) == '.md' )
|
||||
{
|
||||
$this->errors = ['message' => 'Please unpublish all pages in the folder first.'];
|
||||
}
|
||||
}
|
||||
|
||||
if(!$this->errors)
|
||||
|
@@ -18,12 +18,22 @@ class MetaApiController extends ContentController
|
||||
}
|
||||
|
||||
# get the standard meta-definitions and the meta-definitions from plugins (same for all sites)
|
||||
public function aggregateMetaDefinitions()
|
||||
public function aggregateMetaDefinitions($folder = null)
|
||||
{
|
||||
$writeYaml = new writeYaml();
|
||||
|
||||
$metatabs = $writeYaml->getYaml('system' . DIRECTORY_SEPARATOR . 'author', 'metatabs.yaml');
|
||||
|
||||
if($folder)
|
||||
{
|
||||
$metatabs['meta']['fields']['contains'] = [
|
||||
'type' => 'radio',
|
||||
'label' => 'This folder contains:',
|
||||
'options' => ['pages' => 'PAGES (sort in navigation with drag & drop)', 'posts' => 'POSTS (sorted by publish date, for news or blogs)'],
|
||||
'class' => 'medium'
|
||||
];
|
||||
}
|
||||
|
||||
# loop through all plugins
|
||||
foreach($this->settings['plugins'] as $name => $plugin)
|
||||
{
|
||||
@@ -78,8 +88,20 @@ class MetaApiController extends ContentController
|
||||
$pagemeta = $writeYaml->getPageMetaDefaults($this->content, $this->settings, $this->item);
|
||||
}
|
||||
|
||||
# get global metadefinitions
|
||||
$metadefinitions = $this->aggregateMetaDefinitions();
|
||||
# if item is a folder
|
||||
if($this->item->elementType == "folder" && isset($this->item->contains))
|
||||
{
|
||||
|
||||
$pagemeta['meta']['contains'] = isset($pagemeta['meta']['contains']) ? $pagemeta['meta']['contains'] : $this->item->contains;
|
||||
|
||||
# get global metadefinitions
|
||||
$metadefinitions = $this->aggregateMetaDefinitions($folder = true);
|
||||
}
|
||||
else
|
||||
{
|
||||
# get global metadefinitions
|
||||
$metadefinitions = $this->aggregateMetaDefinitions();
|
||||
}
|
||||
|
||||
$metadata = [];
|
||||
$metascheme = [];
|
||||
@@ -98,7 +120,7 @@ class MetaApiController extends ContentController
|
||||
# store the metascheme in cache for frontend
|
||||
$writeYaml->updateYaml('cache', 'metatabs.yaml', $metascheme);
|
||||
|
||||
return $response->withJson(array('metadata' => $metadata, 'metadefinitions' => $metadefinitions, 'errors' => false));
|
||||
return $response->withJson(array('metadata' => $metadata, 'metadefinitions' => $metadefinitions, 'item' => $this->item, 'errors' => false));
|
||||
}
|
||||
|
||||
public function updateArticleMeta(Request $request, Response $response, $args)
|
||||
@@ -117,8 +139,25 @@ class MetaApiController extends ContentController
|
||||
return $response->withJson($this->errors, 404);
|
||||
}
|
||||
|
||||
# load metadefinitions
|
||||
$metaDefinitions = $this->aggregateMetaDefinitions();
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
|
||||
|
||||
# if item is a folder
|
||||
if($this->item->elementType == "folder")
|
||||
{
|
||||
$pagemeta['meta']['contains'] = isset($pagemeta['meta']['contains']) ? $pagemeta['meta']['contains'] : $this->item->contains;
|
||||
|
||||
# get global metadefinitions
|
||||
$metaDefinitions = $this->aggregateMetaDefinitions($folder = true);
|
||||
}
|
||||
else
|
||||
{
|
||||
# get global metadefinitions
|
||||
$metaDefinitions = $this->aggregateMetaDefinitions();
|
||||
}
|
||||
|
||||
# create validation object
|
||||
$validate = $this->getValidator();
|
||||
@@ -147,12 +186,6 @@ class MetaApiController extends ContentController
|
||||
|
||||
# return validation errors
|
||||
if($errors){ return $response->withJson(array('errors' => $errors),422); }
|
||||
|
||||
# set structure
|
||||
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
|
||||
|
||||
$writeYaml = new writeYaml();
|
||||
|
||||
@@ -167,6 +200,50 @@ class MetaApiController extends ContentController
|
||||
|
||||
if($tab == 'meta')
|
||||
{
|
||||
|
||||
# if manual date has been modified
|
||||
if(isset($metaInput['manualdate']) && !isset($metaPage['meta']['manualdate']) OR ($metaInput['manualdate'] != $metaPage['meta']['manualdate']))
|
||||
{
|
||||
# update the time
|
||||
$metaInput['time'] = date('H-i-s', time());
|
||||
|
||||
# if it is a post, then rename the post
|
||||
if($this->item->elementType == "file" && strlen($this->item->order) == 12)
|
||||
{
|
||||
# create file-prefix with date
|
||||
$datetime = $metaInput['manualdate'] . '-' . $metaInput['time'];
|
||||
$datetime = implode(explode('-', $datetime));
|
||||
$datetime = substr($datetime,0,12);
|
||||
|
||||
# create the new filename
|
||||
$pathWithoutFile = str_replace($this->item->originalName, "", $this->item->path);
|
||||
$newPathWithoutType = $pathWithoutFile . $datetime . '-' . $this->item->slug;
|
||||
|
||||
$writeYaml->renamePost($this->item->pathWithoutType, $newPathWithoutType);
|
||||
|
||||
# recreate the draft structure
|
||||
$this->setStructure($draft = true, $cache = false);
|
||||
|
||||
# update item
|
||||
$this->setItem();
|
||||
}
|
||||
}
|
||||
|
||||
# if folder has changed and contains pages instead of posts or posts instead of pages
|
||||
if($this->item->elementType == "folder" && ($metaPage['meta']['contains'] !== $metaInput['contains']))
|
||||
{
|
||||
$structure = true;
|
||||
|
||||
if($metaInput['contains'] == "posts")
|
||||
{
|
||||
$writeYaml->transformPagesToPosts($this->item);
|
||||
}
|
||||
if($metaInput['contains'] == "pages")
|
||||
{
|
||||
$writeYaml->transformPostsToPages($this->item);
|
||||
}
|
||||
}
|
||||
|
||||
# normalize the meta-input
|
||||
$metaInput['navtitle'] = (isset($metaInput['navtitle']) && $metaInput['navtitle'] !== null )? $metaInput['navtitle'] : '';
|
||||
$metaInput['hide'] = (isset($metaInput['hide']) && $metaInput['hide'] !== null) ? $metaInput['hide'] : false;
|
||||
@@ -179,9 +256,8 @@ class MetaApiController extends ContentController
|
||||
|
||||
$structure = true;
|
||||
}
|
||||
|
||||
# check if navtitle or hide-value has been changed
|
||||
elseif(
|
||||
# check if navtitle or hide-value has been changed
|
||||
($metaPage['meta']['navtitle'] != $metaInput['navtitle'])
|
||||
OR
|
||||
($metaPage['meta']['hide'] != $metaInput['hide'])
|
||||
@@ -192,21 +268,6 @@ class MetaApiController extends ContentController
|
||||
|
||||
$structure = true;
|
||||
}
|
||||
|
||||
if($structure)
|
||||
{
|
||||
# store the file
|
||||
$writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended);
|
||||
|
||||
# recreate the draft structure
|
||||
$this->setStructure($draft = true, $cache = false);
|
||||
|
||||
# set item in navigation active again
|
||||
$activeItem = Folder::getItemForUrl($this->structure, $this->item->urlRel, $this->uri->getBaseUrl());
|
||||
|
||||
# send new structure to frontend
|
||||
$structure = $this->structure;
|
||||
}
|
||||
}
|
||||
|
||||
# add the new/edited metadata
|
||||
@@ -215,8 +276,26 @@ class MetaApiController extends ContentController
|
||||
# store the metadata
|
||||
$writeYaml->updateYaml($this->settings['contentFolder'], $this->item->pathWithoutType . '.yaml', $meta);
|
||||
|
||||
if($structure)
|
||||
{
|
||||
# store the extended file
|
||||
$writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended);
|
||||
|
||||
# recreate the draft structure
|
||||
$this->setStructure($draft = true, $cache = false);
|
||||
|
||||
# update item
|
||||
$this->setItem();
|
||||
|
||||
# set item in navigation active again
|
||||
$activeItem = Folder::getItemForUrl($this->structure, $this->item->urlRel, $this->uri->getBaseUrl());
|
||||
|
||||
# send new structure to frontend
|
||||
$structure = $this->structure;
|
||||
}
|
||||
|
||||
# return with the new metadata
|
||||
return $response->withJson(array('metadata' => $metaInput, 'structure' => $structure, 'errors' => false));
|
||||
return $response->withJson(array('metadata' => $metaInput, 'structure' => $structure, 'item' => $this->item, 'errors' => false));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -63,7 +63,8 @@ class PageController extends Controller
|
||||
$sitemap->updateSitemap('cache', 'sitemap.xml', 'lastSitemap.txt', $structure, $uri->getBaseUrl());
|
||||
|
||||
/* check and update the typemill-version in the user settings */
|
||||
$this->updateVersion($uri->getBaseUrl());
|
||||
# this version check is not needed
|
||||
# $this->updateVersion($uri->getBaseUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +250,7 @@ class PageController extends Controller
|
||||
/* cache structure */
|
||||
$cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure);
|
||||
|
||||
if($this->containsHiddenPages($extended))
|
||||
if($extended && $this->containsHiddenPages($extended))
|
||||
{
|
||||
# generate the navigation (delete empty pages)
|
||||
$navigation = $this->createNavigationFromStructure($structure);
|
||||
@@ -295,6 +296,7 @@ class PageController extends Controller
|
||||
return $navigation;
|
||||
}
|
||||
|
||||
# not in use, stored the latest version in user settings, but that does not make sense because checkd on the fly with api in admin
|
||||
protected function updateVersion($baseUrl)
|
||||
{
|
||||
/* check the latest public typemill version */
|
||||
|
@@ -136,15 +136,18 @@ class SettingsController extends Controller
|
||||
|
||||
/* add the preview image */
|
||||
$img = getcwd() . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeName . DIRECTORY_SEPARATOR . $themeName;
|
||||
$jpg = $img . '.jpg';
|
||||
$png = $img . '.png';
|
||||
$img = file_exists($jpg) ? $jpg : false;
|
||||
if(!$img)
|
||||
|
||||
$image = false;
|
||||
if(file_exists($img . '.jpg'))
|
||||
{
|
||||
$img = file_exists($png) ? $png : false;
|
||||
$image = $themeName . '.jpg';
|
||||
}
|
||||
if(file_exists($img . '.png'))
|
||||
{
|
||||
$image = $themeName . '.png';
|
||||
}
|
||||
|
||||
$themedata[$themeName]['img'] = $img;
|
||||
$themedata[$themeName]['img'] = $image;
|
||||
}
|
||||
|
||||
/* add the users for navigation */
|
||||
|
@@ -38,7 +38,7 @@ class TwigLanguageExtension extends \Twig_Extension
|
||||
$string = strtoupper( $string );
|
||||
|
||||
//translates the string
|
||||
$translated_label = $this->labels[$string];
|
||||
$translated_label = isset($this->labels[$string]) ? $this->labels[$string] : null;
|
||||
|
||||
// if the string is not present, set the original string
|
||||
if( empty($translated_label) ){
|
||||
|
@@ -77,7 +77,8 @@ class Field
|
||||
/* defines additional data, that are allowed for fields */
|
||||
private $helpers = array(
|
||||
'help',
|
||||
'description'
|
||||
'description',
|
||||
'fieldsize'
|
||||
);
|
||||
|
||||
public function __construct($fieldName, array $fieldConfigs)
|
||||
|
@@ -91,11 +91,13 @@ class Folder
|
||||
return $folderContent;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transforms array of folder item into an array of item-objects with additional information for each item
|
||||
* vars: multidimensional array with folder- and file-names
|
||||
* returns: array of objects. Each object contains information about an item (file or folder).
|
||||
*/
|
||||
*/
|
||||
|
||||
public static function getFolderContentDetails(array $folderContent, $extended, $baseUrl, $fullSlugWithFolder = NULL, $fullSlugWithoutFolder = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL)
|
||||
{
|
||||
$contentDetails = [];
|
||||
@@ -129,13 +131,14 @@ class Folder
|
||||
|
||||
$item->originalName = $key;
|
||||
$item->elementType = 'folder';
|
||||
$item->contains = self::getFolderContentType($name, $fullPath . DIRECTORY_SEPARATOR . $key . DIRECTORY_SEPARATOR . 'index.yaml');
|
||||
$item->status = $status;
|
||||
$item->fileType = $fileType;
|
||||
$item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL;
|
||||
$item->name = implode(" ",$nameParts);
|
||||
$item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name);
|
||||
$item->slug = implode("-",$nameParts);
|
||||
$item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug));
|
||||
$item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug));
|
||||
$item->path = $fullPath . DIRECTORY_SEPARATOR . $key;
|
||||
$item->pathWithoutType = $fullPath . DIRECTORY_SEPARATOR . $key . DIRECTORY_SEPARATOR . 'index';
|
||||
$item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug;
|
||||
@@ -156,6 +159,12 @@ class Folder
|
||||
$item->hide = ($extended[$item->urlRelWoF]['hide'] === true) ? true : false;
|
||||
}
|
||||
|
||||
# sort posts in descending order
|
||||
if($item->contains == "posts")
|
||||
{
|
||||
rsort($name);
|
||||
}
|
||||
|
||||
$item->folderContent = self::getFolderContentDetails($name, $extended, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter);
|
||||
}
|
||||
else
|
||||
@@ -192,7 +201,7 @@ class Folder
|
||||
$item->name = implode(" ",$nameParts);
|
||||
$item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name);
|
||||
$item->slug = implode("-",$nameParts);
|
||||
$item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug));
|
||||
$item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug));
|
||||
$item->path = $fullPath . DIRECTORY_SEPARATOR . $name;
|
||||
$item->pathWithoutType = $fullPath . DIRECTORY_SEPARATOR . $nameWithoutType;
|
||||
$item->key = $iteration;
|
||||
@@ -221,6 +230,44 @@ class Folder
|
||||
return $contentDetails;
|
||||
}
|
||||
|
||||
public static function getFolderContentType($folder, $yamlpath)
|
||||
{
|
||||
# check if folder is empty or has only index.yaml-file. This is a rare case so make it quick and dirty
|
||||
if(count($folder) == 1)
|
||||
{
|
||||
# check if in folder yaml file contains "posts", then return posts
|
||||
$folderyamlpath = getcwd() . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . $yamlpath;
|
||||
|
||||
$fileContent = false;
|
||||
if(file_exists($folderyamlpath))
|
||||
{
|
||||
$fileContent = file_get_contents($folderyamlpath);
|
||||
}
|
||||
|
||||
if($fileContent && strpos($fileContent, 'contains: posts') !== false)
|
||||
{
|
||||
return 'posts';
|
||||
}
|
||||
return 'pages';
|
||||
}
|
||||
else
|
||||
{
|
||||
$file = $folder[0];
|
||||
$nameParts = self::getStringParts($file);
|
||||
$order = count($nameParts) > 1 ? array_shift($nameParts) : NULL;
|
||||
$order = substr($order, 0, 7);
|
||||
|
||||
if(\DateTime::createFromFormat('Ymd', $order) !== FALSE)
|
||||
{
|
||||
return "posts";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "pages";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getItemForUrl($folderContentDetails, $url, $baseUrl, $result = NULL)
|
||||
{
|
||||
|
||||
|
@@ -48,6 +48,16 @@ class Validation
|
||||
return false;
|
||||
}, 'wrong password');
|
||||
|
||||
Validator::addRule('navigation', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
$format = '/[@#^*()=\[\]{};:"\\|,.<>\/]/';
|
||||
if ( preg_match($format, $value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, 'contains special characters');
|
||||
|
||||
Validator::addRule('noSpecialChars', function($field, $value, array $params, array $fields)
|
||||
{
|
||||
$format = '/[!@#$%^&*()_+=\[\]{};\':"\\|,.<>\/?]/';
|
||||
@@ -283,11 +293,12 @@ class Validation
|
||||
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('lengthBetween', 'item_name', 1, 40);
|
||||
# $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())
|
||||
@@ -305,7 +316,8 @@ class Validation
|
||||
$v = new Validator($params);
|
||||
|
||||
$v->rule('required', ['item_name', 'type', 'url']);
|
||||
$v->rule('noSpecialChars', 'item_name');
|
||||
# $v->rule('noSpecialChars', 'item_name');
|
||||
$v->rule('navigation', 'item_name');
|
||||
$v->rule('lengthBetween', 'item_name', 1, 40);
|
||||
$v->rule('in', 'type', ['file', 'folder']);
|
||||
|
||||
@@ -397,7 +409,7 @@ class Validation
|
||||
case "text":
|
||||
$v->rule('noHTML', $fieldName);
|
||||
$v->rule('lengthMax', $fieldName, 500);
|
||||
$v->rule('regex', $fieldName, '/^[\pL0-9_ \-\.\?\!\/\:]*$/u');
|
||||
# $v->rule('regex', $fieldName, '/^[\pL0-9_ \-\.\?\!\/\:]*$/u');
|
||||
break;
|
||||
case "textarea":
|
||||
$v->rule('noHTML', $fieldName);
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
# this check is not in use anymore (was in use to check and store latest version in user settings on page refresh)
|
||||
|
||||
class VersionCheck
|
||||
{
|
||||
function checkVersion($url)
|
||||
@@ -15,11 +17,13 @@ class VersionCheck
|
||||
|
||||
$context = stream_context_create($opts);
|
||||
|
||||
if(false === ($version = @file_get_contents('http://typemill.net/api/v1/checkversion', false, $context)))
|
||||
if(false === ($version = @file_get_contents('https://typemill.net/api/v1/checkversion', false, $context)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$version = json_decode($version);
|
||||
die();
|
||||
|
||||
return $version->system->typemill;
|
||||
}
|
||||
}
|
@@ -110,7 +110,7 @@ class Write
|
||||
return false;
|
||||
}
|
||||
|
||||
public function moveElement($item, $folderPath, $index)
|
||||
public function moveElement($item, $folderPath, $index, $date = null)
|
||||
{
|
||||
$filetypes = array('md', 'txt', 'yaml');
|
||||
|
||||
@@ -159,4 +159,35 @@ class Write
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function renamePost($oldPathWithoutType, $newPathWithoutType)
|
||||
{
|
||||
$filetypes = array('md', 'txt', 'yaml');
|
||||
|
||||
$oldPath = $this->basePath . 'content' . $oldPathWithoutType;
|
||||
$newPath = $this->basePath . 'content' . $newPathWithoutType;
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
@@ -115,6 +115,7 @@ class WriteYaml extends Write
|
||||
'description' => $description,
|
||||
'author' => $author,
|
||||
'created' => date("Y-m-d"),
|
||||
'time' => date("H-i-s"),
|
||||
]
|
||||
];
|
||||
|
||||
@@ -162,4 +163,109 @@ class WriteYaml extends Write
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
|
||||
public function transformPagesToPosts($folder){
|
||||
|
||||
$filetypes = array('md', 'txt', 'yaml');
|
||||
|
||||
foreach($folder->folderContent as $page)
|
||||
{
|
||||
# create old filename without filetype
|
||||
$oldFile = $this->basePath . 'content' . $page->pathWithoutType;
|
||||
|
||||
# set default date
|
||||
$date = date('Y-m-d', time());
|
||||
$time = date('H-i', time());
|
||||
|
||||
$meta = $this->getYaml('content', $page->pathWithoutType . '.yaml');
|
||||
|
||||
if($meta)
|
||||
{
|
||||
# get dates from meta
|
||||
if(isset($meta['meta']['manualdate'])){ $date = $meta['meta']['manualdate']; }
|
||||
elseif(isset($meta['meta']['created'])){ $date = $meta['meta']['created']; }
|
||||
elseif(isset($meta['meta']['modified'])){ $date = $meta['meta']['modified']; }
|
||||
|
||||
# set time
|
||||
if(isset($meta['meta']['time']))
|
||||
{
|
||||
$time = $meta['meta']['time'];
|
||||
}
|
||||
}
|
||||
|
||||
$datetime = $date . '-' . $time;
|
||||
$datetime = implode(explode('-', $datetime));
|
||||
$datetime = substr($datetime,0,12);
|
||||
|
||||
# create new file-name without filetype
|
||||
$newFile = $this->basePath . 'content' . $folder->path . DIRECTORY_SEPARATOR . $datetime . '-' . $page->slug;
|
||||
|
||||
$result = true;
|
||||
|
||||
foreach($filetypes as $filetype)
|
||||
{
|
||||
$oldFilePath = $oldFile . '.' . $filetype;
|
||||
$newFilePath = $newFile . '.' . $filetype;
|
||||
|
||||
#check if file with filetype exists and rename
|
||||
if($oldFilePath != $newFilePath && file_exists($oldFilePath))
|
||||
{
|
||||
if(@rename($oldFilePath, $newFilePath))
|
||||
{
|
||||
$result = $result;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function transformPostsToPages($folder){
|
||||
|
||||
$filetypes = array('md', 'txt', 'yaml');
|
||||
$index = 0;
|
||||
|
||||
foreach($folder->folderContent as $page)
|
||||
{
|
||||
# create old filename without filetype
|
||||
$oldFile = $this->basePath . 'content' . $page->pathWithoutType;
|
||||
|
||||
$order = $index;
|
||||
|
||||
if($index < 10)
|
||||
{
|
||||
$order = '0' . $index;
|
||||
}
|
||||
|
||||
# create new file-name without filetype
|
||||
$newFile = $this->basePath . 'content' . $folder->path . DIRECTORY_SEPARATOR . $order . '-' . $page->slug;
|
||||
|
||||
$result = true;
|
||||
|
||||
foreach($filetypes as $filetype)
|
||||
{
|
||||
$oldFilePath = $oldFile . '.' . $filetype;
|
||||
$newFilePath = $newFile . '.' . $filetype;
|
||||
|
||||
#check if file with filetype exists and rename
|
||||
if($oldFilePath != $newFilePath && file_exists($oldFilePath))
|
||||
{
|
||||
if(@rename($oldFilePath, $newFilePath))
|
||||
{
|
||||
$result = $result;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,6 +13,7 @@ $app->post('/api/v1/article/html', ContentApiController::class . ':getArticleHtm
|
||||
$app->post('/api/v1/article/publish', ContentApiController::class . ':publishArticle')->setName('api.article.publish')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/article/unpublish', ContentApiController::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/article/discard', ContentApiController::class . ':discardArticleChanges')->setName('api.article.discard')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/post', ContentApiController::class . ':createPost')->setName('api.post.create')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/article', ContentApiController::class . ':createArticle')->setName('api.article.create')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/article', ContentApiController::class . ':updateArticle')->setName('api.article.update')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/article', ContentApiController::class . ':deleteArticle')->setName('api.article.delete')->add(new RestrictApiAccess($container['router']));
|
||||
|
@@ -683,6 +683,9 @@ form .large, form .medium, form .small{
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
form .hidden{
|
||||
display: none;
|
||||
}
|
||||
.large img{
|
||||
width: 100%;
|
||||
}
|
||||
@@ -932,7 +935,7 @@ ul.cardInfo{
|
||||
border: 0px;
|
||||
}
|
||||
.cardFields.open{
|
||||
max-height: 2800px;
|
||||
max-height: 5000px;
|
||||
transition: max-height 0.5s ease-in;
|
||||
overflow: hidden;
|
||||
border: 1px;
|
||||
@@ -957,6 +960,19 @@ ul.cardInfo{
|
||||
display: inline-block;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.cardField.full{
|
||||
width: 100%;
|
||||
}
|
||||
.cardField.half{
|
||||
width: 49.5%;
|
||||
float: left;
|
||||
margin-right:0.5%;
|
||||
}
|
||||
.cardField.third{
|
||||
width: 32%;
|
||||
float:left;
|
||||
margin-right: 1%;
|
||||
}
|
||||
.cardField label{
|
||||
width: 100%;
|
||||
margin-top: 0px;
|
||||
@@ -1650,6 +1666,65 @@ button.hdown:hover,button.hdown:focus,button.hdown:active{
|
||||
}
|
||||
|
||||
|
||||
.blog-post{
|
||||
|
||||
}
|
||||
.blog-post svg{
|
||||
color: #ccc;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.blog-post a,
|
||||
.blog-post a:link,
|
||||
.blog-post a:visited{
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding: 0px 15px;
|
||||
margin: 5px 0;
|
||||
border-left: 3px solid;
|
||||
box-sizing: border-box;
|
||||
background-color: #f9f8f6;
|
||||
}
|
||||
.blog-post a:focus,
|
||||
.blog-post a:hover,
|
||||
.blog-post a:active{
|
||||
background-color: #f6f5f3;
|
||||
}
|
||||
.blog-post a.unpublished{
|
||||
border-color: #cc4146;
|
||||
}
|
||||
.blog-post a.published{
|
||||
border-color: #66b0a3;
|
||||
}
|
||||
.blog-post a.modified{
|
||||
border-color: #FFA500;
|
||||
}
|
||||
.blog-post .post-date{
|
||||
float: right;
|
||||
font-size: 0.8em;
|
||||
font-weight: 300;
|
||||
color: #444;
|
||||
}
|
||||
.posts.formWrapper{
|
||||
margin: 20px 0;
|
||||
}
|
||||
.posts input{
|
||||
width: 80%;
|
||||
float: left;
|
||||
}
|
||||
button.post-button{
|
||||
color: #f9f8f6;
|
||||
background: #66b0a3;
|
||||
border: 0px;
|
||||
width: 20%;
|
||||
text-align: center;
|
||||
}
|
||||
button.post-button:hover{
|
||||
background: #4D978A;
|
||||
}
|
||||
|
||||
|
||||
/* .format-bar at the bottom of the page */
|
||||
|
||||
/********************
|
||||
|
@@ -75,6 +75,24 @@
|
||||
|
||||
</section>
|
||||
|
||||
{% if item.elementType == "folder" %}
|
||||
|
||||
<section id="posts" v-if="posts">
|
||||
<div class="posts formWrapper">
|
||||
<input v-model="posttitle" type="text" maxlength="60" name="title" placeholder="add a short title">
|
||||
<button type="button" @click.prevent="createPost()" class="post-button">create new post</button>
|
||||
</div>
|
||||
<div>
|
||||
<single-post
|
||||
v-for="post in posts"
|
||||
v-bind:key="post.keyPath"
|
||||
v-bind:post="post"
|
||||
></single-post>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% include 'editor/publish-controller.twig' %}
|
||||
|
||||
<input id="path" type="hidden" value="{{ item.urlRel }}" required readonly />
|
||||
|
@@ -18,7 +18,6 @@ let typemillUtilities = {
|
||||
|
||||
addYoutubePlayButton: function(element)
|
||||
{
|
||||
console.info(element.parentNode);
|
||||
element.parentNode.classList.add("video-container");
|
||||
|
||||
var youtubePlaybutton = document.createElement("button");
|
||||
@@ -28,12 +27,6 @@ let typemillUtilities = {
|
||||
element.parentNode.appendChild(youtubePlaybutton);
|
||||
},
|
||||
|
||||
start: function(){
|
||||
this.setYoutubeItems();
|
||||
this.addYoutubePlayButtons();
|
||||
this.listenToYoutube();
|
||||
},
|
||||
|
||||
listenToYoutube: function(){
|
||||
document.addEventListener('click', function (event) {
|
||||
|
||||
@@ -58,4 +51,10 @@ let typemillUtilities = {
|
||||
}
|
||||
}, true);
|
||||
},
|
||||
|
||||
start: function(){
|
||||
this.setYoutubeItems();
|
||||
this.addYoutubePlayButtons();
|
||||
this.listenToYoutube();
|
||||
},
|
||||
};
|
@@ -1,13 +1,14 @@
|
||||
const FormBus = new Vue();
|
||||
|
||||
Vue.component('component-text', {
|
||||
props: ['class', 'id', 'description', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'],
|
||||
props: ['class', 'id', 'description', 'maxlength', 'hidden', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'],
|
||||
template: '<div class="large">' +
|
||||
'<label>{{ $t(label) }}</label>' +
|
||||
'<input type="text"' +
|
||||
' :id="id"' +
|
||||
' :maxlength="maxlength"' +
|
||||
' :readonly="readonly"' +
|
||||
' :hidden="hidden"' +
|
||||
' :required="required"' +
|
||||
' :disabled="disabled"' +
|
||||
' :name="name"' +
|
||||
@@ -357,11 +358,10 @@ Vue.component('tab-meta', {
|
||||
|
||||
let meta = new Vue({
|
||||
|
||||
i18n: new VueI18n({
|
||||
locale: language,
|
||||
messages: vuejsLabels
|
||||
}),
|
||||
|
||||
i18n: new VueI18n({
|
||||
locale: language,
|
||||
messages: vuejsLabels
|
||||
}),
|
||||
delimiters: ['${', '}'],
|
||||
el: '#metanav',
|
||||
data: function () {
|
||||
@@ -416,6 +416,16 @@ let meta = new Vue({
|
||||
|
||||
self.formData = response.data.metadata;
|
||||
|
||||
var item = response.data.item;
|
||||
if(item.elementType == "folder" && item.contains == "posts")
|
||||
{
|
||||
posts.posts = item.folderContent;
|
||||
posts.folderid = item.keyPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
posts.posts = false;
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
@@ -449,6 +459,17 @@ let meta = new Vue({
|
||||
{
|
||||
navi.items = response.data.structure;
|
||||
}
|
||||
|
||||
var item = response.data.item;
|
||||
if(item.elementType == "folder" && item.contains == "posts")
|
||||
{
|
||||
posts.posts = item.folderContent;
|
||||
posts.folderid = item.keyPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
posts.posts = false;
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
const navcomponent = Vue.component('navigation', {
|
||||
template: '#navigation-template',
|
||||
props: ['homepage', 'showForm', 'name', 'hide', 'newItem', 'parent', 'active', 'filetype', 'status', 'elementtype', 'element', 'folder', 'level', 'url', 'root', 'freeze'],
|
||||
props: ['homepage', 'showForm', 'name', 'hide', 'newItem', 'parent', 'active', 'filetype', 'status', 'elementtype', 'contains', 'element', 'folder', 'level', 'url', 'root', 'freeze'],
|
||||
data: function () {
|
||||
return {
|
||||
showForm: false,
|
||||
@@ -134,9 +134,9 @@ const navcomponent = Vue.component('navigation', {
|
||||
{
|
||||
publishController.errors.message = false;
|
||||
|
||||
if(this.$root.$data.format.test(this.newItem) || !this.newItem || this.newItem.length > 40)
|
||||
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 40.';
|
||||
publishController.errors.message = 'Special Characters are not allowed. Length between 1 and 60.';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,13 +194,13 @@ let navi = new Vue({
|
||||
data: function () {
|
||||
return {
|
||||
title: "Navigation",
|
||||
items: JSON.parse(document.getElementById("data-navi").dataset.navi),
|
||||
items: navigation,
|
||||
homepage: JSON.parse(document.getElementById("data-navi").dataset.homepage),
|
||||
editormode: document.getElementById("data-navi").dataset.editormode,
|
||||
root: document.getElementById("main").dataset.url,
|
||||
freeze: false,
|
||||
modalWindow: false,
|
||||
format: /[!@#$%^&*()_+=\[\]{};':"\\|,.<>\/?]/,
|
||||
format: /[@#*()=\[\]{};:"\\|,.<>\/]/,
|
||||
folderName: '',
|
||||
showForm: false,
|
||||
newItem: '',
|
||||
|
68
system/author/js/vue-posts.js
Normal file
68
system/author/js/vue-posts.js
Normal file
@@ -0,0 +1,68 @@
|
||||
Vue.component('single-post', {
|
||||
props: ['post'],
|
||||
template: '<div class="blog-post">' +
|
||||
'<a :class="post.status" :href="getUrl(post.urlRelWoF)">' +
|
||||
'<h4><svg class="icon baseline icon-file-text-o"><use xlink:href="#icon-file-text-o"></use></svg> {{ post.name }} <span class="post-date">{{ getDate(post.order) }}</span></h4>' +
|
||||
'</a>' +
|
||||
'</div>',
|
||||
methods: {
|
||||
getUrl: function(posturl){
|
||||
return this.$root.$data.root + '/tm/content/' + this.$root.$data.editormode + posturl;
|
||||
},
|
||||
getDate: function(str){
|
||||
var cleandate = [str.slice(0,4), str.slice(4,6), str.slice(6,8)];
|
||||
return cleandate.join("-");
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let posts = new Vue({
|
||||
el: "#posts",
|
||||
data: function () {
|
||||
return {
|
||||
posts: false,
|
||||
posttitle: '',
|
||||
folderid: false,
|
||||
format: /[@#*()=\[\]{};:"\\|,.<>\/]/,
|
||||
root: document.getElementById("main").dataset.url,
|
||||
editormode: document.getElementById("data-navi").dataset.editormode
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createPost: function(evt){
|
||||
|
||||
publishController.errors.message = false;
|
||||
|
||||
if(this.format.test(this.posttitle) || this.posttitle == '' || this.posttitle.length > 60)
|
||||
{
|
||||
publishController.errors.message = 'Special Characters are not allowed. Length between 1 and 60.';
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
myaxios.post('/api/v1/post',{
|
||||
'folder_id': this.folderid,
|
||||
'item_name': this.posttitle,
|
||||
'type': 'file',
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response) {
|
||||
if(response.data.posts)
|
||||
{
|
||||
self.posts = response.data.posts.folderContent;
|
||||
self.posttitle = '';
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
publishController.errors.message = error.response.data.errors;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
@@ -163,7 +163,7 @@
|
||||
<article>
|
||||
{% block content %}{% endblock %}
|
||||
</article>
|
||||
<footer></footer>
|
||||
<footer></footer>
|
||||
</div>
|
||||
<script src="{{ base_url }}/system/author/js/axios.min.js?20191124"></script>
|
||||
<script>
|
||||
@@ -171,24 +171,26 @@
|
||||
myaxios.defaults.baseURL = "{{ base_url }}";
|
||||
</script>
|
||||
<script src="{{ base_url }}/system/author/js/vue.min.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/vue-i18n.min.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/vue-i18n.min.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/autosize.min.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/sortable.min.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/vuedraggable.umd.min.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/author.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/vue-publishcontroller.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/vue-blox-config.js?20200120"></script>
|
||||
<script>
|
||||
let formatConfig = {{ settings.formats|json_encode() }};
|
||||
let language = {{ settings.language|json_encode() }};
|
||||
let vuejsLabels = {{ settings.vuejsLabels|json_encode() }};
|
||||
let language = {{ settings.language|json_encode() }};
|
||||
let vuejsLabels = {{ settings.vuejsLabels|json_encode() }};
|
||||
let navigation = {{ navigation|json_encode() }};
|
||||
</script>
|
||||
|
||||
{{ assets.renderEditorJS() }}
|
||||
|
||||
<script src="{{ base_url }}/system/author/js/vue-blox.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/vue-meta.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/vue-posts.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/sortable.min.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/vuedraggable.umd.min.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/vue-navi.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/vue-meta.js?20200120"></script>
|
||||
<script src="{{ base_url }}/system/author/js/lazy-video.js?20200120"></script>
|
||||
|
||||
{{ assets.renderJS() }}
|
||||
|
@@ -27,9 +27,15 @@ meta:
|
||||
description: Used as fallback when no manual date is set.
|
||||
created:
|
||||
type: date
|
||||
label: Created at (readonly)
|
||||
label: Created at (read only)
|
||||
readonly: readonly
|
||||
class: medium
|
||||
time:
|
||||
type: text
|
||||
readonly: readonly
|
||||
hidden: true
|
||||
class: hidden
|
||||
pattern: '[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
|
||||
navtitle:
|
||||
type: text
|
||||
label: Navigation Title
|
||||
@@ -45,17 +51,6 @@ meta:
|
||||
# label: Show page to
|
||||
# class: medium
|
||||
# options:
|
||||
# null: null
|
||||
# public: Public (standard)
|
||||
# members: Members only (logged in)
|
||||
# customers: Customers only (paying)
|
||||
# list:
|
||||
# type: select
|
||||
# label: List sub-pages
|
||||
# class: medium
|
||||
# options:
|
||||
# null: null
|
||||
# standard: Standard order
|
||||
# revert: Reverse order
|
||||
# asc: Date ascending
|
||||
# desc: Date descending
|
||||
# customers: Customers only (paying)
|
@@ -1,5 +1,5 @@
|
||||
<nav id="sidebar-menu" class="sidebar-menu--content">
|
||||
<div id="data-navi" data-navi='{{ navigation|json_encode() }}' data-homepage='{{ homepage|json_encode() }}' data-editormode="{{settings.editor}}"></div>
|
||||
<div id="data-navi" data-homepage='{{ homepage|json_encode() }}' data-editormode="{{settings.editor}}"></div>
|
||||
<div id="mobile-menu" class="menu-action">{{ __('Menu') }} <span class="button-arrow"></span></div>
|
||||
<div id="navi" class="content-navi" v-model="freeze" v-cloak>
|
||||
<div class="navi-list">
|
||||
@@ -17,7 +17,7 @@
|
||||
animation="150"
|
||||
:disabled="freeze">
|
||||
<navigation
|
||||
v-for="item in items"
|
||||
v-for="item in items"
|
||||
ref="draggit"
|
||||
:freeze="freeze"
|
||||
:name="item.name"
|
||||
@@ -30,8 +30,9 @@
|
||||
:id="item.keyPath"
|
||||
:key="item.keyPath"
|
||||
:elementtype="item.elementType"
|
||||
:contains="item.contains"
|
||||
:filetype="item.fileType"
|
||||
:status="item.status"
|
||||
:status="item.status"
|
||||
:folder="item.folderContent"
|
||||
></navigation>
|
||||
</draggable>
|
||||
@@ -61,7 +62,7 @@
|
||||
<a v-bind:href="getUrl(root, url)" :class="checkActive(active,parent)"><span class="iconwrapper"><svg class="icon" :class="getIconClass(elementtype, filetype, hide)"><use :xlink:href="getIcon(elementtype, filetype, hide)"></use></svg></span><span :class="getLevel(level)">{{ name }}</span><span class="movewrapper"><span class="movewrapper"><svg class="icon icon-arrows-v"><use xlink:href="#icon-arrows-v"></use></svg></span></a>
|
||||
<draggable v-if="folder" class="navi-list" tag="ul"
|
||||
@start="onStart"
|
||||
@end="onEnd"
|
||||
@end="onEnd"
|
||||
:list="folder"
|
||||
:move="checkMove"
|
||||
group="file"
|
||||
@@ -69,6 +70,7 @@
|
||||
:disabled="freeze">
|
||||
<navigation
|
||||
v-for="item in folder"
|
||||
v-if="contains == 'pages'"
|
||||
ref="draggit"
|
||||
:freeze="freeze"
|
||||
:name="item.name"
|
||||
@@ -82,11 +84,12 @@
|
||||
:key="item.keyPath"
|
||||
:filetype="item.fileType"
|
||||
:status="item.status"
|
||||
:elementtype="item.elementType"
|
||||
:elementtype="item.elementType"
|
||||
:contains="item.contains"
|
||||
:folder="item.folderContent"
|
||||
></navigation>
|
||||
</draggable>
|
||||
<ul v-if="folder" class="navi-list">
|
||||
<ul v-if="folder && contains == 'pages'" class="navi-list">
|
||||
<li class="navi-item file">
|
||||
<span class="iconwrapper">
|
||||
<svg class="icon icon-plus"><use xlink:href="#icon-plus"></use></svg>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div class="cardField{{ errors[itemName][field.name] ? ' error' : '' }}">
|
||||
<div class="cardField{{ errors[itemName][field.name] ? ' error' : '' }}{{field.fieldsize ? ' ' ~ field.fieldsize : ''}}">
|
||||
|
||||
<label for="{{ itemName }}[{{ field.name }}]">{{ __( field.getLabel() ) }}
|
||||
{% if field.getAttribute('required') %}<strong><abbr title="{{ __('required') }}">*</abbr></strong>{% endif %}
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<div class="cardInner cardHead">
|
||||
|
||||
{% if theme.img %}
|
||||
<img src="{{ base_url() }}/themes/{{themeName}}/{{themeName}}.jpg" />
|
||||
<img src="{{ base_url() }}/themes/{{themeName}}/{{ theme.img }}" />
|
||||
{% else %}
|
||||
<div class="no-preview">{{ __('No Preview') }}</div>
|
||||
{% endif %}
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<div class="cardDescription">
|
||||
<h2>{{ themeName }}</h2>
|
||||
<p>{{ __('TYPEMILL_DESCRIPTION') }}</p>
|
||||
<p>{{ theme.description }}</p>
|
||||
<ul class="cardInfo">
|
||||
<li>{{ theme.version ? theme.version : __('Unknown') }}</li><li>
|
||||
{{ theme.licence ? theme.licence : __('Unkown') }}</li><li>
|
||||
|
@@ -129,7 +129,6 @@ $container['assets'] = function($c)
|
||||
return new \Typemill\Assets($c['request']->getUri()->getBaseUrl());
|
||||
};
|
||||
|
||||
|
||||
/************************
|
||||
* DECIDE FOR SESSION *
|
||||
************************/
|
||||
|
Reference in New Issue
Block a user