1
0
mirror of https://github.com/typemill/typemill.git synced 2025-01-17 05:18:19 +01:00

Version 1.4.9-1.5.0: Refactor controller logic

This commit is contained in:
trendschau 2021-10-06 21:39:16 +02:00
parent f279afe888
commit 5c4a7c9f6a
31 changed files with 1037 additions and 1351 deletions

View File

@ -1,5 +0,0 @@
127.0.0.1;2021-09-26 12:01:24;wrong captcha http://localhost/typemill/tm/recoverpw
127.0.0.1;2021-09-26 12:06:16;wrong captcha http://localhost/typemill/tm/recoverpw
127.0.0.1;2021-09-27 23:44:57;wrong captcha http://localhost/typemill/tm/recoverpw
127.0.0.1;2021-09-27 23:51:19;wrong captcha http://localhost/typemill/tm/recoverpw
127.0.0.1;2021-09-27 23:51:30;wrong captcha http://localhost/typemill/tm/recoverpw

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,59 +0,0 @@
<?php
namespace Typemill\Controllers;
/* Use the slim-container */
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Views\Twig;
use Psr\Container\ContainerInterface;
use Typemill\Events\OnPageReady;
abstract class Controller
{
protected $c;
protected $settings;
public function __construct(ContainerInterface $c)
{
$this->c = $c;
$this->settings = $this->c->get('settings');
$this->c->dispatcher->dispatch('onTwigLoaded');
}
# frontend rendering
protected function render($response, $route, $data)
{
# why commented this out??
$data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
if(isset($_SESSION['old']))
{
unset($_SESSION['old']);
}
$response = $response->withoutHeader('Server');
$response = $response->withAddedHeader('X-Powered-By', 'Typemill');
if(!isset($this->settings['headersoff']) or !$this->settings['headersoff'])
{
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
$response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade');
if($this->c->request->getUri()->getScheme() == 'https')
{
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
}
}
return $this->c->view->render($response, $route, $data);
}
protected function render404($response, $data = NULL)
{
return $this->c->view->render($response->withStatus(404), '/404.twig', $data);
}
}

View File

@ -2,42 +2,22 @@
namespace Typemill\Controllers;
use Slim\Http\Request;
use Slim\Http\Response;
use Psr\Container\ContainerInterface;
use Typemill\Models\Validation;
use Typemill\Models\Folder;
use Typemill\Models\Write;
use Typemill\Models\WriteCache;
use Typemill\Models\WriteYaml;
use Typemill\Models\WriteMeta;
use Typemill\Models\WriteYaml;
use Typemill\Models\Validation;
abstract class ContentController
{
# holds the pimple container
protected $c;
class ControllerAuthor extends ControllerShared
{
# holds the params from request
protected $params;
# holds the slim-uri-object
# holds the slim-uri-object from request
protected $uri;
# holds the errors to output in frontend
protected $errors = false;
# holds a write object to write files
protected $write;
# holds the structure of content folder as a serialized array of objects
protected $structure;
# holds the name of the structure-file with drafts for author environment
protected $structureDraftName;
# holds the name of the structure-file without drafts for live site
protected $structureLiveName;
# holds informations about the homepage
protected $homepage;
@ -56,72 +36,33 @@ abstract class ContentController
# holds the ownership (my content or not my content)
protected $mycontent = false;
public function __construct(ContainerInterface $c)
{
$this->c = $c;
$this->settings = $this->c->get('settings');
$this->structureLiveName = 'structure.txt';
$this->structureDraftName = 'structure-draft.txt';
$this->c->dispatcher->dispatch('onTwigLoaded');
}
# admin ui rendering
protected function render($response, $route, $data)
{
if(isset($_SESSION['old']))
{
unset($_SESSION['old']);
}
$response = $response->withoutHeader('Server');
$response = $response->withAddedHeader('X-Powered-By', 'Typemill');
if(!isset($this->settings['headersoff']) or !$this->settings['headersoff'])
{
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
$response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade');
if($this->c->request->getUri()->getScheme() == 'https')
{
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
}
}
return $this->c->view->render($response, $route, $data);
}
protected function render404($response, $data = NULL)
{
return $this->c->view->render($response->withStatus(404), '/404.twig', $data);
}
protected function renderIntern404($response, $data = NULL)
{
return $this->c->view->render($response->withStatus(404), '/intern404.twig', $data);
}
# author
protected function getValidator()
{
return new Validation();
}
# author
protected function validateEditorInput()
{
$validate = new Validation();
$vResult = $validate->editorInput($this->params);
$validate = new Validation();
$vResult = $validate->editorInput($this->params);
if(is_array($vResult))
{
$message = reset($vResult);
$this->errors = ['errors' => $vResult];
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
$message = reset($vResult);
$this->errors = ['errors' => $vResult];
if(isset($message[0])){
$this->errors['errors']['message'] = $message[0];
}
return false;
}
return true;
}
# author
protected function validateBlockInput()
{
$validate = new Validation();
@ -131,12 +72,18 @@ abstract class ContentController
{
$message = reset($vResult);
$this->errors = ['errors' => $vResult];
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
if(isset($message[0]))
{
$this->errors['errors']['message'] = $message[0];
}
return false;
}
return true;
}
# author
protected function validateNavigationSort()
{
$validate = new Validation();
@ -146,12 +93,17 @@ abstract class ContentController
{
$message = reset($vResult);
$this->errors = ['errors' => $vResult];
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
if(isset($message[0])){
$this->errors['errors']['message'] = $message[0];
}
return false;
}
return true;
}
# author
protected function validateNaviItem()
{
$validate = new Validation();
@ -161,12 +113,18 @@ abstract class ContentController
{
$message = reset($vResult);
$this->errors = ['errors' => $vResult];
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
if(isset($message[0]))
{
$this->errors['errors']['message'] = $message[0];
}
return false;
}
return true;
}
# author
protected function validateBaseNaviItem()
{
$validate = new Validation();
@ -176,110 +134,18 @@ abstract class ContentController
{
$message = reset($vResult);
$this->errors = ['errors' => $vResult];
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
if(isset($message[0]))
{
$this->errors['errors']['message'] = $message[0];
}
return false;
}
return true;
}
protected function setStructure($draft = false, $cache = true)
{
# set initial structure to false
$structure = false;
# name of structure-file for draft or live
$filename = $draft ? $this->structureDraftName : $this->structureLiveName;
# set variables and objects
$this->write = new writeCache();
# check, if cached structure is still valid
if($cache && $this->write->validate('cache', 'lastCache.txt', 600))
{
# get the cached structure
$structure = $this->write->getCache('cache', $filename);
}
# if no structure was found or cache is deactivated
if(!$structure)
{
# scan the content of the folder
$structure = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft);
# if there is content, then get the content details
if(count($structure) > 0)
{
# get the extended structure files with changes like navigation title or hidden pages
$yaml = new writeYaml();
$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
# create an array of object with the whole content of the folder and changes from extended file
$structure = Folder::getFolderContentDetails($structure, $extended, $this->uri->getBaseUrl(), $this->uri->getBasePath());
}
# cache navigation
$this->write->updateCache('cache', $filename, 'lastCache.txt', $structure);
}
$this->structure = $structure;
return true;
}
protected function renameExtended($item, $newFolder)
{
# get the extended structure files with changes like navigation title or hidden pages
$yaml = new writeYaml();
$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
if(isset($extended[$item->urlRelWoF]))
{
$newUrl = $newFolder->urlRelWoF . '/' . $item->slug;
$entry = $extended[$item->urlRelWoF];
unset($extended[$item->urlRelWoF]);
$extended[$newUrl] = $entry;
$yaml->updateYaml('cache', 'structure-extended.yaml', $extended);
}
return true;
}
protected function deleteFromExtended()
{
# get the extended structure files with changes like navigation title or hidden pages
$yaml = new writeYaml();
$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
if($this->item->elementType == "file" && isset($extended[$this->item->urlRelWoF]))
{
unset($extended[$this->item->urlRelWoF]);
$yaml->updateYaml('cache', 'structure-extended.yaml', $extended);
}
if($this->item->elementType == "folder")
{
$changed = false;
# delete all entries with that folder url
foreach($extended as $url => $entries)
{
if( strpos($url, $this->item->urlRelWoF) !== false )
{
$changed = true;
unset($extended[$url]);
}
}
if($changed)
{
$yaml->updateYaml('cache', 'structure-extended.yaml', $extended);
}
}
}
# this is only set by content backend controller
# only backoffice
protected function setHomepage($args)
{
$contentFolder = Folder::scanFolderFlat($this->settings['rootPath'] . $this->settings['contentFolder']);
@ -308,13 +174,14 @@ abstract class ContentController
$this->homepage = ['status' => $status, 'active' => $active];
}
# only backoffice
protected function setItem()
{
# home is only set by backend controller, not by api calls
$home = isset($this->homepage['active']) ? $this->homepage['active'] : false;
# search for the url in the structure
$item = Folder::getItemForUrl($this->structure, $this->params['url'], $this->uri->getBaseUrl(), NULL, $home);
$item = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl(), NULL, $home);
if($item)
{
@ -327,12 +194,14 @@ abstract class ContentController
return false;
}
# only backoffice
# determine if you want to write to published file (md) or to draft (txt)
protected function setItemPath($fileType)
{
$this->path = $this->item->pathWithoutType . '.' . $fileType;
}
# only backoffice
protected function setPublishStatus()
{
$this->item->published = false;
@ -360,7 +229,56 @@ abstract class ContentController
$this->item->fileType = "txt";
}
}
# only backoffice
protected function setContent()
{
# if the file exists
if($this->item->published OR $this->item->drafted)
{
$content = $this->writeCache->getFile($this->settings['contentFolder'], $this->path);
if($this->item->fileType == 'txt')
{
# decode the json-draft to an array
$content = json_decode($content);
}
}
elseif($this->item->elementType == "folder")
{
$content = '';
}
else
{
$this->errors = ['errors' => ['message' => 'requested file not found']];
return false;
}
$this->content = $content;
return true;
}
# only backoffice
protected function checkContentOwnership()
{
# get page meta
$writeMeta = new writeMeta();
$pagemeta = $writeMeta->getPageMeta($this->settings, $this->item);
# check ownership
if(isset($pagemeta['meta']['owner']) && $pagemeta['meta']['owner'] && $pagemeta['meta']['owner'] !== '' )
{
$allowedusers = array_map('trim', explode(",", $pagemeta['meta']['owner']));
if(isset($_SESSION['user']) && in_array($_SESSION['user'], $allowedusers))
{
$this->mycontent = true;
return true;
}
}
return false;
}
# only backoffice
protected function deleteContentFiles($fileTypes, $folder = false)
{
$basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
@ -381,6 +299,7 @@ abstract class ContentController
return true;
}
# only backoffice
protected function deleteContentFolder()
{
$basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
@ -418,50 +337,4 @@ abstract class ContentController
}
return false;
}
protected function setContent()
{
# if the file exists
if($this->item->published OR $this->item->drafted)
{
$content = $this->write->getFile($this->settings['contentFolder'], $this->path);
if($this->item->fileType == 'txt')
{
# decode the json-draft to an array
$content = json_decode($content);
}
}
elseif($this->item->elementType == "folder")
{
$content = '';
}
else
{
$this->errors = ['errors' => ['message' => 'requested file not found']];
return false;
}
$this->content = $content;
return true;
}
protected function checkContentOwnership()
{
# get page meta
$writeMeta = new writeMeta();
$pagemeta = $writeMeta->getPageMeta($this->settings, $this->item);
# check ownership
if(isset($pagemeta['meta']['owner']) && $pagemeta['meta']['owner'] && $pagemeta['meta']['owner'] !== '' )
{
$allowedusers = array_map('trim', explode(",", $pagemeta['meta']['owner']));
if(isset($_SESSION['user']) && in_array($_SESSION['user'], $allowedusers))
{
$this->mycontent = true;
return true;
}
}
return false;
}
}

View File

@ -5,19 +5,15 @@ namespace Typemill\Controllers;
use Slim\Http\Request;
use Slim\Http\Response;
use Typemill\Models\Folder;
use Typemill\Models\Write;
use Typemill\Models\WriteYaml;
use Typemill\Models\WriteMeta;
use Typemill\Models\WriteCache;
use Typemill\Extensions\ParsedownExtension;
use Typemill\Events\OnPagePublished;
use Typemill\Events\OnPageUnpublished;
use Typemill\Events\OnPageDeleted;
use Typemill\Events\OnPageSorted;
use \URLify;
class ArticleApiController extends ContentController
class ControllerAuthorArticleApi extends ControllerAuthor
{
public function publishArticle(Request $request, Response $response, $args)
{
@ -38,7 +34,7 @@ class ArticleApiController extends ContentController
}
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
# set information for homepage
$this->setHomepage($args = false);
@ -87,16 +83,22 @@ class ArticleApiController extends ContentController
$this->setItemPath('md');
# update the file
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $this->content))
if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $this->content))
{
# update the file
$delete = $this->deleteContentFiles(['txt']);
# update the internal structure
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
# update the public structure
$this->setStructure($draft = false, $cache = false);
$this->setFreshStructureLive();
# update the navigation
$this->setFreshNavigation();
# update the sitemap
$this->updateSitemap();
# complete the page meta if title or description not set
$writeMeta = new WriteMeta();
@ -127,7 +129,7 @@ class ArticleApiController extends ContentController
}
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
# set information for homepage
$this->setHomepage($args = false);
@ -151,7 +153,7 @@ class ArticleApiController extends ContentController
# check if draft exists, if not, create one.
if(!$this->item->drafted)
{
# set path for the file (or folder)
# set path for the live file (or folder)
$this->setItemPath('md');
# set content of markdown-file
@ -166,11 +168,11 @@ class ArticleApiController extends ContentController
# encode the content into json
$contentJson = json_encode($contentArray);
# set path for the file (or folder)
# set path for the draft file (or folder)
$this->setItemPath('txt');
/* update the file */
if(!$this->write->writeFile($this->settings['contentFolder'], $this->path, $contentJson))
# update the file
if(!$this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $contentJson))
{
return $response->withJson(['errors' => ['message' => 'Could not create a draft of the page. Please check if the folder is writable']], 404);
}
@ -189,16 +191,22 @@ class ArticleApiController extends ContentController
}
}
# update the file
# delete the live file
$delete = $this->deleteContentFiles(['md']);
if($delete)
{
# update the internal structure
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
# update the live structure
$this->setStructure($draft = false, $cache = false);
$this->setFreshStructureLive();
# update the navigation
$this->setFreshNavigation();
# update the sitemap
$this->updateSitemap();
# dispatch event
$this->c->dispatcher->dispatch('onPageUnpublished', new OnPageUnpublished($this->item));
@ -224,7 +232,7 @@ class ArticleApiController extends ContentController
}
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
# set information for homepage
$this->setHomepage($args = false);
@ -242,9 +250,6 @@ class ArticleApiController extends ContentController
}
}
# remove the unpublished changes
$delete = $this->deleteContentFiles(['txt']);
# set redirect url to edit page
$url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'];
if(isset($this->item->urlRelWoF) && $this->item->urlRelWoF != '/' )
@ -258,13 +263,13 @@ class ArticleApiController extends ContentController
if($delete)
{
# update the backend structure
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
return $response->withJson(['data' => $this->structure, 'errors' => false, 'url' => $url], 200);
return $response->withJson(['data' => $this->structureDraft, 'errors' => false, 'url' => $url], 200);
}
else
{
return $response->withJson(['data' => $this->structure, 'errors' => $this->errors], 404);
return $response->withJson(['data' => $this->structureDraft, 'errors' => $this->errors], 404);
}
}
@ -284,7 +289,7 @@ class ArticleApiController extends ContentController
$url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'];
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
# set information for homepage
$this->setHomepage($args = false);
@ -317,7 +322,7 @@ class ArticleApiController extends ContentController
if(count($this->item->keyPathArray) > 1)
{
# get the parent item
$parentItem = Folder::getParentItem($this->structure, $this->item->keyPathArray);
$parentItem = Folder::getParentItem($this->structureDraft, $this->item->keyPathArray);
if($parentItem)
{
@ -327,18 +332,24 @@ class ArticleApiController extends ContentController
}
# update the live structure
$this->setStructure($draft = false, $cache = false);
$this->setFreshStructureDraft();
# update the backend structure
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureLive();
# check if page is in extended structure and delete it
$this->deleteFromExtended();
# update the navigation
$this->setFreshNavigation();
# update the sitemap
$this->updateSitemap();
# dispatch event
$this->c->dispatcher->dispatch('onPageDeleted', new OnPageDeleted($this->item));
return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url), 200);
return $response->withJson(array('data' => $this->structureDraft, 'errors' => false, 'url' => $url), 200);
}
else
{
@ -362,7 +373,7 @@ class ArticleApiController extends ContentController
if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); }
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
# set information for homepage
$this->setHomepage($args = false);
@ -380,7 +391,7 @@ class ArticleApiController extends ContentController
}
}
# set path for the file (or folder)
# set draft path for the file (or folder)
$this->setItemPath('txt');
# merge title with content for complete markdown document
@ -395,11 +406,11 @@ class ArticleApiController extends ContentController
# encode the content into json
$contentJson = json_encode($contentArray);
/* update the file */
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $contentJson))
# update the file
if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $contentJson))
{
# update the internal structure
$this->setStructure($draft = true, $cache = false);
# update the internal structure to show that this page has drafted changes now
$this->setFreshStructureDraft();
return $response->withJson(['success'], 200);
}
@ -423,14 +434,14 @@ class ArticleApiController extends ContentController
return $response->withJson(array('data' => false, 'errors' => 'You are not allowed to update content.'), 403);
}
# validate input
# validate input 1: check if valid characters
if(!preg_match("/^[a-z0-9\-]*$/", $this->params['slug']))
{
return $response->withJson(['errors' => ['message' => 'the slug contains invalid characters.' ]],422);
}
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
# set information for homepage
$this->setHomepage($args = false);
@ -438,7 +449,7 @@ class ArticleApiController extends ContentController
# set item
if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
# validate input part 2
# validate input part 2: check if slug has changed or is empty
if($this->params['slug'] == $this->item->slug OR $this->params['slug'] == '')
{
return $response->withJson(['errors' => ['message' => 'the slug is empty or the same as the old one.' ]],422);
@ -460,19 +471,54 @@ class ArticleApiController extends ContentController
# create the new file name with the updated slug
$newPathWithoutType = $pathWithoutFile . $this->item->order . '-' . $this->params['slug'];
# rename the file
$write = new WriteCache();
$write->renamePost($this->item->pathWithoutType, $newPathWithoutType);
# delete the cache
$error = $write->deleteCacheFiles($dir);
if($error)
# validate input part 3: check if name is taken already
$parentKey = $this->item->keyPathArray;
array_pop($parentKey);
if(!empty($parentKey))
{
return $response->withJson(['errors' => $error], 500);
$parentFolder = Folder::getItemWithKeyPath($this->structureDraft, $parentKey);
foreach($parentFolder->folderContent as $item)
{
if($item->slug == $this->params['slug'])
{
return $response->withJson(['errors' => ['message' => 'There is already a page with that slug' ]],422);
}
}
}
else
{
foreach($this->structureDraft as $baseItem)
{
if($baseItem->slug == $this->params['slug'])
{
return $response->withJson(['errors' => ['message' => 'There is already a page with that slug' ]],422);
}
}
}
# recreates the cache for structure, structure-extended and navigation
$write->getFreshStructure($pathToContent, $this->uri);
# rename the file
if($this->item->elementType == 'file')
{
$this->writeCache->renamePost($this->item->pathWithoutType, $newPathWithoutType);
}
elseif($this->item->elementType == 'folder')
{
$this->writeCache->renameFile('content', $this->item->path, $newPathWithoutType);
}
# delete the cache
$error = $this->writeCache->deleteCacheFiles($dir);
if($error)
{
return $response->withJson(['errors' => $errors], 500);
}
# recreates the cache for structure, structure-extended, navigation, sitemap
$this->setFreshStructureDraft();
$this->setFreshStructureLive();
$this->setFreshNavigation();
$this->updateSitemap();
$newUrlRel = str_replace($this->item->slug, $this->params['slug'], $this->item->urlRelWoF);
@ -497,10 +543,10 @@ class ArticleApiController extends ContentController
$url = false;
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
# validate input
if(!$this->validateNavigationSort()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Data not valid. Please refresh the page and try again.', 'url' => $url), 422); }
if(!$this->validateNavigationSort()){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'Data not valid. Please refresh the page and try again.', 'url' => $url), 422); }
# get the ids (key path) for item, old folder and new folder
$itemKeyPath = explode('.', $this->params['item_id']);
@ -508,9 +554,9 @@ class ArticleApiController extends ContentController
$parentKeyTo = explode('.', $this->params['parent_id_to']);
# get the item from structure
$item = Folder::getItemWithKeyPath($this->structure, $itemKeyPath);
$item = Folder::getItemWithKeyPath($this->structureDraft, $itemKeyPath);
if(!$item){ return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); }
if(!$item){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); }
# needed for acl check
$this->item = $item;
@ -521,7 +567,7 @@ class ArticleApiController extends ContentController
# check ownership. This code should nearly never run, because there is no button/interface to trigger it.
if(!$this->checkContentOwnership())
{
return $response->withJson(array('data' => $this->structure, 'errors' => 'You are not allowed to move that content.'), 403);
return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'You are not allowed to move that content.'), 403);
}
}
@ -531,12 +577,12 @@ class ArticleApiController extends ContentController
# create empty and default values so that the logic below still works
$newFolder = new \stdClass();
$newFolder->path = '';
$folderContent = $this->structure;
$folderContent = $this->structureDraft;
}
else
{
# get the target folder from structure
$newFolder = Folder::getItemWithKeyPath($this->structure, $parentKeyTo);
$newFolder = Folder::getItemWithKeyPath($this->structureDraft, $parentKeyTo);
# get the content of the target folder
$folderContent = $newFolder->folderContent;
@ -570,43 +616,42 @@ class ArticleApiController extends ContentController
# initialize index
$index = 0;
# initialise write object
$write = new Write();
# iterate through the whole content of the new folder to rename the files
$writeError = false;
foreach($folderContent as $folderItem)
{
if(!$write->moveElement($folderItem, $newFolder->path, $index))
if(!$this->writeCache->moveElement($folderItem, $newFolder->path, $index))
{
$writeError = true;
}
$index++;
}
if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => ['message' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.'], 'url' => $url), 404); }
if($writeError){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => ['message' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.'], 'url' => $url), 404); }
# update the structure for editor
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
# get item for url and set it active again
if(isset($this->params['url']))
{
$activeItem = Folder::getItemForUrl($this->structure, $this->params['url'], $this->uri->getBaseUrl());
$activeItem = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl());
}
# keep the internal structure for response
$internalStructure = $this->structure;
# update the structure for website
$this->setStructure($draft = false, $cache = false);
$this->setFreshStructureLive();
# update the navigation
$this->setFreshNavigation();
# update the sitemap
$this->updateSitemap();
# dispatch event
$this->c->dispatcher->dispatch('onPageSorted', new OnPageSorted($this->params));
return $response->withJson(array('data' => $internalStructure, 'errors' => false, 'url' => $url));
return $response->withJson(array('data' => $this->structureDraft, 'errors' => false, 'url' => $url));
}
public function createPost(Request $request, Response $response, $args)
{
# get params from call
@ -623,41 +668,41 @@ class ArticleApiController extends ContentController
$url = false;
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
if(!$this->setStructureDraft()){ 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' => ['message' => 'Special Characters not allowed. Length between 1 and 60 chars.'], 'url' => $url), 422); }
if(!$this->validateNaviItem()){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => ['message' => '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);
$folder = Folder::getItemWithKeyPath($this->structureDraft, $folderKeyPath);
if(!$folder){ return $response->withJson(array('data' => $this->structure, 'errors' => ['message' => 'We could not find this page. Please refresh and try again.'], 'url' => $url), 404); }
if(!$folder){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => ['message' => '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']));
$slug = Folder::createSlug($this->params['item_name'], $this->settings);
$namePath = date("YmdHi") . '-' . $slug;
$folderPath = 'content' . $folder->path;
$content = json_encode(['# ' . $name, 'Content']);
# initialise write object
$write = new WriteYaml();
$writeYaml = new WriteYaml();
# check, if name exists
if($write->checkFile($folderPath, $namePath . '.txt') OR $write->checkFile($folderPath, $namePath . '.md'))
if($writeYaml->checkFile($folderPath, $namePath . '.txt') OR $writeYaml->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);
return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404);
}
if(!$write->writeFile($folderPath, $namePath . '.txt', $content))
if(!$writeYaml->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);
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);
}
# get extended structure
$extended = $write->getYaml('cache', 'structure-extended.yaml');
$extended = $writeYaml->getYaml('cache', 'structure-extended.yaml');
# create the url for the item
$urlWoF = $folder->urlRelWoF . '/' . $slug;
@ -666,17 +711,17 @@ class ArticleApiController extends ContentController
$extended[$urlWoF] = ['hide' => false, 'navtitle' => $name];
# store the extended structure
$write->updateYaml('cache', 'structure-extended.yaml', $extended);
$writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended);
# update the structure for editor
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
$folder = Folder::getItemWithKeyPath($this->structure, $folderKeyPath);
$folder = Folder::getItemWithKeyPath($this->structureYaml, $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));
return $response->withJson(array('posts' => $folder, $this->structureDraft, 'errors' => false, 'url' => $url));
}
public function createArticle(Request $request, Response $response, $args)
@ -695,36 +740,31 @@ class ArticleApiController extends ContentController
$url = false;
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
if(!$this->setStructureDraft()){ 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); }
if(!$this->validateNaviItem()){ return $response->withJson(array('data' => $this->structureDraft, '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);
$folder = Folder::getItemWithKeyPath($this->structureDraft, $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); }
if(!$folder){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); }
# Rename all files within the folder to make sure, that namings and orders are correct
# 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;
$slug = Folder::createSlug($this->params['item_name'], $this->settings);
# initialize index
$index = 0;
# initialise write object
$write = new WriteYaml();
$writeYaml = new WriteYaml();
# iterate through the whole content of the new folder
$writeError = false;
@ -734,17 +774,17 @@ class ArticleApiController extends ContentController
# check, if the same name as new item, then return an error
if($folderItem->slug == $slug)
{
return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404);
return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404);
}
if(!$write->moveElement($folderItem, $folder->path, $index))
if(!$writeYaml->moveElement($folderItem, $folder->path, $index))
{
$writeError = true;
}
$index++;
}
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); }
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;
@ -755,18 +795,18 @@ class ArticleApiController extends ContentController
if($this->params['type'] == 'file')
{
if(!$write->writeFile($folderPath, $namePath . '.txt', $content))
if(!$writeYaml->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);
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(!$write->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath))
if(!$writeYaml->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath))
{
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);
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);
}
$write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content);
$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;
@ -774,7 +814,7 @@ class ArticleApiController extends ContentController
}
# get extended structure
$extended = $write->getYaml('cache', 'structure-extended.yaml');
$extended = $writeYaml->getYaml('cache', 'structure-extended.yaml');
# create the url for the item
$urlWoF = $folder->urlRelWoF . '/' . $slug;
@ -783,21 +823,21 @@ class ArticleApiController extends ContentController
$extended[$urlWoF] = ['hide' => false, 'navtitle' => $name];
# store the extended structure
$write->updateYaml('cache', 'structure-extended.yaml', $extended);
$writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended);
# update the structure for editor
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
# get item for url and set it active again
if(isset($this->params['url']))
{
$activeItem = Folder::getItemForUrl($this->structure, $this->params['url'], $this->uri->getBaseUrl());
$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->structure, 'errors' => false, 'url' => $url));
return $response->withJson(array('data' => $this->structureDraft, 'errors' => false, 'url' => $url));
}
public function createBaseItem(Request $request, Response $response, $args)
@ -816,67 +856,61 @@ class ArticleApiController extends ContentController
$url = false;
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
# validate input
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;
if(!$this->validateBaseNaviItem()){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'Special Characters not allowed. Length between 1 and 20 chars.', 'url' => $url), 422); }
$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']));
$slug = Folder::createSlug($this->params['item_name'], $this->settings);
# initialize index
$index = 0;
$index = 0;
# initialise write object
$write = new WriteYaml();
$writeYaml = new WriteYaml();
# iterate through the whole content of the new folder
$writeError = false;
foreach($this->structure as $item)
foreach($this->structureDraft as $item)
{
# check, if the same name as new item, then return an error
if($item->slug == $slug)
{
return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 422);
return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 422);
}
if(!$write->moveElement($item, '', $index))
if(!$writeYaml->moveElement($item, '', $index))
{
$writeError = true;
}
$index++;
}
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), 422); }
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), 422); }
# add prefix number to the name
$namePath = $index > 9 ? $index . '-' . $slug : '0' . $index . '-' . $slug;
$folderPath = 'content';
# create default content
# $content = json_encode(['# Add Title', 'Add Content']);
$content = json_encode(['# ' . $name, 'Content']);
if($this->params['type'] == 'file')
{
if(!$write->writeFile($folderPath, $namePath . '.txt', $content))
if(!$writeYaml->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), 422);
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), 422);
}
}
elseif($this->params['type'] == 'folder')
{
if(!$write->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath))
if(!$this->writeCache->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath))
{
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), 422);
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), 422);
}
$write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content);
$writeYaml->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;
@ -884,7 +918,7 @@ class ArticleApiController extends ContentController
# get extended structure
$extended = $write->getYaml('cache', 'structure-extended.yaml');
$extended = $writeYaml->getYaml('cache', 'structure-extended.yaml');
# create the url for the item
$urlWoF = '/' . $slug;
@ -893,20 +927,21 @@ class ArticleApiController extends ContentController
$extended[$urlWoF] = ['hide' => false, 'navtitle' => $name];
# store the extended structure
$write->updateYaml('cache', 'structure-extended.yaml', $extended);
$writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended);
# update the structure for editor
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
# get item for url and set it active again
if(isset($this->params['url']))
{
$activeItem = Folder::getItemForUrl($this->structure, $this->params['url'], $this->uri->getBaseUrl());
$activeItem = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl());
}
return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url));
return $response->withJson(array('data' => $this->structureDraft, 'errors' => false, 'url' => $url));
}
# get the backend navigation
public function getNavigation(Request $request, Response $response, $args)
{
# get params from call
@ -914,7 +949,7 @@ class ArticleApiController extends ContentController
$this->uri = $request->getUri()->withUserInfo('');
# set structure
if(!$this->setStructure($draft = true, $cache = false)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
# set information for homepage
$this->setHomepage($args = false);
@ -922,10 +957,10 @@ class ArticleApiController extends ContentController
# get item for url and set it active again
if(isset($this->params['url']))
{
$activeItem = Folder::getItemForUrl($this->structure, $this->params['url'], $this->uri->getBaseUrl());
$activeItem = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl());
}
return $response->withJson(array('data' => $this->structure, 'homepage' => $this->homepage, 'errors' => false));
return $response->withJson(array('data' => $this->structureDraft, 'homepage' => $this->homepage, 'errors' => false));
}
public function getArticleMarkdown(Request $request, Response $response, $args)
@ -941,7 +976,7 @@ class ArticleApiController extends ContentController
}
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
# set information for homepage
$this->setHomepage($args = false);
@ -1006,7 +1041,7 @@ class ArticleApiController extends ContentController
}
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
# set information for homepage
$this->setHomepage($args = false);

View File

@ -4,17 +4,10 @@ namespace Typemill\Controllers;
use Slim\Http\Request;
use Slim\Http\Response;
use Typemill\Models\Folder;
use Typemill\Models\Write;
use Typemill\Models\WriteYaml;
use Typemill\Models\ProcessImage;
use Typemill\Models\ProcessFile;
use Typemill\Extensions\ParsedownExtension;
use \URLify;
class BlockApiController extends ContentController
class ControllerAuthorBlockApi extends ControllerAuthor
{
public function addBlock(Request $request, Response $response, $args)
{
/* get params from call */
@ -31,7 +24,7 @@ class BlockApiController extends ContentController
if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); }
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
$this->setHomepage($args = false);
@ -111,10 +104,10 @@ class BlockApiController extends ContentController
$this->setItemPath('txt');
/* update the file */
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
{
# update the internal structure
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
$this->content = $pageMarkdown;
}
else
@ -229,7 +222,7 @@ class BlockApiController extends ContentController
if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); }
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
$this->setHomepage($args = false);
@ -312,11 +305,11 @@ class BlockApiController extends ContentController
# set path for the file (or folder)
$this->setItemPath('txt');
/* update the file */
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
# update the file
if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
{
# update the internal structure
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
# updated the content variable
$this->content = $pageMarkdown;
@ -326,7 +319,7 @@ class BlockApiController extends ContentController
return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
}
/* parse markdown-file to content-array, if title parse title. */
# parse markdown-file to content-array, if title parse title.
if($this->params['block_id'] == 0)
{
$blockArray = $parsedown->text($blockMarkdownTitle);
@ -380,12 +373,9 @@ class BlockApiController extends ContentController
{
return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to publish content.']), 403);
}
# validate input
# if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); }
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
$this->setHomepage($args = false);
@ -448,10 +438,10 @@ class BlockApiController extends ContentController
$this->setItemPath('txt');
/* update the file */
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
{
# update the internal structure
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
# update this content
$this->content = $pageMarkdown;
@ -493,7 +483,7 @@ class BlockApiController extends ContentController
}
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
$this->setHomepage($args = false);
@ -561,10 +551,10 @@ class BlockApiController extends ContentController
$this->setItemPath('txt');
/* update the file */
if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
{
# update the internal structure
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
}
else
{
@ -580,335 +570,4 @@ class BlockApiController extends ContentController
return $response->withJson(array('markdown' => $pageMarkdown, 'toc' => $toc, 'errors' => $errors));
}
public function getMediaLibImages(Request $request, Response $response, $args)
{
# get params from call
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
$imageProcessor = new ProcessImage($this->settings['images']);
if(!$imageProcessor->checkFolders('images'))
{
return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
}
$imagelist = $imageProcessor->scanMediaFlat();
return $response->withJson(array('images' => $imagelist));
}
public function getMediaLibFiles(Request $request, Response $response, $args)
{
# get params from call
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
$fileProcessor = new ProcessFile();
if(!$fileProcessor->checkFolders())
{
return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
}
$filelist = $fileProcessor->scanFilesFlat();
return $response->withJson(array('files' => $filelist));
}
public function getImage(Request $request, Response $response, $args)
{
# get params from call
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
$this->setStructure($draft = true, $cache = false);
$imageProcessor = new ProcessImage($this->settings['images']);
if(!$imageProcessor->checkFolders('images'))
{
return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
}
$imageDetails = $imageProcessor->getImageDetails($this->params['name'], $this->structure);
if($imageDetails)
{
return $response->withJson(array('image' => $imageDetails));
}
# return $response->withJson(array('image' => false, 'errors' => 'image name invalid or not found'));
return $response->withJson(['errors' => ['message' => 'Image name invalid or not found.']], 404);
}
public function getFile(Request $request, Response $response, $args)
{
# get params from call
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
$this->setStructure($draft = true, $cache = false);
$fileProcessor = new ProcessFile();
if(!$fileProcessor->checkFolders())
{
return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
}
$fileDetails = $fileProcessor->getFileDetails($this->params['name'], $this->structure);
if($fileDetails)
{
return $response->withJson(['file' => $fileDetails]);
}
return $response->withJson(['errors' => ['message' => 'file name invalid or not found']],404);
}
public function createImage(Request $request, Response $response, $args)
{
# get params from call
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
# do this shit in the model ...
$imagename = explode('.', $this->params['name']);
array_pop($imagename);
$imagename = implode('-', $imagename);
$name = URLify::filter(iconv(mb_detect_encoding($imagename, mb_detect_order(), true), "UTF-8", $imagename));
$imageProcessor = new ProcessImage($this->settings['images']);
if(!$imageProcessor->checkFolders('images'))
{
return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
}
if($imageProcessor->createImage($this->params['image'], $name, $this->settings['images']))
{
return $response->withJson(array('errors' => false));
}
return $response->withJson(array('errors' => ['message' => 'could not store image to temporary folder']));
}
public function createFile(Request $request, Response $response, $args)
{
# get params from call
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
$finfo = finfo_open( FILEINFO_MIME_TYPE );
$mtype = finfo_file( $finfo, $this->params['file'] );
finfo_close( $finfo );
$allowedMimes = $this->getAllowedMtypes();
if(!in_array($mtype, $allowedMimes))
{
return $response->withJson(array('errors' => ['message' => 'File-type is not allowed']));
}
# sanitize file name
$filename = basename($this->params['name']);
$filename = explode('.', $this->params['name']);
array_pop($filename);
$filename = implode('-', $filename);
$name = URLify::filter(iconv(mb_detect_encoding($filename, mb_detect_order(), true), "UTF-8", $filename));
$fileProcessor = new ProcessFile();
if(!$fileProcessor->checkFolders())
{
return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
}
if($fileProcessor->createFile($this->params['file'], $name))
{
return $response->withJson(array('errors' => false, 'name' => $name));
}
return $response->withJson(array('errors' => ['message' => 'could not store file to temporary folder']));
}
public function publishImage(Request $request, Response $response, $args)
{
$params = $request->getParsedBody();
$imageProcessor = new ProcessImage($this->settings['images']);
if(!$imageProcessor->checkFolders())
{
return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
}
$imageUrl = $imageProcessor->publishImage();
if($imageUrl)
{
# replace the image placeholder in markdown with the image url
$params['markdown'] = str_replace('imgplchldr', $imageUrl, $params['markdown']);
$request = $request->withParsedBody($params);
if($params['new'])
{
return $this->addBlock($request, $response, $args);
}
return $this->updateBlock($request, $response, $args);
}
return $response->withJson(array('errors' => ['message' => 'could not store image to media folder']));
}
public function deleteImage(Request $request, Response $response, $args)
{
# get params from call
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
if(!isset($this->params['name']))
{
return $response->withJson(array('errors' => ['message' => 'image name is missing']));
}
$imageProcessor = new ProcessImage($this->settings['images']);
if(!$imageProcessor->checkFolders())
{
return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
}
$errors = $imageProcessor->deleteImage($this->params['name']);
return $response->withJson(array('errors' => $errors));
}
public function deleteFile(Request $request, Response $response, $args)
{
# get params from call
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
if(!isset($this->params['name']))
{
return $response->withJson(array('errors' => ['message' => 'file name is missing']));
}
$fileProcessor = new ProcessFile();
$errors = false;
if($fileProcessor->deleteFile($this->params['name']))
{
return $response->withJson(array('errors' => false));
}
return $response->withJson(array('errors' => ['message' => 'could not delete the file']));
}
public function saveVideoImage(Request $request, Response $response, $args)
{
/* get params from call */
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
$class = false;
$imageUrl = $this->params['markdown'];
if(strpos($imageUrl, 'https://www.youtube.com/watch?v=') !== false)
{
$videoID = str_replace('https://www.youtube.com/watch?v=', '', $imageUrl);
$videoID = strpos($videoID, '&') ? substr($videoID, 0, strpos($videoID, '&')) : $videoID;
$class = 'youtube';
}
if(strpos($imageUrl, 'https://youtu.be/') !== false)
{
$videoID = str_replace('https://youtu.be/', '', $imageUrl);
$videoID = strpos($videoID, '?') ? substr($videoID, 0, strpos($videoID, '?')) : $videoID;
$class = 'youtube';
}
if($class == 'youtube')
{
$videoURLmaxres = 'https://i1.ytimg.com/vi/' . $videoID . '/maxresdefault.jpg';
$videoURL0 = 'https://i1.ytimg.com/vi/' . $videoID . '/0.jpg';
}
$ctx = stream_context_create(array(
'https' => array(
'timeout' => 1
)
)
);
$imageData = @file_get_contents($videoURLmaxres, 0, $ctx);
if($imageData === false)
{
$imageData = @file_get_contents($videoURL0, 0, $ctx);
if($imageData === false)
{
return $response->withJson(['errors' => ['message' => 'We did not find that video or could not get a preview image.']], 500);
}
}
$imageData64 = 'data:image/jpeg;base64,' . base64_encode($imageData);
$desiredSizes = $this->settings['images'];
$desiredSizes['live'] = ['width' => 560, 'height' => 315];
$imageProcessor = new ProcessImage($desiredSizes);
if(!$imageProcessor->checkFolders('images'))
{
return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
}
if(!$imageProcessor->createImage($imageData64, 'youtube-' . $videoID, $desiredSizes, $overwrite = true))
{
return $response->withJson(['errors' => ['message' => 'We could not create the image.']], 500);
}
$imageUrl = $imageProcessor->publishImage();
if($imageUrl)
{
$this->params['markdown'] = '![' . $class . '-video](' . $imageUrl . ' "click to load video"){#' . $videoID. ' .' . $class . '}';
$request = $request->withParsedBody($this->params);
if($this->params['new'])
{
return $this->addBlock($request, $response, $args);
}
return $this->updateBlock($request, $response, $args);
}
return $response->withJson(array('errors' => ['message' => 'could not store the preview image']));
}
private function getAllowedMtypes()
{
return array(
'application/zip',
'application/gzip',
'application/vnd.rar',
'application/vnd.visio',
'application/vnd.ms-excel',
'application/vnd.ms-powerpoint',
'application/vnd.ms-word.document.macroEnabled.12',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.apple.keynote',
'application/vnd.apple.mpegurl',
'application/vnd.apple.numbers',
'application/vnd.apple.pages',
'application/vnd.amazon.mobi8-ebook',
'application/epub+zip',
'application/pdf',
'image/png',
'image/jpeg',
'image/jpg',
'image/gif',
'image/svg+xml',
'font/*',
'audio/mpeg',
'audio/mp4',
'audio/ogg',
'video/mpeg',
'video/mp4',
'video/ogg',
);
}
}

View File

@ -4,11 +4,10 @@ namespace Typemill\Controllers;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Views\Twig;
use Typemill\Models\Folder;
use Typemill\Extensions\ParsedownExtension;
class ContentBackendController extends ContentController
class ControllerAuthorEditor extends ControllerAuthor
{
/**
* Show Content for raw editor
@ -25,7 +24,7 @@ class ContentBackendController extends ContentController
$this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
# set structure
if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
if(!$this->setStructureDraft()){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
# set information for homepage
$this->setHomepage($args);
@ -37,7 +36,7 @@ class ContentBackendController extends ContentController
$this->checkContentOwnership();
# get the breadcrumb (here we need it only to mark the actual item active in navigation)
$breadcrumb = isset($this->item->keyPathArray) ? Folder::getBreadcrumb($this->structure, $this->item->keyPathArray) : false;
$breadcrumb = isset($this->item->keyPathArray) ? Folder::getBreadcrumb($this->structureDraft, $this->item->keyPathArray) : false;
# set the status for published and drafted
$this->setPublishStatus();
@ -78,10 +77,10 @@ class ContentBackendController extends ContentController
}
}
return $this->render($response, 'editor/editor-raw.twig', array(
return $this->renderIntern($response, 'editor/editor-raw.twig', array(
'acl' => $this->c->acl,
'mycontent' => $this->mycontent,
'navigation' => $this->structure,
'navigation' => $this->structureDraft,
'homepage' => $this->homepage,
'title' => $title,
'content' => $content,
@ -105,13 +104,13 @@ class ContentBackendController extends ContentController
$this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
# set structure
if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
if(!$this->setStructureDraft()){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
# set information for homepage
$this->setHomepage($args);
# set item
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structureDraft, 'settings' => $this->settings, 'content' => $this->errors )); }
# we have to check ownership here to use it for permission-check in tempates
$this->checkContentOwnership();
@ -168,20 +167,15 @@ class ContentBackendController extends ContentController
unset($content[0]);
}
return $this->render($response, 'editor/editor-blox.twig', array(
return $this->renderIntern($response, 'editor/editor-blox.twig', array(
'acl' => $this->c->acl,
'mycontent' => $this->mycontent,
'navigation' => $this->structure,
'homepage' => $this->homepage,
'navigation' => $this->structureDraft,
'homepage' => $this->homepage,
'title' => $title,
'content' => $content,
'item' => $this->item,
'item' => $this->item,
'settings' => $this->settings
));
}
public function showEmpty(Request $request, Response $response, $args)
{
return $this->renderIntern404($response, array( 'settings' => $this->settings ));
}
}

View File

@ -7,9 +7,8 @@ use Slim\Http\Response;
use Typemill\Models\ProcessImage;
use Typemill\Models\ProcessFile;
use Typemill\Controllers\BlockApiController;
use \URLify;
class MediaApiController extends ContentController
class ControllerAuthorMediaApi extends ControllerAuthor
{
public function getMediaLibImages(Request $request, Response $response, $args)
{
@ -51,7 +50,7 @@ class MediaApiController extends ContentController
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
$this->setStructure($draft = true, $cache = false);
$this->setStructureDraft();
$imageProcessor = new ProcessImage($this->settings['images']);
if(!$imageProcessor->checkFolders('images'))
@ -59,7 +58,7 @@ class MediaApiController extends ContentController
return $response->withJson(['errors' => 'Please check if your media-folder exists and all folders inside are writable.'], 500);
}
$imageDetails = $imageProcessor->getImageDetails($this->params['name'], $this->structure);
$imageDetails = $imageProcessor->getImageDetails($this->params['name'], $this->structureDraft);
if($imageDetails)
{
@ -75,7 +74,7 @@ class MediaApiController extends ContentController
$this->params = $request->getParams();
$this->uri = $request->getUri()->withUserInfo('');
$this->setStructure($draft = true, $cache = false);
$this->setStructureDraft();
$fileProcessor = new ProcessFile();
if(!$fileProcessor->checkFolders())
@ -83,7 +82,7 @@ class MediaApiController extends ContentController
return $response->withJson(['errors' => 'Please check if your media-folder exists and all folders inside are writable.'], 500);
}
$fileDetails = $fileProcessor->getFileDetails($this->params['name'], $this->structure);
$fileDetails = $fileProcessor->getFileDetails($this->params['name'], $this->structureDraft);
if($fileDetails)
{

View File

@ -9,7 +9,7 @@ use Typemill\Models\WriteMeta;
use Typemill\Models\Folder;
use Typemill\Events\OnMetaDefinitionsLoaded;
class MetaApiController extends ContentController
class ControllerAuthorMetaApi extends ControllerAuthor
{
# get the standard meta-definitions and the meta-definitions from plugins (same for all sites)
public function getMetaDefinitions(Request $request, Response $response, $args)
@ -75,7 +75,7 @@ class MetaApiController extends ContentController
$this->uri = $request->getUri()->withUserInfo('');
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
# set information for homepage
$this->setHomepage($args = false);
@ -148,16 +148,6 @@ class MetaApiController extends ContentController
$metadefinitions[$tabname]['fields'][$fieldname]['options'] = $userroles;
}
}
/*
# special treatment for customfields
if(isset($fielddefinitions['type']) && ($fielddefinitions['type'] == 'customfields' ) && $metadata[$tabname][$fieldname] )
{
$metadata[$tabname][$fieldname] = $this->customfieldsPrepareForEdit($metadata[$tabname][$fieldname]);
}
*/
}
}
@ -190,7 +180,7 @@ class MetaApiController extends ContentController
}
# set structure
if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
# set information for homepage
$this->setHomepage($args = false);
@ -256,20 +246,6 @@ class MetaApiController extends ContentController
{
$errors[$tab][$fieldName] = $result[$fieldName][0];
}
/*
# special treatment for customfields
if($fieldDefinition && isset($fieldDefinition['type']) && ($fieldDefinition['type'] == 'customfields' ) )
{
$arrayFeatureOn = false;
if(isset($fieldDefinition['data']) && ($fieldDefinition['data'] == 'array'))
{
$arrayFeatureOn = true;
}
$metaInput[$fieldName] = $this->customfieldsPrepareForSave($metaInput[$fieldName], $arrayFeatureOn);
}
*/
}
}
@ -312,7 +288,7 @@ class MetaApiController extends ContentController
$writeMeta->renamePost($this->item->pathWithoutType, $newPathWithoutType);
# recreate the draft structure
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
# update item
$this->setItem();
@ -372,16 +348,25 @@ class MetaApiController extends ContentController
$writeMeta->updateYaml('cache', 'structure-extended.yaml', $extended);
# recreate the draft structure
$this->setStructure($draft = true, $cache = false);
$this->setFreshStructureDraft();
# recreate the live structure
$this->setFreshStructureLive();
# recreate the navigation
$this->setFreshNavigation();
# update the sitemap
$this->updateSitemap();
# update item
$this->setItem();
# set item in navigation active again
$activeItem = Folder::getItemForUrl($this->structure, $this->item->urlRel, $this->uri->getBaseUrl());
$activeItem = Folder::getItemForUrl($this->structureDraft, $this->item->urlRel, $this->uri->getBaseUrl());
# send new structure to frontend
$structure = $this->structure;
$structure = $this->structureDraft;
}
# return with the new metadata
@ -410,75 +395,6 @@ class MetaApiController extends ContentController
return $flattab;
}
# can be deleted ??
private function customfieldsPrepareForEdit($customfields)
{
# to edit fields in vue we have to transform the arrays in yaml into an array of objects like [{key: abc, value: xyz}{...}]
$customfieldsForEdit = [];
foreach($customfields as $key => $value)
{
$valuestring = $value;
# and make sure that arrays are transformed back into strings
if(isset($value) && is_array($value))
{
$valuestring = '- ' . implode(PHP_EOL . '- ', $value);
}
$customfieldsForEdit[] = ['key' => $key, 'value' => $valuestring];
}
return $customfieldsForEdit;
}
# can be deleted?
private function customfieldsPrepareForSave($customfields, $arrayFeatureOn)
{
# we have to convert the incoming array of objects from vue [{key: abc, value: xyz}{...}] into key-value arrays for yaml.
$customfieldsForSave = [];
foreach($customfields as $valuePair)
{
# doupbe check, not really needed because it is validated already
if(!isset($valuePair['key']) OR ($valuePair['key'] == ''))
{
# do not use data without valid keys
continue;
}
$key = $valuePair['key'];
$value = '';
if(isset($valuePair['value']))
{
$value = $valuePair['value'];
# check if value is formatted as a list, then transform it into an array
if($arrayFeatureOn)
{
# normalize line breaks, php-eol does not work here
$cleanvalue = str_replace(array("\r\n", "\r"), "\n", $valuePair['value']);
$cleanvalue = trim($cleanvalue, "\n");
$arrayValues = explode("\n- ",$cleanvalue);
if(count($arrayValues) > 1)
{
$value = array_map(function($item) { return trim($item, '- '); }, $arrayValues);
}
}
}
$customfieldsForSave[$key] = $value;
}
return $customfieldsForSave;
}
protected function hasChanged($input, $page, $field)
{
if(isset($input[$field]) && isset($page[$field]) && $input[$field] == $page[$field])

View File

@ -10,7 +10,7 @@ use Typemill\Models\User;
use Typemill\Models\WriteYaml;
use Typemill\Extensions\ParsedownExtension;
class AuthController extends Controller
class ControllerFrontendAuth extends ControllerShared
{
# redirect if visit /setup route
public function redirect(Request $request, Response $response)

View File

@ -5,11 +5,8 @@ namespace Typemill\Controllers;
use Typemill\Models\Validation;
use Typemill\Models\WriteYaml;
class FormController extends Controller
class ControllerFrontendForms extends ControllerShared
{
/*************************************
** SAVE THEME- AND PLUGIN-SETTINGS **
*************************************/
public function savePublicForm($request, $response, $args)
{

View File

@ -7,7 +7,7 @@ use Typemill\Models\Validation;
use Typemill\Models\User;
use Typemill\Models\Write;
class SetupController extends Controller
class ControllerFrontendSetup extends ControllerShared
{
# redirect if visit /setup route

View File

@ -3,14 +3,8 @@
namespace Typemill\Controllers;
use Typemill\Models\Folder;
use Typemill\Models\WriteCache;
use Typemill\Models\WriteSitemap;
use Typemill\Models\WriteYaml;
use Typemill\Models\WriteMeta;
use \Symfony\Component\Yaml\Yaml;
use Typemill\Models\VersionCheck;
use Typemill\Models\Markdown;
use Typemill\Events\OnCacheUpdated;
use Typemill\Extensions\ParsedownExtension;
use Typemill\Events\OnPagetreeLoaded;
use Typemill\Events\OnBreadcrumbLoaded;
use Typemill\Events\OnItemLoaded;
@ -20,78 +14,59 @@ use Typemill\Events\OnMarkdownLoaded;
use Typemill\Events\OnContentArrayLoaded;
use Typemill\Events\OnHtmlLoaded;
use Typemill\Events\OnRestrictionsLoaded;
use Typemill\Extensions\ParsedownExtension;
class PageController extends Controller
class ControllerFrontendWebsite extends ControllerShared
{
public function index($request, $response, $args)
{
{
# Initiate Variables
$contentHTML = false;
$item = false;
$home = false;
$breadcrumb = false;
$currentpage = false;
$this->pathToContent = $this->settings['rootPath'] . $this->settings['contentFolder'];
$this->uri = $request->getUri()->withUserInfo('');
$this->base_url = $this->uri->getBaseUrl();
/* Initiate Variables */
$structure = false;
$contentHTML = false;
$item = false;
$home = false;
$breadcrumb = false;
$currentpage = false;
$pathToContent = $this->settings['rootPath'] . $this->settings['contentFolder'];
$cache = new WriteCache();
$uri = $request->getUri()->withUserInfo('');
$base_url = $uri->getBaseUrl();
$this->pathToContent = $pathToContent;
try
# if there is no structure at all, the content folder is probably empty
if(!$this->setStructureLive())
{
# if the cached structure is still valid, use it
if($cache->validate('cache', 'lastCache.txt', 600))
{
$structure = $cache->getCachedStructure();
}
else
{
# dispatch message that the cache has been refreshed
$this->c->dispatcher->dispatch('onCacheUpdated', new OnCacheUpdated(false));
}
return $this->render($response, '/index.twig', array( 'content' => '<h1>No Content</h1><p>Your content folder is empty.</p>' ));
}
if(!isset($structure) OR !$structure)
{
# if not, get a fresh structure of the content folder
$structure = $cache->getFreshStructure($pathToContent, $uri);
# we can create an initial sitemap here, but makes no sense for every pagecall. Sitemap will be created on first author interaction (publish/delete/channge page).
# $this->checkSitemap();
# if there is no structure at all, the content folder is probably empty
if(!$structure)
{
$content = '<h1>No Content</h1><p>Your content folder is empty.</p>';
# if the admin activated to refresh the cache automatically each 10 minutes (e.g. use without admin area)
if(isset($this->settings['refreshcache']) && $this->settings['refreshcache'] && !$this->writeCache->validate('cache', 'lastCache.txt', 600))
{
# delete the cache
$dir = $this->settings['basePath'] . 'cache';
$this->writeCache->deleteCacheFiles($dir);
return $this->render($response, '/index.twig', array( 'content' => $content ));
}
elseif(!$cache->validate('cache', 'lastSitemap.txt', 86400))
{
# update sitemap
$sitemap = new WriteSitemap();
$sitemap->updateSitemap('cache', 'sitemap.xml', 'lastSitemap.txt', $structure, $uri->getBaseUrl());
}
}
# update the internal structure
$this->setFreshStructureDraft();
# dispatch event and let others manipulate the structure
$structure = $this->c->dispatcher->dispatch('onPagetreeLoaded', new OnPagetreeLoaded($structure))->getData();
}
catch (Exception $e)
{
echo $e->getMessage();
exit(1);
# update the public structure
$this->setFreshStructureLive();
# update the navigation
$this->setFreshNavigation();
# update the sitemap
$this->updateSitemap();
}
# get meta-Information
$writeMeta = new WriteMeta();
$theme = $this->settings['theme'];
# dispatch event and let others manipulate the structure
$this->structureLive = $this->c->dispatcher->dispatch('onPagetreeLoaded', new OnPagetreeLoaded($this->structureLive))->getData();
# check if there is a custom theme css
$customcss = $writeMeta->checkFile('cache', $theme . '-custom.css');
$theme = $this->settings['theme'];
$customcss = $this->writeCache->checkFile('cache', $theme . '-custom.css');
if($customcss)
{
$this->c->assets->addCSS($base_url . '/cache/' . $theme . '-custom.css');
$this->c->assets->addCSS($this->base_url . '/cache/' . $theme . '-custom.css');
}
$logo = false;
@ -106,13 +81,13 @@ class PageController extends Controller
$favicon = true;
}
# the navigation is a copy of the structure without the hidden pages
$navigation = $cache->getCache('cache', 'navigation.txt');
# hint: if the navigation has been deleted from the cache, then we do not recreate it here to save performace. Instead you have to recreate cache in admin or change a page (publish/unpublish/delete/move)
$navigation = $this->writeCache->getCache('cache', 'navigation.txt');
if(!$navigation)
{
# use the structure if there is no cached navigation
$navigation = $structure;
$navigation = $this->structureLive;
}
# start pagination
@ -149,23 +124,23 @@ class PageController extends Controller
if(empty($args))
{
$home = true;
$item = Folder::getItemForUrl($navigation, $uri->getBasePath(), $uri->getBaseUrl(), NULL, $home);
$urlRel = $uri->getBasePath();
$item = Folder::getItemForUrl($navigation, $this->uri->getBasePath(), $this->uri->getBaseUrl(), NULL, $home);
$urlRel = $this->uri->getBasePath();
}
else
{
# get the request url, trim args so physical folders have no trailing slash
$urlRel = $uri->getBasePath() . '/' . trim($args['params'], "/");
$urlRel = $this->uri->getBasePath() . '/' . trim($args['params'], "/");
# find the url in the content-item-tree and return the item-object for the file
# important to use the structure here so it is found, even if the item is hidden.
$item = Folder::getItemForUrl($structure, $urlRel, $uri->getBasePath());
$item = Folder::getItemForUrl($this->structureLive, $urlRel, $this->uri->getBasePath());
# if the item is a folder and if that folder is not hidden
if($item && $item->elementType == 'folder' && isset($item->hide) && !$item->hide)
{
# use the navigation instead of the structure so that hidden elements are erased
$item = Folder::getItemForUrl($navigation, $urlRel, $uri->getBaseUrl(), NULL, $home);
$item = Folder::getItemForUrl($navigation, $urlRel, $this->uri->getBaseUrl(), NULL, $home);
}
# if there is still no item, return a 404-page
@ -174,7 +149,7 @@ class PageController extends Controller
return $this->render404($response, array(
'navigation' => $navigation,
'settings' => $this->settings,
'base_url' => $base_url,
'base_url' => $this->base_url,
'title' => false,
'content' => false,
'item' => false,
@ -187,7 +162,6 @@ class PageController extends Controller
}
}
if(isset($item->hide))
{
# if it is a hidden page
@ -195,11 +169,11 @@ class PageController extends Controller
{
# get breadcrumb for page and set pages active
# use structure here because the hidden item is not part of the navigation
$breadcrumb = Folder::getBreadcrumb($structure, $item->keyPathArray);
$breadcrumb = Folder::getBreadcrumb($this->structureLive, $item->keyPathArray);
$breadcrumb = $this->c->dispatcher->dispatch('onBreadcrumbLoaded', new OnBreadcrumbLoaded($breadcrumb))->getData();
# add the paging to the item
$item = Folder::getPagingForItem($structure, $item);
$item = Folder::getPagingForItem($this->structureLive, $item);
}
else
{
@ -217,7 +191,7 @@ class PageController extends Controller
$item = $this->c->dispatcher->dispatch('onItemLoaded', new OnItemLoaded($item))->getData();
# set the filepath
$filePath = $pathToContent . $item->path;
$filePath = $this->pathToContent . $item->path;
# check if url is a folder and add index.md
if($item->elementType == 'folder')
@ -230,6 +204,9 @@ class PageController extends Controller
# dispatch the original content without plugin-manipulations for case anyone wants to use it
$this->c->dispatcher->dispatch('onOriginalLoaded', new OnOriginalLoaded($contentMD));
# initiate object for metadata
$writeMeta = new WriteMeta();
# makes sure that you always have the full meta with title, description and all the rest.
$metatabs = $writeMeta->completePageMeta($contentMD, $this->settings, $item);
@ -243,7 +220,7 @@ class PageController extends Controller
$itemUrl = isset($item->urlRel) ? $item->urlRel : false;
/* initialize parsedown */
$parsedown = new ParsedownExtension($base_url, $this->settings);
$parsedown = new ParsedownExtension($this->base_url, $this->settings);
/* set safe mode to escape javascript and html in markdown */
$parsedown->setSafeMode(true);
@ -334,21 +311,21 @@ class PageController extends Controller
$firstImage = false;
if($img_url)
{
$firstImage = array('img_url' => $base_url . '/' . $img_url, 'img_alt' => $img_alt);
$firstImage = array('img_url' => $this->base_url . '/' . $img_url, 'img_alt' => $img_alt);
}
$route = empty($args) && isset($this->settings['themes'][$theme]['cover']) ? '/cover.twig' : '/index.twig';
return $this->render($response, $route, [
'home' => $home,
'navigation' => $navigation,
'navigation' => $navigation,
'title' => $title,
'content' => $contentHTML,
'item' => $item,
'breadcrumb' => $breadcrumb,
'settings' => $this->settings,
'base_url' => $this->base_url,
'metatabs' => $metatabs,
'base_url' => $base_url,
'image' => $firstImage,
'logo' => $logo,
'favicon' => $favicon,

View File

@ -14,7 +14,7 @@ use Typemill\Events\OnUserfieldsLoaded;
use Typemill\Events\OnSystemnaviLoaded;
use Typemill\Events\OnUserDeleted;
class SettingsController extends Controller
class ControllerSettings extends ControllerShared
{
public function showBlank($request, $response, $args)
@ -22,7 +22,7 @@ class SettingsController extends Controller
$user = new User();
$settings = $this->c->get('settings');
$route = $request->getAttribute('route');
$navigation = $this->getNavigation();
$navigation = $this->getMainNavigation();
$content = '<h1>Hello</h1><p>I am the showBlank method from the settings controller</p><p>In most cases I have been called from a plugin. But if you see this content, then the plugin does not work or has forgotten to inject its own content.</p>';
@ -48,7 +48,7 @@ class SettingsController extends Controller
$languages = $this->getLanguages();
$locale = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? substr($_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2) : 'en';
$route = $request->getAttribute('route');
$navigation = $this->getNavigation();
$navigation = $this->getMainNavigation();
# set navigation active
$navigation['System']['active'] = true;
@ -118,6 +118,8 @@ class SettingsController extends Controller
'recoversubject' => $newSettings['recoversubject'],
'recovermessage' => $newSettings['recovermessage'],
'securitylog' => isset($newSettings['securitylog']) ? true : null,
'oldslug' => isset($newSettings['oldslug']) ? true : null,
'refreshcache' => isset($newSettings['refreshcache']) ? true : null,
);
# https://www.slimframework.com/docs/v3/cookbook/uploading-files.html;
@ -294,7 +296,7 @@ class SettingsController extends Controller
/* add the users for navigation */
$route = $request->getAttribute('route');
$navigation = $this->getNavigation();
$navigation = $this->getMainNavigation();
# set navigation active
$navigation['Themes']['active'] = true;
@ -363,7 +365,7 @@ class SettingsController extends Controller
}
$route = $request->getAttribute('route');
$navigation = $this->getNavigation();
$navigation = $this->getMainNavigation();
# set navigation active
$navigation['Plugins']['active'] = true;
@ -591,7 +593,7 @@ class SettingsController extends Controller
$userform = $fieldsModel->getFields($userSettings, 'users', 'user', $fieldDefinitions);
$route = $request->getAttribute('route');
$navigation = $this->getNavigation();
$navigation = $this->getMainNavigation();
# set navigation active
$navigation['Account']['active'] = true;
@ -647,7 +649,7 @@ class SettingsController extends Controller
$userform = $fieldsModel->getFields($userSettings, 'users', 'user', $fieldDefinitions);
$route = $request->getAttribute('route');
$navigation = $this->getNavigation();
$navigation = $this->getMainNavigation();
# set navigation active
$navigation['Users']['active'] = true;
@ -675,7 +677,7 @@ class SettingsController extends Controller
$userdata = array();
$route = $request->getAttribute('route');
$settings = $this->c->get('settings');
$navigation = $this->getNavigation();
$navigation = $this->getMainNavigation();
# set navigation active
$navigation['Users']['active'] = true;
@ -764,7 +766,7 @@ class SettingsController extends Controller
$userroles = $this->c->acl->getRoles();
$route = $request->getAttribute('route');
$settings = $this->c->get('settings');
$navigation = $this->getNavigation();
$navigation = $this->getMainNavigation();
# set navigation active
$navigation['Users']['active'] = true;
@ -969,22 +971,26 @@ class SettingsController extends Controller
public function clearCache($request, $response, $args)
{
$settings = $this->c->get('settings');
$dir = $settings['basePath'] . 'cache';
$uri = $request->getUri()->withUserInfo('');
$pathToContent = $settings['rootPath'] . $settings['contentFolder'];
$writeCache = new writeCache();
$error = $writeCache->deleteCacheFiles($dir);
$this->uri = $request->getUri()->withUserInfo('');
$dir = $this->settings['basePath'] . 'cache';
$error = $this->writeCache->deleteCacheFiles($dir);
if($error)
{
return $response->withJson(['errors' => $error], 500);
}
# this recreates the cache for structure, structure-extended and navigation
$writeCache->getFreshStructure($pathToContent, $uri);
# create a new draft structure
$this->setFreshStructureDraft();
# create a new draft structure
$this->setFreshStructureLive();
# create a new draft structure
$this->setFreshNavigation();
# update the sitemap
$this->updateSitemap();
return $response->withJson(array('errors' => false));
}
@ -1083,7 +1089,7 @@ class SettingsController extends Controller
);
}
private function getNavigation()
private function getMainNavigation()
{
$navigation = [
'System' => ['routename' => 'settings.show', 'icon' => 'icon-wrench', 'aclresource' => 'system', 'aclprivilege' => 'view'],

View File

@ -0,0 +1,446 @@
<?php
namespace Typemill\Controllers;
use Psr\Container\ContainerInterface;
use Typemill\Models\Folder;
use Typemill\Models\WriteCache;
use Typemill\Models\WriteYaml;
use Typemill\Events\OnPageReady;
abstract class ControllerShared
{
# holds the pimple container
protected $c;
# holds the settings
protected $settings;
# holds the write cache object
protected $writeCache;
# holds the structure of content folder as a serialized array of objects
protected $structureDraft = false;
# holds the structure of content folder as a serialized array of objects
protected $structureLive = false;
# holds the name of the structure-file with drafts for author environment
protected $structureDraftName = 'structure-draft.txt';
# holds the name of the structure-file without drafts for live site
protected $structureLiveName = 'structure.txt';
# holds the frontend navigation without hidden pages
protected $navigation = false;
# holds the list of pages with navigation titles and hidden pages. It extends the structures and navigations
protected $extended = false;
public function __construct(ContainerInterface $c)
{
$this->c = $c;
$this->settings = $this->c->get('settings');
# used everywhere so instantiate it
$this->writeCache = new writeCache();
$this->c->dispatcher->dispatch('onTwigLoaded');
}
# render page for frontend
protected function render($response, $route, $data)
{
# why commented this out??
$data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
if(isset($_SESSION['old']))
{
unset($_SESSION['old']);
}
$response = $response->withoutHeader('Server');
$response = $response->withAddedHeader('X-Powered-By', 'Typemill');
if(!isset($this->settings['headersoff']) or !$this->settings['headersoff'])
{
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
$response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade');
if($this->c->request->getUri()->getScheme() == 'https')
{
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
}
}
return $this->c->view->render($response, $route, $data);
}
# render 404 for frontend
protected function render404($response, $data = NULL)
{
return $this->c->view->render($response->withStatus(404), '/404.twig', $data);
}
# render page for authors (admin-area)
protected function renderIntern($response, $route, $data)
{
if(isset($_SESSION['old']))
{
unset($_SESSION['old']);
}
$response = $response->withoutHeader('Server');
$response = $response->withAddedHeader('X-Powered-By', 'Typemill');
if(!isset($this->settings['headersoff']) or !$this->settings['headersoff'])
{
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
$response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade');
if($this->c->request->getUri()->getScheme() == 'https')
{
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
}
}
return $this->c->view->render($response, $route, $data);
}
# render 404 for authors
protected function renderIntern404($response, $data = NULL)
{
return $this->c->view->render($response->withStatus(404), '/intern404.twig', $data);
}
# reads the cached structure with published and non-published pages for the author
protected function setStructureDraft()
{
# get the cached structure
$this->structureDraft = $this->writeCache->getCache('cache', $this->structureDraftName);
# if there is no cached structure
if(!$this->structureDraft)
{
return $this->setFreshStructureDraft();
}
return true;
}
# creates a fresh structure with published and non-published pages for the author
protected function setFreshStructureDraft()
{
# scan the content of the folder
$pagetreeDraft = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft = true );
# if there is content, then get the content details
if(count($pagetreeDraft) > 0)
{
# get the extended structure files with changes like navigation title or hidden pages
$yaml = new writeYaml();
$extended = $this->getExtended();
# create an array of object with the whole content of the folder and changes from extended file
$this->structureDraft = Folder::getFolderContentDetails($pagetreeDraft, $extended, $this->settings, $this->uri->getBaseUrl(), $this->uri->getBasePath());
# cache structure draft
$this->writeCache->updateCache('cache', $this->structureDraftName, 'lastCache.txt', $this->structureDraft);
return true;
}
return false;
}
# reads the cached structure of published pages
protected function setStructureLive()
{
# get the cached structure
$this->structureLive = $this->writeCache->getCache('cache', $this->structureLiveName);
# if there is no cached structure
if(!$this->structureLive)
{
return $this->setFreshStructureLive();
}
return true;
}
# creates a fresh structure with published pages
protected function setFreshStructureLive()
{
# scan the content of the folder
$pagetreeLive = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft = false );
# if there is content, then get the content details
if(count($pagetreeLive) > 0)
{
# get the extended structure files with changes like navigation title or hidden pages
$yaml = new writeYaml();
$extended = $this->getExtended();
# create an array of object with the whole content of the folder and changes from extended file
$this->structureLive = Folder::getFolderContentDetails($pagetreeLive, $extended, $this->settings, $this->uri->getBaseUrl(), $this->uri->getBasePath());
# cache structure live
$this->writeCache->updateCache('cache', $this->structureLiveName, 'lastCache.txt', $this->structureLive);
return true;
}
return false;
}
# reads the live navigation from cache (live structure without hidden pages)
protected function setNavigation()
{
# get the cached structure
$this->navigation = $this->writeCache->getCache('cache', 'navigation.txt');
# if there is no cached structure
if(!$this->navigation)
{
return $this->setFreshNavigation();
}
return true;
}
# creates a fresh live navigation (live structure without hidden pages)
protected function setFreshNavigation()
{
if(!$this->extended)
{
$extended = $this->getExtended();
}
if($this->containsHiddenPages($this->extended))
{
if(!$this->structureLive)
{
$this->setStructureLive();
}
$structureLive = $this->structureLive;
$this->navigation = $this->createNavigation($structureLive);
# cache navigation
$this->writeCache->updateCache('cache', 'navigation.txt', false, $this->navigation);
return true;
}
# make sure no old navigation file is left
$this->writeCache->deleteFileWithPath('cache' . DIRECTORY_SEPARATOR . 'navigation.txt');
return false;
}
# create navigation from structure
protected function createNavigation($structureLive)
{
foreach ($structureLive as $key => $element)
{
if($element->hide === true)
{
unset($structureLive[$key]);
}
elseif(isset($element->folderContent))
{
$structureLive[$key]->folderContent = $this->createNavigation($element->folderContent);
}
}
return $structureLive;
}
# controllerFrontendWebsite, but not in use, makes no sense to check on each page load
public function checkSitemap()
{
if(!$this->writeCache->getCache('cache', 'sitemap.xml'))
{
if(!$this->structureLive)
{
$this->setStructureLive();
}
$this->updateSitemap();
}
return true;
}
public function updateSitemap()
{
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
$sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
$sitemap = $this->addUrlSet($sitemap, $this->uri->getBaseUrl());
$sitemap .= $this->generateUrlSets($this->structureLive);
$sitemap .= '</urlset>';
$this->writeCache->writeFile('cache', 'sitemap.xml', $sitemap);
}
public function generateUrlSets($structureLive)
{
$urlset = '';
foreach($structureLive as $item)
{
if($item->elementType == 'folder')
{
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
$urlset .= $this->generateUrlSets($item->folderContent, $urlset);
}
else
{
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
}
}
return $urlset;
}
public function addUrlSet($urlset, $url)
{
$urlset .= ' <url>' . "\n";
$urlset .= ' <loc>' . $url . '</loc>' . "\n";
$urlset .= ' </url>' . "\n";
return $urlset;
}
protected function getExtended()
{
$yaml = new writeYaml();
if(!$this->extended)
{
$this->extended = $yaml->getYaml('cache', 'structure-extended.yaml');
}
if(!$this->extended)
{
# scan the content of the folder
$pagetreeDraft = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft = true );
# if there is content, then get the content details
if(count($pagetreeDraft) == 0)
{
return false;
}
# create an array of object with the whole content of the folder and changes from extended file
$structureDraft = Folder::getFolderContentDetails($pagetreeDraft, $extended = false, $this->settings, $this->uri->getBaseUrl(), $this->uri->getBasePath());
$this->extended = $this->createExtended($this->settings['rootPath'] . $this->settings['contentFolder'], $yaml, $structureDraft);
$yaml->updateYaml('cache', 'structure-extended.yaml', $this->extended);
}
return $this->extended;
}
# creates a file that holds all hide flags and navigation titles
# reads all meta-files and creates an array with url => ['hide' => bool, 'navtitle' => 'bla']
public function createExtended($contentPath, $yaml, $structureLive, $extended = NULL)
{
if(!$extended)
{
$extended = [];
}
foreach ($structureLive as $key => $item)
{
# $filename = ($item->elementType == 'folder') ? DIRECTORY_SEPARATOR . 'index.yaml' : $item->pathWithoutType . '.yaml';
$filename = $item->pathWithoutType . '.yaml';
if(file_exists($contentPath . $filename))
{
# read file
$meta = $yaml->getYaml('content', $filename);
$extended[$item->urlRelWoF]['hide'] = isset($meta['meta']['hide']) ? $meta['meta']['hide'] : false;
$extended[$item->urlRelWoF]['navtitle'] = isset($meta['meta']['navtitle']) ? $meta['meta']['navtitle'] : '';
}
if ($item->elementType == 'folder')
{
$extended = $this->createExtended($contentPath, $yaml, $item->folderContent, $extended);
}
}
return $extended;
}
# only backoffice
protected function renameExtended($item, $newFolder)
{
# get the extended structure files with changes like navigation title or hidden pages
$yaml = new writeYaml();
$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
if(isset($extended[$item->urlRelWoF]))
{
$newUrl = $newFolder->urlRelWoF . '/' . $item->slug;
$entry = $extended[$item->urlRelWoF];
unset($extended[$item->urlRelWoF]);
$extended[$newUrl] = $entry;
$yaml->updateYaml('cache', 'structure-extended.yaml', $extended);
}
return true;
}
# only backoffice
protected function deleteFromExtended()
{
# get the extended structure files with changes like navigation title or hidden pages
$yaml = new writeYaml();
$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
if($this->item->elementType == "file" && isset($extended[$this->item->urlRelWoF]))
{
unset($extended[$this->item->urlRelWoF]);
$yaml->updateYaml('cache', 'structure-extended.yaml', $extended);
}
if($this->item->elementType == "folder")
{
$changed = false;
# delete all entries with that folder url
foreach($extended as $url => $entries)
{
if( strpos($url, $this->item->urlRelWoF) !== false )
{
$changed = true;
unset($extended[$url]);
}
}
if($changed)
{
$yaml->updateYaml('cache', 'structure-extended.yaml', $extended);
}
}
}
# checks if there is a hidden page, returns true on first find
protected function containsHiddenPages($extended)
{
foreach($extended as $element)
{
if(isset($element['hide']) && $element['hide'] === true)
{
return true;
}
}
return false;
}
}

View File

@ -2,7 +2,7 @@
namespace Typemill\Extensions;
use \URLify;
use Typemill\Models\Folder;
class ParsedownExtension extends \ParsedownExtra
{
@ -10,6 +10,8 @@ class ParsedownExtension extends \ParsedownExtra
{
parent::__construct();
$this->settings = $settings;
# show anchor next to headline?
$this->showAnchor = isset($settings['headlineanchors']) ? $settings['headlineanchors'] : false;
@ -329,7 +331,7 @@ class ParsedownExtension extends \ParsedownExtra
}
$text = trim($Line['text'], '#');
$headline = URLify::filter($Line['text']);
$headline = Folder::createSlug($Line['text'], $this->settings);
if ($this->strictMode and isset($text[0]) and $text[0] !== ' ')
{

View File

@ -69,7 +69,7 @@ class Folder
if($fileType == 'md')
{
$folderContent[] = $item;
$folderContent[] = $item;
}
if($draft === true && $fileType == 'txt')
@ -97,7 +97,7 @@ class Folder
* 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)
public static function getFolderContentDetails(array $folderContent, $extended, $settings, $baseUrl, $fullSlugWithFolder = NULL, $fullSlugWithoutFolder = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL)
{
$contentDetails = [];
$iteration = 0;
@ -130,14 +130,14 @@ class Folder
$item->originalName = $key;
$item->elementType = 'folder';
$item->contains = self::getFolderContentType($name, $fullPath . DIRECTORY_SEPARATOR . $key . DIRECTORY_SEPARATOR . 'index.yaml');
$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 = self::createSlug($item->slug, $settings);
$item->path = $fullPath . DIRECTORY_SEPARATOR . $key;
$item->pathWithoutType = $fullPath . DIRECTORY_SEPARATOR . $key . DIRECTORY_SEPARATOR . 'index';
$item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug;
@ -164,7 +164,7 @@ class Folder
rsort($name);
}
$item->folderContent = self::getFolderContentDetails($name, $extended, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter);
$item->folderContent = self::getFolderContentDetails($name, $extended, $settings, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter);
}
elseif($name)
{
@ -200,7 +200,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 = self::createSlug($item->slug, $settings);
$item->path = $fullPath . DIRECTORY_SEPARATOR . $name;
$item->pathWithoutType = $fullPath . DIRECTORY_SEPARATOR . $nameWithoutType;
$item->key = $iteration;
@ -637,4 +637,31 @@ class Folder
$parts = preg_split('/\./',$fileName);
return $parts[0];
}
public static function createSlug($name, $settings = NULL)
{
$name = iconv(mb_detect_encoding($name, mb_detect_order(), true), "UTF-8", $name);
# prior version 1.5.0 settings was no language and remove stop words from slug
$language = "";
$use_remove_list = true;
# if user has not activated the old slug logig < version 1.5.0 style
if($settings && ( !isset($settings['oldslug']) OR !$settings['oldslug'] ) )
{
# then use the language attr and do not remove stop words as default behavior
$language = isset($settings['langattr']) ? $settings['langattr'] : "";
$use_remove_list = false;
}
return URLify::filter(
$name,
$length = 60,
$language,
$file_name = false,
$use_remove_list,
$lower_case = true,
$treat_underscore_as_space = true
);
}
}

View File

@ -28,6 +28,7 @@ class Helpers{
return $ip;
}
public static function addLogEntry($action)
{
$line = self::getUserIP();

View File

@ -1,7 +1,7 @@
<?php
namespace Typemill\Models;
use \URLify;
use Typemill\Models\Folder;
class ProcessAssets
{
@ -107,7 +107,7 @@ class ProcessAssets
$pathinfo = pathinfo($originalname);
$this->extension = strtolower($pathinfo['extension']);
$this->filename = URLify::filter(iconv(mb_detect_encoding($pathinfo['filename'], mb_detect_order(), true), "UTF-8", $pathinfo['filename']));
$this->filename = Folder::createSlug($pathinfo['filename']);
$filename = $this->filename;

View File

@ -3,7 +3,6 @@ namespace Typemill\Models;
use Slim\Http\UploadedFile;
use Typemill\Models\Helpers;
use \URLify;
class ProcessFile extends ProcessAssets
{

View File

@ -1,29 +0,0 @@
<?php
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)
{
$opts = array(
'http'=>array(
'method' => "GET",
'header' => "Referer: $url\r\n"
)
);
$context = stream_context_create($opts);
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;
}
}

View File

@ -127,8 +127,39 @@ class Write
}
return false;
}
}
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;
}
public function moveElement($item, $folderPath, $index, $date = null)
{
$filetypes = array('md', 'txt', 'yaml');
@ -178,35 +209,4 @@ 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;
}
}

View File

@ -110,124 +110,4 @@ class WriteCache extends Write
return $error;
}
public function getFreshStructure($contentPath, $uri)
{
# scan the content of the folder
$pagetree = Folder::scanFolder('content');
# if there is no content, render an empty page
if(count($pagetree) == 0)
{
return false;
}
# get the extended structure files with changes like navigation title or hidden pages
$yaml = new writeYaml();
$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
# create an array of object with the whole content of the folder
$structure = Folder::getFolderContentDetails($pagetree, $extended, $uri->getBaseUrl(), $uri->getBasePath());
# now update the extended structure
if(!$extended)
{
$extended = $this->createExtended($contentPath, $yaml, $structure);
if(!empty($extended))
{
$yaml->updateYaml('cache', 'structure-extended.yaml', $extended);
# we have to update the structure with extended again
$structure = Folder::getFolderContentDetails($pagetree, $extended, $uri->getBaseUrl(), $uri->getBasePath());
}
else
{
$extended = false;
}
}
# cache structure
$this->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure);
if($extended && $this->containsHiddenPages($extended))
{
# generate the navigation (delete empty pages)
$navigation = $this->createNavigationFromStructure($structure);
# cache navigation
$this->updateCache('cache', 'navigation.txt', false, $navigation);
}
else
{
# make sure no separate navigation file is set
$this->deleteFileWithPath('cache' . DIRECTORY_SEPARATOR . 'navigation.txt');
}
# load and return the cached structure, because might be manipulated with navigation....
$structure = $this->getCachedStructure();
return $structure;
}
# creates a file that holds all hide flags and navigation titles
# reads all meta-files and creates an array with url => ['hide' => bool, 'navtitle' => 'bla']
public function createExtended($contentPath, $yaml, $structure, $extended = NULL)
{
if(!$extended)
{
$extended = [];
}
foreach ($structure as $key => $item)
{
# $filename = ($item->elementType == 'folder') ? DIRECTORY_SEPARATOR . 'index.yaml' : $item->pathWithoutType . '.yaml';
$filename = $item->pathWithoutType . '.yaml';
if(file_exists($contentPath . $filename))
{
# read file
$meta = $yaml->getYaml('content', $filename);
$extended[$item->urlRelWoF]['hide'] = isset($meta['meta']['hide']) ? $meta['meta']['hide'] : false;
$extended[$item->urlRelWoF]['navtitle'] = isset($meta['meta']['navtitle']) ? $meta['meta']['navtitle'] : '';
}
if ($item->elementType == 'folder')
{
$extended = $this->createExtended($contentPath, $yaml, $item->folderContent, $extended);
}
}
return $extended;
}
public function createNavigationFromStructure($navigation)
{
foreach ($navigation as $key => $element)
{
if($element->hide === true)
{
unset($navigation[$key]);
}
elseif(isset($element->folderContent))
{
$navigation[$key]->folderContent = $this->createNavigationFromStructure($element->folderContent);
}
}
return $navigation;
}
# checks if there is a hidden page, returns true on first find
protected function containsHiddenPages($extended)
{
foreach($extended as $element)
{
if(isset($element['hide']) && $element['hide'] === true)
{
return true;
}
}
return false;
}
}

View File

@ -1,45 +0,0 @@
<?php
namespace Typemill\Models;
class WriteSitemap extends Write
{
public function updateSitemap($folderName, $sitemapFileName, $requestFileName, $data, $baseUrl)
{
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
$sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
$sitemap = $this->addUrlSet($sitemap, $baseUrl);
$sitemap .= $this->generateUrlSets($data);
$sitemap .= '</urlset>';
$this->writeFile($folderName, $sitemapFileName, $sitemap);
$this->writeFile($folderName, $requestFileName, time());
}
public function generateUrlSets($data)
{
$urlset = '';
foreach($data as $item)
{
if($item->elementType == 'folder')
{
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
$urlset .= $this->generateUrlSets($item->folderContent, $urlset);
}
else
{
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
}
}
return $urlset;
}
public function addUrlSet($urlset, $url)
{
$urlset .= ' <url>' . "\n";
$urlset .= ' <loc>' . $url . '</loc>' . "\n";
$urlset .= ' </url>' . "\n";
return $urlset;
}
}

View File

@ -1,52 +1,50 @@
<?php
use Typemill\Controllers\SettingsController;
use Typemill\Controllers\ContentController;
use Typemill\Controllers\ContentApiController;
use Typemill\Controllers\ArticleApiController;
use Typemill\Controllers\BlockApiController;
use Typemill\Controllers\MediaApiController;
use Typemill\Controllers\MetaApiController;
use Typemill\Controllers\ControllerAuthorArticleApi;
use Typemill\Controllers\ControllerAuthorBlockApi;
use Typemill\Controllers\ControllerAuthorMetaApi;
use Typemill\Controllers\ControllerAuthorMediaApi;
use Typemill\Controllers\ControllerSettings;
use Typemill\Middleware\RestrictApiAccess;
$app->get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/clearcache', SettingsController::class . ':clearCache')->setName('api.clearcache')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/users/getbynames', SettingsController::class . ':getUsersByNames')->setName('api.usersbynames')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/users/getbyemail', SettingsController::class . ':getUsersByEmail')->setName('api.usersbyemail')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/users/getbyrole', SettingsController::class . ':getUsersByRole')->setName('api.usersbyrole')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/themes', ControllerSettings::class . ':getThemeSettings')->setName('api.themes')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/clearcache', ControllerSettings::class . ':clearCache')->setName('api.clearcache')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/users/getbynames', ControllerSettings::class . ':getUsersByNames')->setName('api.usersbynames')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/users/getbyemail', ControllerSettings::class . ':getUsersByEmail')->setName('api.usersbyemail')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/users/getbyrole', ControllerSettings::class . ':getUsersByRole')->setName('api.usersbyrole')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/markdown', ArticleApiController::class . ':getArticleMarkdown')->setName('api.article.markdown')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/html', ArticleApiController::class . ':getArticleHtml')->setName('api.article.html')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/publish', ArticleApiController::class . ':publishArticle')->setName('api.article.publish')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/article/unpublish', ArticleApiController::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/article/discard', ArticleApiController::class . ':discardArticleChanges')->setName('api.article.discard')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/rename', ArticleApiController::class . ':renameArticle')->setName('api.article.rename')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/sort', ArticleApiController::class . ':sortArticle')->setName('api.article.sort')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article', ArticleApiController::class . ':createArticle')->setName('api.article.create')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/article', ArticleApiController::class . ':updateArticle')->setName('api.article.update')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/article', ArticleApiController::class . ':deleteArticle')->setName('api.article.delete')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/baseitem', ArticleApiController::class . ':createBaseItem')->setName('api.baseitem.create')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/navigation', ArticleApiController::class . ':getNavigation')->setName('api.navigation.get')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/post', ArticleApiController::class . ':createPost')->setName('api.post.create')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/markdown', ControllerAuthorArticleApi::class . ':getArticleMarkdown')->setName('api.article.markdown')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/html', ControllerAuthorArticleApi::class . ':getArticleHtml')->setName('api.article.html')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/publish', ControllerAuthorArticleApi::class . ':publishArticle')->setName('api.article.publish')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/article/unpublish', ControllerAuthorArticleApi::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/article/discard', ControllerAuthorArticleApi::class . ':discardArticleChanges')->setName('api.article.discard')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/rename', ControllerAuthorArticleApi::class . ':renameArticle')->setName('api.article.rename')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/sort', ControllerAuthorArticleApi::class . ':sortArticle')->setName('api.article.sort')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article', ControllerAuthorArticleApi::class . ':createArticle')->setName('api.article.create')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/article', ControllerAuthorArticleApi::class . ':updateArticle')->setName('api.article.update')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/article', ControllerAuthorArticleApi::class . ':deleteArticle')->setName('api.article.delete')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/baseitem', ControllerAuthorArticleApi::class . ':createBaseItem')->setName('api.baseitem.create')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/navigation', ControllerAuthorArticleApi::class . ':getNavigation')->setName('api.navigation.get')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/post', ControllerAuthorArticleApi::class . ':createPost')->setName('api.post.create')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/metadefinitions', MetaApiController::class . ':getMetaDefinitions')->setName('api.metadefinitions.get')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/article/metaobject', MetaApiController::class . ':getArticleMetaobject')->setName('api.articlemetaobject.get')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/article/metadata', MetaApiController::class . ':getArticleMeta')->setName('api.articlemeta.get')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/metadata', MetaApiController::class . ':updateArticleMeta')->setName('api.articlemeta.update')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/metadefinitions', ControllerAuthorMetaApi::class . ':getMetaDefinitions')->setName('api.metadefinitions.get')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/article/metaobject', ControllerAuthorMetaApi::class . ':getArticleMetaobject')->setName('api.articlemetaobject.get')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/article/metadata', ControllerAuthorMetaApi::class . ':getArticleMeta')->setName('api.articlemeta.get')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/article/metadata', ControllerAuthorMetaApi::class . ':updateArticleMeta')->setName('api.articlemeta.update')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/block', BlockApiController::class . ':addBlock')->setName('api.block.add')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/block', BlockApiController::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/block', BlockApiController::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/moveblock', BlockApiController::class . ':moveBlock')->setName('api.block.move')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/video', BlockApiController::class . ':saveVideoImage')->setName('api.video.save')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/block', ControllerAuthorBlockApi::class . ':addBlock')->setName('api.block.add')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/block', ControllerAuthorBlockApi::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/block', ControllerAuthorBlockApi::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/moveblock', ControllerAuthorBlockApi::class . ':moveBlock')->setName('api.block.move')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/video', ControllerAuthorBlockApi::class . ':saveVideoImage')->setName('api.video.save')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/medialib/images', MediaApiController::class . ':getMediaLibImages')->setName('api.medialibimg.get')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/medialib/files', MediaApiController::class . ':getMediaLibFiles')->setName('api.medialibfiles.get')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/image', MediaApiController::class . ':getImage')->setName('api.image.get')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/image', MediaApiController::class . ':createImage')->setName('api.image.create')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/image', MediaApiController::class . ':publishImage')->setName('api.image.publish')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/image', MediaApiController::class . ':deleteImage')->setName('api.image.delete')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/file', MediaApiController::class . ':getFile')->setName('api.file.get')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/file', MediaApiController::class . ':uploadFile')->setName('api.file.upload')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/file', MediaApiController::class . ':publishFile')->setName('api.file.publish')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/file', MediaApiController::class . ':deleteFile')->setName('api.file.delete')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/medialib/images', ControllerAuthorMediaApi::class . ':getMediaLibImages')->setName('api.medialibimg.get')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/medialib/files', ControllerAuthorMediaApi::class . ':getMediaLibFiles')->setName('api.medialibfiles.get')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/image', ControllerAuthorMediaApi::class . ':getImage')->setName('api.image.get')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/image', ControllerAuthorMediaApi::class . ':createImage')->setName('api.image.create')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/image', ControllerAuthorMediaApi::class . ':publishImage')->setName('api.image.publish')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/image', ControllerAuthorMediaApi::class . ':deleteImage')->setName('api.image.delete')->add(new RestrictApiAccess($container['router']));
$app->get('/api/v1/file', ControllerAuthorMediaApi::class . ':getFile')->setName('api.file.get')->add(new RestrictApiAccess($container['router']));
$app->post('/api/v1/file', ControllerAuthorMediaApi::class . ':uploadFile')->setName('api.file.upload')->add(new RestrictApiAccess($container['router']));
$app->put('/api/v1/file', ControllerAuthorMediaApi::class . ':publishFile')->setName('api.file.publish')->add(new RestrictApiAccess($container['router']));
$app->delete('/api/v1/file', ControllerAuthorMediaApi::class . ':deleteFile')->setName('api.file.delete')->add(new RestrictApiAccess($container['router']));

View File

@ -1,11 +1,10 @@
<?php
use Typemill\Controllers\PageController;
use Typemill\Controllers\FormController;
use Typemill\Controllers\SetupController;
use Typemill\Controllers\AuthController;
use Typemill\Controllers\SettingsController;
use Typemill\Controllers\ContentBackendController;
use Typemill\Controllers\ControllerAuthorEditor;
use Typemill\Controllers\ControllerSettings;
use Typemill\Controllers\ControllerFrontendWebsite;
use Typemill\Controllers\ControllerFrontendForms;
use Typemill\Controllers\ControllerFrontendAuth;
use Typemill\Controllers\ControllerFrontendSetup;
use Typemill\Middleware\RedirectIfUnauthenticated;
use Typemill\Middleware\RedirectIfAuthenticated;
use Typemill\Middleware\RedirectIfNoAdmin;
@ -13,57 +12,57 @@ use Typemill\Middleware\accessMiddleware;
if($settings['settings']['setup'])
{
$app->get('/setup', SetupController::class . ':show')->setName('setup.show');
$app->post('/setup', SetupController::class . ':create')->setName('setup.create');
$app->get('/setup', ControllerFrontendSetup::class . ':show')->setName('setup.show');
$app->post('/setup', ControllerFrontendSetup::class . ':create')->setName('setup.create');
}
else
{
$app->get('/setup', AuthController::class . ':redirect');
$app->get('/setup', ControllerFrontendAuth::class . ':redirect');
}
if($settings['settings']['welcome'])
{
$app->get('/setup/welcome', SetupController::class . ':welcome')->setName('setup.welcome')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/setup/welcome', ControllerFrontendSetup::class . ':welcome')->setName('setup.welcome')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
}
else
{
$app->get('/setup/welcome', AuthController::class . ':redirect')->setName('setup.welcome');
$app->get('/setup/welcome', ControllerFrontendAuth::class . ':redirect')->setName('setup.welcome');
}
$app->post('/tm/formpost', FormController::class . ':savePublicForm')->setName('form.save');
$app->post('/tm/formpost', ControllerFrontendForms::class . ':savePublicForm')->setName('form.save');
$app->get('/tm', AuthController::class . ':redirect');
$app->get('/tm/login', AuthController::class . ':show')->setName('auth.show')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->post('/tm/login', AuthController::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->get('/tm/logout', AuthController::class . ':logout')->setName('auth.logout')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm', ControllerFrontendAuth::class . ':redirect');
$app->get('/tm/login', ControllerFrontendAuth::class . ':show')->setName('auth.show')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->post('/tm/login', ControllerFrontendAuth::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->get('/tm/logout', ControllerFrontendAuth::class . ':logout')->setName('auth.logout')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
if(isset($settings['settings']['recoverpw']) && $settings['settings']['recoverpw'])
{
$app->get('/tm/recoverpw', AuthController::class . ':showrecoverpassword')->setName('auth.recoverpwshow')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->post('/tm/recoverpw', AuthController::class . ':recoverpassword')->setName('auth.recoverpw')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->get('/tm/recoverpwnew', AuthController::class . ':showrecoverpasswordnew')->setName('auth.recoverpwshownew')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->post('/tm/recoverpwnew', AuthController::class . ':createrecoverpasswordnew')->setName('auth.recoverpwnew')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->get('/tm/recoverpw', ControllerFrontendAuth::class . ':showrecoverpassword')->setName('auth.recoverpwshow')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->post('/tm/recoverpw', ControllerFrontendAuth::class . ':recoverpassword')->setName('auth.recoverpw')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->get('/tm/recoverpwnew', ControllerFrontendAuth::class . ':showrecoverpasswordnew')->setName('auth.recoverpwshownew')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->post('/tm/recoverpwnew', ControllerFrontendAuth::class . ':createrecoverpasswordnew')->setName('auth.recoverpwnew')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
}
$app->get('/tm/settings', SettingsController::class . ':showSettings')->setName('settings.show')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'view'));
$app->post('/tm/settings', SettingsController::class . ':saveSettings')->setName('settings.save')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'update'));
$app->get('/tm/settings', ControllerSettings::class . ':showSettings')->setName('settings.show')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'view'));
$app->post('/tm/settings', ControllerSettings::class . ':saveSettings')->setName('settings.save')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'update'));
$app->get('/tm/themes', SettingsController::class . ':showThemes')->setName('themes.show')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'view'));
$app->post('/tm/themes', SettingsController::class . ':saveThemes')->setName('themes.save')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'update'));
$app->get('/tm/themes', ControllerSettings::class . ':showThemes')->setName('themes.show')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'view'));
$app->post('/tm/themes', ControllerSettings::class . ':saveThemes')->setName('themes.save')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'update'));
$app->get('/tm/plugins', SettingsController::class . ':showPlugins')->setName('plugins.show')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'view'));
$app->post('/tm/plugins', SettingsController::class . ':savePlugins')->setName('plugins.save')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'update'));
$app->get('/tm/plugins', ControllerSettings::class . ':showPlugins')->setName('plugins.show')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'view'));
$app->post('/tm/plugins', ControllerSettings::class . ':savePlugins')->setName('plugins.save')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'update'));
$app->get('/tm/account', SettingsController::class . ':showAccount')->setName('user.account')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'view'));
$app->get('/tm/user/new', SettingsController::class . ':newUser')->setName('user.new')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'create'));
$app->post('/tm/user/create', SettingsController::class . ':createUser')->setName('user.create')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'create'));
$app->post('/tm/user/update', SettingsController::class . ':updateUser')->setName('user.update')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'update'));
$app->post('/tm/user/delete', SettingsController::class . ':deleteUser')->setName('user.delete')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'delete'));
$app->get('/tm/user/{username}', SettingsController::class . ':showUser')->setName('user.show')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'view'));
$app->get('/tm/users', SettingsController::class . ':listUser')->setName('user.list')->add(new accessMiddleware($container['router'], $container['acl'], 'userlist', 'view'));
$app->get('/tm/account', ControllerSettings::class . ':showAccount')->setName('user.account')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'view'));
$app->get('/tm/user/new', ControllerSettings::class . ':newUser')->setName('user.new')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'create'));
$app->post('/tm/user/create', ControllerSettings::class . ':createUser')->setName('user.create')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'create'));
$app->post('/tm/user/update', ControllerSettings::class . ':updateUser')->setName('user.update')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'update'));
$app->post('/tm/user/delete', ControllerSettings::class . ':deleteUser')->setName('user.delete')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'delete'));
$app->get('/tm/user/{username}', ControllerSettings::class . ':showUser')->setName('user.show')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'view'));
$app->get('/tm/users', ControllerSettings::class . ':listUser')->setName('user.list')->add(new accessMiddleware($container['router'], $container['acl'], 'userlist', 'view'));
$app->get('/tm/content/raw[/{params:.*}]', ContentBackendController::class . ':showContent')->setName('content.raw')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view'));
$app->get('/tm/content/visual[/{params:.*}]', ContentBackendController::class . ':showBlox')->setName('content.visual')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view'));
$app->get('/tm/content[/{params:.*}]', ContentBackendController::class . ':showEmpty')->setName('content.empty')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view'));
$app->get('/tm/content/raw[/{params:.*}]', ControllerAuthorEditor::class . ':showContent')->setName('content.raw')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view'));
$app->get('/tm/content/visual[/{params:.*}]', ControllerAuthorEditor::class . ':showBlox')->setName('content.visual')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view'));
$app->get('/tm/content[/{params:.*}]', ControllerAuthorEditor::class . ':showEmpty')->setName('content.empty')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view'));
foreach($routes as $pluginRoute)
{
@ -85,13 +84,13 @@ foreach($routes as $pluginRoute)
if($settings['settings']['setup'])
{
$app->get('/[{params:.*}]', SetupController::class . ':redirect');
$app->get('/[{params:.*}]', ControllerFrontendSetup::class . ':redirect');
}
elseif(isset($settings['settings']['access']) && $settings['settings']['access'] != '')
{
$app->get('/[{params:.*}]', PageController::class . ':index')->setName('home')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'view'));
$app->get('/[{params:.*}]', ControllerFrontendWebsite::class . ':index')->setName('home')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'view'));
}
else
{
$app->get('/[{params:.*}]', PageController::class . ':index')->setName('home');
$app->get('/[{params:.*}]', ControllerFrontendWebsite::class . ':index')->setName('home');
}

View File

@ -191,6 +191,8 @@ class Settings
'recovermessage' => true,
'recoverfrom' => true,
'securitylog' => true,
'oldslug' => true,
'refreshcache' => true,
];
# cleanup the existing usersettings

View File

@ -1,5 +1,19 @@
meta:
fields:
fieldsetnavi:
type: fieldset
legend: Navigation
fields:
navtitle:
type: text
label: Navigation Title
class: medium
maxlength: 60
hide:
type: checkbox
label: Hide
checkboxlabel: Hide page from navigation
class: medium
fieldsetcontent:
type: fieldset
legend: Meta-Content
@ -76,20 +90,6 @@ meta:
hidden: true
class: hidden
pattern: '[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
fieldsetnavi:
type: fieldset
legend: Navigation
fields:
navtitle:
type: text
label: Navigation Title
class: medium
maxlength: 60
hide:
type: checkbox
label: Hide
checkboxlabel: Hide page from navigation
class: medium
contains:
type: radio
label: This folder contains

View File

@ -261,6 +261,13 @@
<span class="checkmark"></span>
</label>
</div>
<div class="large{{ errors.settings.oldslug ? ' error' : '' }}">
<label for="settings[oldslug]">{{ __('Old Slug Logic') }}</label>
<label class="control-group">{{ __('Use the old logic from versions < 1.5.0 to create the slug') }}
<input name="settings[oldslug]" type="checkbox" {% if (settings.oldslug or old.settings.oldslug) %} checked {% endif %}>
<span class="checkmark"></span>
</label>
</div>
<div class="medium{{ errors.settings.twigcache ? ' error' : '' }}">
<label for="settings[twigcache]">{{ __('Twig Cache') }}</label>
<label class="control-group">{{ __('Activate Cache for Twig Templates') }}
@ -272,6 +279,13 @@
<div class="label">{{ __('Recreate cached files') }}</div>
<button id="clearcache" class="link bg-tm-green white dim bn br1 ph3 pv2 f6">{{ __('Recreate Cache') }}</button><div id="cacheresult" class="dib ph3 pv2"></div>
</div>
<div class="large{{ errors.settings.refreshcache ? ' error' : '' }}">
<label for="settings[refreshcache]">{{ __('Refresh Cache') }}</label>
<label class="control-group">{{ __('Refresh the cache after 10 minutes. Use this if you change content files manually e.g. with FTP.') }}
<input name="settings[refreshcache]" type="checkbox" {% if (settings.refreshcache or old.settings.refreshcache) %} checked {% endif %}>
<span class="checkmark"></span>
</label>
</div>
<div class="medium{{ errors.settings.proxy ? ' error' : '' }}">
<label for="settings[proxy]">{{ __('Proxy') }}</label>
<label class="control-group">{{ __('Use X-Forwarded Headers') }}