mirror of
https://github.com/typemill/typemill.git
synced 2025-07-31 19:30:40 +02:00
Version 1.2.16
This commit is contained in:
@@ -1,179 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Slim\Views\Twig;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\User;
|
||||
use Typemill\Models\WriteYaml;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
|
||||
public function redirect(Request $request, Response $response)
|
||||
{
|
||||
if(isset($_SESSION['login']))
|
||||
{
|
||||
return $response->withRedirect($this->c->router->pathFor('content.raw'));
|
||||
}
|
||||
else
|
||||
{
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* show login form
|
||||
*
|
||||
* @param obj $request the slim request object.
|
||||
* @param obj $response the slim response object.
|
||||
* @param array $args with arguments past to the slim router
|
||||
* @return obj $response and string route.
|
||||
*/
|
||||
|
||||
public function show(Request $request, Response $response, $args)
|
||||
{
|
||||
$data = array();
|
||||
|
||||
/* check previous login attemps */
|
||||
$yaml = new WriteYaml();
|
||||
$logins = $yaml->getYaml('settings/users', '.logins');
|
||||
$userIP = $this->getUserIP();
|
||||
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
|
||||
|
||||
if($userLogins)
|
||||
{
|
||||
/* get the latest */
|
||||
$lastLogin = intval($logins[$userIP][$userLogins-1]);
|
||||
|
||||
/* if last login is longer than 60 seconds ago, clear it. */
|
||||
if(time() - $lastLogin > 60)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
/* Did the user made three login attemps that failed? */
|
||||
elseif($userLogins >= 3)
|
||||
{
|
||||
$timeleft = 60 - (time() - $lastLogin);
|
||||
$data['messages'] = array('time' => $timeleft, 'error' => array( 'Too many bad logins. Please wait.'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render($response, '/auth/login.twig', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* signin an existing user
|
||||
*
|
||||
* @param obj $request the slim request object with form data in the post params.
|
||||
* @param obj $response the slim response object.
|
||||
* @return obj $response with redirect to route.
|
||||
*/
|
||||
|
||||
public function login(Request $request, Response $response)
|
||||
{
|
||||
/* log user attemps to authenticate */
|
||||
$yaml = new WriteYaml();
|
||||
$logins = $yaml->getYaml('settings/users', '.logins');
|
||||
$userIP = $this->getUserIP();
|
||||
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
|
||||
|
||||
/* if there have been user logins before. You have to do this again, because user does not always refresh the login page and old login attemps are stored. */
|
||||
if($userLogins)
|
||||
{
|
||||
/* get the latest */
|
||||
$lastLogin = intval($logins[$userIP][$userLogins-1]);
|
||||
|
||||
/* if last login is longer than 60 seconds ago, clear it and add this attempt */
|
||||
if(time() - $lastLogin > 60)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
/* Did the user made three login attemps that failed? */
|
||||
elseif($userLogins >= 2)
|
||||
{
|
||||
$logins[$userIP][] = time();
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
}
|
||||
|
||||
/* authentication */
|
||||
$params = $request->getParams();
|
||||
$validation = new Validation();
|
||||
|
||||
if($validation->signin($params))
|
||||
{
|
||||
$user = new User();
|
||||
$userdata = $user->getUser($params['username']);
|
||||
|
||||
if($userdata && password_verify($params['password'], $userdata['password']))
|
||||
{
|
||||
$user->login($userdata['username']);
|
||||
|
||||
/* clear the user login attemps */
|
||||
if($userLogins)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('content.raw'));
|
||||
}
|
||||
}
|
||||
|
||||
/* if authentication failed, add attempt to log file */
|
||||
$logins[$userIP][] = time();
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
|
||||
$this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.');
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
/**
|
||||
* log out a user
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function logout(Request $request, Response $response)
|
||||
{
|
||||
if(isset($_SESSION))
|
||||
{
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
private function getUserIP()
|
||||
{
|
||||
$client = @$_SERVER['HTTP_CLIENT_IP'];
|
||||
$forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
$remote = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if(filter_var($client, FILTER_VALIDATE_IP))
|
||||
{
|
||||
$ip = $client;
|
||||
}
|
||||
elseif(filter_var($forward, FILTER_VALIDATE_IP))
|
||||
{
|
||||
$ip = $forward;
|
||||
}
|
||||
else
|
||||
{
|
||||
$ip = $remote;
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Slim\Views\Twig;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\User;
|
||||
use Typemill\Models\WriteYaml;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
|
||||
public function redirect(Request $request, Response $response)
|
||||
{
|
||||
if(isset($_SESSION['login']))
|
||||
{
|
||||
return $response->withRedirect($this->c->router->pathFor('content.raw'));
|
||||
}
|
||||
else
|
||||
{
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* show login form
|
||||
*
|
||||
* @param obj $request the slim request object.
|
||||
* @param obj $response the slim response object.
|
||||
* @param array $args with arguments past to the slim router
|
||||
* @return obj $response and string route.
|
||||
*/
|
||||
|
||||
public function show(Request $request, Response $response, $args)
|
||||
{
|
||||
$data = array();
|
||||
|
||||
/* check previous login attemps */
|
||||
$yaml = new WriteYaml();
|
||||
$logins = $yaml->getYaml('settings/users', '.logins');
|
||||
$userIP = $this->getUserIP();
|
||||
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
|
||||
|
||||
if($userLogins)
|
||||
{
|
||||
/* get the latest */
|
||||
$lastLogin = intval($logins[$userIP][$userLogins-1]);
|
||||
|
||||
/* if last login is longer than 60 seconds ago, clear it. */
|
||||
if(time() - $lastLogin > 60)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
/* Did the user made three login attemps that failed? */
|
||||
elseif($userLogins >= 3)
|
||||
{
|
||||
$timeleft = 60 - (time() - $lastLogin);
|
||||
$data['messages'] = array('time' => $timeleft, 'error' => array( 'Too many bad logins. Please wait.'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render($response, '/auth/login.twig', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* signin an existing user
|
||||
*
|
||||
* @param obj $request the slim request object with form data in the post params.
|
||||
* @param obj $response the slim response object.
|
||||
* @return obj $response with redirect to route.
|
||||
*/
|
||||
|
||||
public function login(Request $request, Response $response)
|
||||
{
|
||||
/* log user attemps to authenticate */
|
||||
$yaml = new WriteYaml();
|
||||
$logins = $yaml->getYaml('settings/users', '.logins');
|
||||
$userIP = $this->getUserIP();
|
||||
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
|
||||
|
||||
/* if there have been user logins before. You have to do this again, because user does not always refresh the login page and old login attemps are stored. */
|
||||
if($userLogins)
|
||||
{
|
||||
/* get the latest */
|
||||
$lastLogin = intval($logins[$userIP][$userLogins-1]);
|
||||
|
||||
/* if last login is longer than 60 seconds ago, clear it and add this attempt */
|
||||
if(time() - $lastLogin > 60)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
/* Did the user made three login attemps that failed? */
|
||||
elseif($userLogins >= 2)
|
||||
{
|
||||
$logins[$userIP][] = time();
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
}
|
||||
|
||||
/* authentication */
|
||||
$params = $request->getParams();
|
||||
$validation = new Validation();
|
||||
|
||||
if($validation->signin($params))
|
||||
{
|
||||
$user = new User();
|
||||
$userdata = $user->getUser($params['username']);
|
||||
|
||||
if($userdata && password_verify($params['password'], $userdata['password']))
|
||||
{
|
||||
$user->login($userdata['username']);
|
||||
|
||||
/* clear the user login attemps */
|
||||
if($userLogins)
|
||||
{
|
||||
unset($logins[$userIP]);
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
}
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('content.raw'));
|
||||
}
|
||||
}
|
||||
|
||||
/* if authentication failed, add attempt to log file */
|
||||
$logins[$userIP][] = time();
|
||||
$yaml->updateYaml('settings/users', '.logins', $logins);
|
||||
|
||||
$this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.');
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
/**
|
||||
* log out a user
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function logout(Request $request, Response $response)
|
||||
{
|
||||
if(isset($_SESSION))
|
||||
{
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
|
||||
private function getUserIP()
|
||||
{
|
||||
$client = @$_SERVER['HTTP_CLIENT_IP'];
|
||||
$forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
$remote = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if(filter_var($client, FILTER_VALIDATE_IP))
|
||||
{
|
||||
$ip = $client;
|
||||
}
|
||||
elseif(filter_var($forward, FILTER_VALIDATE_IP))
|
||||
{
|
||||
$ip = $forward;
|
||||
}
|
||||
else
|
||||
{
|
||||
$ip = $remote;
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,160 +1,163 @@
|
||||
<?php
|
||||
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Show Content for raw editor
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function showContent(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->uri = $request->getUri();
|
||||
$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 )); }
|
||||
|
||||
# set information for homepage
|
||||
$this->setHomepage();
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
# 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;
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# add the modified date for the file
|
||||
$this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
$title = false;
|
||||
|
||||
# if content is an array, then it is a draft
|
||||
if(is_array($content))
|
||||
{
|
||||
# transform array to markdown
|
||||
$parsedown = new ParsedownExtension();
|
||||
$content = $parsedown->arrayBlocksToMarkdown($content);
|
||||
}
|
||||
|
||||
# if there is content
|
||||
if($content != '')
|
||||
{
|
||||
# normalize linebreaks
|
||||
$content = str_replace(array("\r\n", "\r"), "\n", $content);
|
||||
$content = trim($content, "\n");
|
||||
|
||||
# and strip out title
|
||||
if($content[0] == '#')
|
||||
{
|
||||
$contentParts = explode("\n", $content, 2);
|
||||
$title = trim($contentParts[0], "# \t\n\r\0\x0B");
|
||||
$content = trim($contentParts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render($response, 'editor/editor-raw.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Content for blox editor
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function showBlox(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->uri = $request->getUri();
|
||||
$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 )); }
|
||||
|
||||
# set information for homepage
|
||||
$this->setHomepage();
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# add the modified date for the file
|
||||
$this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
|
||||
if($content == '')
|
||||
{
|
||||
$content = [];
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
# if content is not an array, then transform it
|
||||
if(!is_array($content))
|
||||
{
|
||||
# turn markdown into an array of markdown-blocks
|
||||
$content = $parsedown->markdownToArrayBlocks($content);
|
||||
}
|
||||
|
||||
# needed for ToC links
|
||||
$relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
|
||||
|
||||
foreach($content as $key => $block)
|
||||
{
|
||||
/* parse markdown-file to content-array */
|
||||
$contentArray = $parsedown->text($block);
|
||||
|
||||
/* parse markdown-content-array to content-string */
|
||||
$content[$key] = $parsedown->markup($contentArray, $relurl);
|
||||
}
|
||||
|
||||
# extract title and delete from content array, array will start at 1 after that.
|
||||
$title = '# add title';
|
||||
if(isset($content[0]))
|
||||
{
|
||||
$title = $content[0];
|
||||
unset($content[0]);
|
||||
}
|
||||
|
||||
return $this->render($response, 'editor/editor-blox.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
}
|
||||
|
||||
public function showEmpty(Request $request, Response $response, $args)
|
||||
{
|
||||
return $this->renderIntern404($response, array( 'settings' => $this->settings ));
|
||||
}
|
||||
<?php
|
||||
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Show Content for raw editor
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function showContent(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->uri = $request->getUri();
|
||||
$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 )); }
|
||||
|
||||
# set information for homepage
|
||||
$this->setHomepage();
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
# 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;
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# add the modified date for the file
|
||||
$this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
$title = false;
|
||||
|
||||
# if content is an array, then it is a draft
|
||||
if(is_array($content))
|
||||
{
|
||||
# transform array to markdown
|
||||
$parsedown = new ParsedownExtension();
|
||||
$content = $parsedown->arrayBlocksToMarkdown($content);
|
||||
}
|
||||
|
||||
# if there is content
|
||||
if($content != '')
|
||||
{
|
||||
# normalize linebreaks
|
||||
$content = str_replace(array("\r\n", "\r"), "\n", $content);
|
||||
$content = trim($content, "\n");
|
||||
|
||||
# and strip out title
|
||||
if($content[0] == '#')
|
||||
{
|
||||
$contentParts = explode("\n", $content, 2);
|
||||
$title = trim($contentParts[0], "# \t\n\r\0\x0B");
|
||||
$content = trim($contentParts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render($response, 'editor/editor-raw.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Content for blox editor
|
||||
*
|
||||
* @param obj $request the slim request object
|
||||
* @param obj $response the slim response object
|
||||
* @return obje $response with redirect to route
|
||||
*/
|
||||
|
||||
public function showBlox(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->uri = $request->getUri();
|
||||
$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 )); }
|
||||
|
||||
# set information for homepage
|
||||
$this->setHomepage();
|
||||
|
||||
# set item
|
||||
if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
# set the status for published and drafted
|
||||
$this->setPublishStatus();
|
||||
|
||||
# set path
|
||||
$this->setItemPath($this->item->fileType);
|
||||
|
||||
# add the modified date for the file
|
||||
$this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
|
||||
|
||||
# read content from file
|
||||
if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
|
||||
|
||||
$content = $this->content;
|
||||
|
||||
if($content == '')
|
||||
{
|
||||
$content = [];
|
||||
}
|
||||
|
||||
# initialize parsedown extension
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
# to fix footnote-logic in parsedown, set visual mode to true
|
||||
$parsedown->setVisualMode();
|
||||
|
||||
# if content is not an array, then transform it
|
||||
if(!is_array($content))
|
||||
{
|
||||
# turn markdown into an array of markdown-blocks
|
||||
$content = $parsedown->markdownToArrayBlocks($content);
|
||||
}
|
||||
|
||||
# needed for ToC links
|
||||
$relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
|
||||
|
||||
foreach($content as $key => $block)
|
||||
{
|
||||
/* parse markdown-file to content-array */
|
||||
$contentArray = $parsedown->text($block);
|
||||
|
||||
/* parse markdown-content-array to content-string */
|
||||
$content[$key] = $parsedown->markup($contentArray, $relurl);
|
||||
}
|
||||
|
||||
# extract title and delete from content array, array will start at 1 after that.
|
||||
$title = '# add title';
|
||||
if(isset($content[0]))
|
||||
{
|
||||
$title = $content[0];
|
||||
unset($content[0]);
|
||||
}
|
||||
|
||||
return $this->render($response, 'editor/editor-blox.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings ));
|
||||
}
|
||||
|
||||
public function showEmpty(Request $request, Response $response, $args)
|
||||
{
|
||||
return $this->renderIntern404($response, array( 'settings' => $this->settings ));
|
||||
}
|
||||
}
|
@@ -1,370 +1,370 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Models\Write;
|
||||
use Typemill\Models\WriteCache;
|
||||
|
||||
abstract class ContentController
|
||||
{
|
||||
# holds the pimple container
|
||||
protected $c;
|
||||
|
||||
# holds the params from request
|
||||
protected $params;
|
||||
|
||||
# holds the slim-uri-object
|
||||
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;
|
||||
|
||||
# hold the page-item as an object
|
||||
protected $item;
|
||||
|
||||
# hold the breadcrumb as an object
|
||||
protected $breadcrumb;
|
||||
|
||||
# holds the path to the requested file
|
||||
protected $path = false;
|
||||
|
||||
# holds the content of the page
|
||||
protected $content;
|
||||
|
||||
public function __construct(ContainerInterface $c)
|
||||
{
|
||||
$this->c = $c;
|
||||
$this->settings = $this->c->get('settings');
|
||||
$this->structureLiveName = 'structure.txt';
|
||||
$this->structureDraftName = 'structure-draft.txt';
|
||||
}
|
||||
|
||||
protected function render($response, $route, $data)
|
||||
{
|
||||
if(isset($_SESSION['old']))
|
||||
{
|
||||
unset($_SESSION['old']);
|
||||
}
|
||||
|
||||
$response = $response->withoutHeader('Server');
|
||||
$response = $response->withoutHeader('X-Powered-By');
|
||||
|
||||
if($this->c->request->getUri()->getScheme() == 'https')
|
||||
{
|
||||
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
|
||||
}
|
||||
|
||||
$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');
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
protected function validateEditorInput()
|
||||
{
|
||||
$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]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateBlockInput()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->blockInput($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateNavigationSort()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->navigationSort($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateNaviItem()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->navigationItem($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
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)
|
||||
{
|
||||
# create an array of object with the whole content of the folder
|
||||
$structure = Folder::getFolderContentDetails($structure, $this->uri->getBaseUrl(), $this->uri->getBasePath());
|
||||
}
|
||||
|
||||
# cache navigation
|
||||
$this->write->updateCache('cache', $filename, 'lastCache.txt', $structure);
|
||||
}
|
||||
|
||||
$this->structure = $structure;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function setHomepage()
|
||||
{
|
||||
$contentFolder = Folder::scanFolderFlat($this->settings['rootPath'] . $this->settings['contentFolder']);
|
||||
|
||||
if(in_array('index.md', $contentFolder))
|
||||
{
|
||||
$md = true;
|
||||
$status = 'published';
|
||||
}
|
||||
if(in_array('index.txt', $contentFolder))
|
||||
{
|
||||
$txt = true;
|
||||
$status = 'unpublished';
|
||||
}
|
||||
if(isset($txt) && isset($md))
|
||||
{
|
||||
$status = 'modified';
|
||||
}
|
||||
|
||||
$active = false;
|
||||
if($this->params['url'] == '/' || $this->params['url'] == $this->uri->getBasePath() )
|
||||
{
|
||||
$active = 'active';
|
||||
}
|
||||
|
||||
$this->homepage = ['status' => $status, 'active' => $active];
|
||||
}
|
||||
|
||||
protected function setItem()
|
||||
{
|
||||
# if it is the homepage
|
||||
if($this->params['url'] == $this->uri->getBasePath() OR $this->params['url'] == '/')
|
||||
{
|
||||
$item = new \stdClass;
|
||||
$item->elementType = 'folder';
|
||||
$item->path = '';
|
||||
$item->urlRel = '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
# search for the url in the structure
|
||||
$item = Folder::getItemForUrl($this->structure, $this->params['url']);
|
||||
}
|
||||
|
||||
if($item)
|
||||
{
|
||||
if($item->elementType == 'file')
|
||||
{
|
||||
$pathParts = explode('.', $item->path);
|
||||
$fileType = array_pop($pathParts);
|
||||
$pathWithoutType = implode('.', $pathParts);
|
||||
$item->pathWithoutType = $pathWithoutType;
|
||||
}
|
||||
elseif($item->elementType == 'folder')
|
||||
{
|
||||
$item->pathWithoutItem = $item->path;
|
||||
$item->path = $item->path . DIRECTORY_SEPARATOR . 'index';
|
||||
$item->pathWithoutType = $item->path;
|
||||
}
|
||||
$this->item = $item;
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->errors = ['errors' => ['message' => 'requested page-url not found']];
|
||||
return false;
|
||||
}
|
||||
|
||||
# determine if you want to write to published file (md) or to draft (txt)
|
||||
protected function setItemPath($fileType)
|
||||
{
|
||||
$this->path = $this->item->pathWithoutType . '.' . $fileType;
|
||||
}
|
||||
|
||||
protected function setPublishStatus()
|
||||
{
|
||||
$this->item->published = false;
|
||||
$this->item->drafted = false;
|
||||
|
||||
if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.md'))
|
||||
{
|
||||
$this->item->published = true;
|
||||
|
||||
# add file-type in case it is a folder
|
||||
$this->item->fileType = "md";
|
||||
}
|
||||
|
||||
if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.txt'))
|
||||
{
|
||||
$this->item->drafted = true;
|
||||
|
||||
# add file-type in case it is a folder
|
||||
$this->item->fileType = "txt";
|
||||
}
|
||||
|
||||
if(!$this->item->drafted && !$this->item->published && $this->item->elementType == "folder")
|
||||
{
|
||||
# set txt as default for a folder, so that we can create an index.txt for a folder.
|
||||
$this->item->fileType = "txt";
|
||||
}
|
||||
}
|
||||
|
||||
protected function deleteContentFiles($fileTypes, $folder = false)
|
||||
{
|
||||
$basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
|
||||
|
||||
foreach($fileTypes as $fileType)
|
||||
{
|
||||
if(file_exists($basePath . $this->item->pathWithoutType . '.' . $fileType) && !unlink($basePath . $this->item->pathWithoutType . '.' . $fileType) )
|
||||
{
|
||||
$this->errors = ['message' => 'We could not delete the file, please check, if the file is writable.'];
|
||||
}
|
||||
}
|
||||
|
||||
if($this->errors)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function deleteContentFolder()
|
||||
{
|
||||
$basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
|
||||
$path = $basePath . $this->item->pathWithoutItem;
|
||||
|
||||
if(file_exists($path))
|
||||
{
|
||||
$files = array_diff(scandir($path), array('.', '..'));
|
||||
|
||||
# check if there are folders first, then stop the operation
|
||||
foreach ($files as $file)
|
||||
{
|
||||
if(is_dir(realpath($path) . DIRECTORY_SEPARATOR . $file))
|
||||
{
|
||||
$this->errors = ['message' => 'Please delete the sub-folder first.'];
|
||||
}
|
||||
}
|
||||
|
||||
if(!$this->errors)
|
||||
{
|
||||
foreach ($files as $file)
|
||||
{
|
||||
unlink(realpath($path) . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
return rmdir($path);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Models\Write;
|
||||
use Typemill\Models\WriteCache;
|
||||
|
||||
abstract class ContentController
|
||||
{
|
||||
# holds the pimple container
|
||||
protected $c;
|
||||
|
||||
# holds the params from request
|
||||
protected $params;
|
||||
|
||||
# holds the slim-uri-object
|
||||
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;
|
||||
|
||||
# hold the page-item as an object
|
||||
protected $item;
|
||||
|
||||
# hold the breadcrumb as an object
|
||||
protected $breadcrumb;
|
||||
|
||||
# holds the path to the requested file
|
||||
protected $path = false;
|
||||
|
||||
# holds the content of the page
|
||||
protected $content;
|
||||
|
||||
public function __construct(ContainerInterface $c)
|
||||
{
|
||||
$this->c = $c;
|
||||
$this->settings = $this->c->get('settings');
|
||||
$this->structureLiveName = 'structure.txt';
|
||||
$this->structureDraftName = 'structure-draft.txt';
|
||||
}
|
||||
|
||||
protected function render($response, $route, $data)
|
||||
{
|
||||
if(isset($_SESSION['old']))
|
||||
{
|
||||
unset($_SESSION['old']);
|
||||
}
|
||||
|
||||
$response = $response->withoutHeader('Server');
|
||||
$response = $response->withoutHeader('X-Powered-By');
|
||||
|
||||
if($this->c->request->getUri()->getScheme() == 'https')
|
||||
{
|
||||
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
|
||||
}
|
||||
|
||||
$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');
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
protected function validateEditorInput()
|
||||
{
|
||||
$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]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateBlockInput()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->blockInput($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateNavigationSort()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->navigationSort($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateNaviItem()
|
||||
{
|
||||
$validate = new Validation();
|
||||
$vResult = $validate->navigationItem($this->params);
|
||||
|
||||
if(is_array($vResult))
|
||||
{
|
||||
$message = reset($vResult);
|
||||
$this->errors = ['errors' => $vResult];
|
||||
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)
|
||||
{
|
||||
# create an array of object with the whole content of the folder
|
||||
$structure = Folder::getFolderContentDetails($structure, $this->uri->getBaseUrl(), $this->uri->getBasePath());
|
||||
}
|
||||
|
||||
# cache navigation
|
||||
$this->write->updateCache('cache', $filename, 'lastCache.txt', $structure);
|
||||
}
|
||||
|
||||
$this->structure = $structure;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function setHomepage()
|
||||
{
|
||||
$contentFolder = Folder::scanFolderFlat($this->settings['rootPath'] . $this->settings['contentFolder']);
|
||||
|
||||
if(in_array('index.md', $contentFolder))
|
||||
{
|
||||
$md = true;
|
||||
$status = 'published';
|
||||
}
|
||||
if(in_array('index.txt', $contentFolder))
|
||||
{
|
||||
$txt = true;
|
||||
$status = 'unpublished';
|
||||
}
|
||||
if(isset($txt) && isset($md))
|
||||
{
|
||||
$status = 'modified';
|
||||
}
|
||||
|
||||
$active = false;
|
||||
if($this->params['url'] == '/' || $this->params['url'] == $this->uri->getBasePath() )
|
||||
{
|
||||
$active = 'active';
|
||||
}
|
||||
|
||||
$this->homepage = ['status' => $status, 'active' => $active];
|
||||
}
|
||||
|
||||
protected function setItem()
|
||||
{
|
||||
# if it is the homepage
|
||||
if($this->params['url'] == $this->uri->getBasePath() OR $this->params['url'] == '/')
|
||||
{
|
||||
$item = new \stdClass;
|
||||
$item->elementType = 'folder';
|
||||
$item->path = '';
|
||||
$item->urlRel = '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
# search for the url in the structure
|
||||
$item = Folder::getItemForUrl($this->structure, $this->params['url']);
|
||||
}
|
||||
|
||||
if($item)
|
||||
{
|
||||
if($item->elementType == 'file')
|
||||
{
|
||||
$pathParts = explode('.', $item->path);
|
||||
$fileType = array_pop($pathParts);
|
||||
$pathWithoutType = implode('.', $pathParts);
|
||||
$item->pathWithoutType = $pathWithoutType;
|
||||
}
|
||||
elseif($item->elementType == 'folder')
|
||||
{
|
||||
$item->pathWithoutItem = $item->path;
|
||||
$item->path = $item->path . DIRECTORY_SEPARATOR . 'index';
|
||||
$item->pathWithoutType = $item->path;
|
||||
}
|
||||
$this->item = $item;
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->errors = ['errors' => ['message' => 'requested page-url not found']];
|
||||
return false;
|
||||
}
|
||||
|
||||
# determine if you want to write to published file (md) or to draft (txt)
|
||||
protected function setItemPath($fileType)
|
||||
{
|
||||
$this->path = $this->item->pathWithoutType . '.' . $fileType;
|
||||
}
|
||||
|
||||
protected function setPublishStatus()
|
||||
{
|
||||
$this->item->published = false;
|
||||
$this->item->drafted = false;
|
||||
|
||||
if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.md'))
|
||||
{
|
||||
$this->item->published = true;
|
||||
|
||||
# add file-type in case it is a folder
|
||||
$this->item->fileType = "md";
|
||||
}
|
||||
|
||||
if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.txt'))
|
||||
{
|
||||
$this->item->drafted = true;
|
||||
|
||||
# add file-type in case it is a folder
|
||||
$this->item->fileType = "txt";
|
||||
}
|
||||
|
||||
if(!$this->item->drafted && !$this->item->published && $this->item->elementType == "folder")
|
||||
{
|
||||
# set txt as default for a folder, so that we can create an index.txt for a folder.
|
||||
$this->item->fileType = "txt";
|
||||
}
|
||||
}
|
||||
|
||||
protected function deleteContentFiles($fileTypes, $folder = false)
|
||||
{
|
||||
$basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
|
||||
|
||||
foreach($fileTypes as $fileType)
|
||||
{
|
||||
if(file_exists($basePath . $this->item->pathWithoutType . '.' . $fileType) && !unlink($basePath . $this->item->pathWithoutType . '.' . $fileType) )
|
||||
{
|
||||
$this->errors = ['message' => 'We could not delete the file, please check, if the file is writable.'];
|
||||
}
|
||||
}
|
||||
|
||||
if($this->errors)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function deleteContentFolder()
|
||||
{
|
||||
$basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
|
||||
$path = $basePath . $this->item->pathWithoutItem;
|
||||
|
||||
if(file_exists($path))
|
||||
{
|
||||
$files = array_diff(scandir($path), array('.', '..'));
|
||||
|
||||
# check if there are folders first, then stop the operation
|
||||
foreach ($files as $file)
|
||||
{
|
||||
if(is_dir(realpath($path) . DIRECTORY_SEPARATOR . $file))
|
||||
{
|
||||
$this->errors = ['message' => 'Please delete the sub-folder first.'];
|
||||
}
|
||||
}
|
||||
|
||||
if(!$this->errors)
|
||||
{
|
||||
foreach ($files as $file)
|
||||
{
|
||||
unlink(realpath($path) . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
return rmdir($path);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
@@ -1,50 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
/* Use the slim-container */
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Views\Twig;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Typemill\Events\OnPageReady;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
protected $c;
|
||||
|
||||
public function __construct(ContainerInterface $c)
|
||||
{
|
||||
$this->c = $c;
|
||||
}
|
||||
|
||||
protected function render($response, $route, $data)
|
||||
{
|
||||
// $data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
|
||||
|
||||
if(isset($_SESSION['old']))
|
||||
{
|
||||
unset($_SESSION['old']);
|
||||
}
|
||||
|
||||
$response = $response->withoutHeader('Server');
|
||||
$response = $response->withoutHeader('X-Powered-By');
|
||||
|
||||
if($this->c->request->getUri()->getScheme() == 'https')
|
||||
{
|
||||
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
|
||||
}
|
||||
|
||||
$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');
|
||||
|
||||
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);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
/* Use the slim-container */
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Views\Twig;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Typemill\Events\OnPageReady;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
protected $c;
|
||||
|
||||
public function __construct(ContainerInterface $c)
|
||||
{
|
||||
$this->c = $c;
|
||||
}
|
||||
|
||||
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->withoutHeader('X-Powered-By');
|
||||
|
||||
if($this->c->request->getUri()->getScheme() == 'https')
|
||||
{
|
||||
$response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000');
|
||||
}
|
||||
|
||||
$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');
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@@ -1,135 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\WriteYaml;
|
||||
|
||||
class FormController extends Controller
|
||||
{
|
||||
/*************************************
|
||||
** SAVE THEME- AND PLUGIN-SETTINGS **
|
||||
*************************************/
|
||||
|
||||
public function savePublicForm($request, $response, $args)
|
||||
{
|
||||
if($request->isPost())
|
||||
{
|
||||
$params = $request->getParams();
|
||||
reset($params);
|
||||
$pluginName = key($params);
|
||||
$referer = $request->getHeader('HTTP_REFERER');
|
||||
|
||||
# simple bot check with honeypot
|
||||
if(isset($params[$pluginName]['personal-mail']))
|
||||
{
|
||||
if($params[$pluginName]['personal-mail'] != '')
|
||||
{
|
||||
$this->c->flash->addMessage('publicform', 'bot');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
unset($params[$pluginName]['personal-mail']);
|
||||
}
|
||||
|
||||
#recaptcha check
|
||||
if(isset($params['g-recaptcha-response']))
|
||||
{
|
||||
$recaptchaApi = 'https://www.google.com/recaptcha/api/siteverify';
|
||||
$settings = $this->c->get('settings');
|
||||
$secret = isset($settings['plugins'][$pluginName]['recaptcha_secretkey']) ? $settings['plugins'][$pluginName]['recaptcha_secretkey'] : false;
|
||||
$recaptchaRequest = ['secret' => $secret, 'response' => $params['g-recaptcha-response']];
|
||||
|
||||
# use key 'http' even if you send the request to https://...
|
||||
$options = array(
|
||||
'http' => array(
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($recaptchaRequest),
|
||||
'timeout' => 5
|
||||
)
|
||||
);
|
||||
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($recaptchaApi, false, $context);
|
||||
$result = json_decode($result);
|
||||
|
||||
if ($result === FALSE || $result->success === FALSE)
|
||||
{
|
||||
$this->c->flash->addMessage('publicform', 'bot');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($params[$pluginName]))
|
||||
{
|
||||
# validate the user-input
|
||||
$this->validateInput('plugins', $pluginName, $params[$pluginName]);
|
||||
}
|
||||
|
||||
# check for errors and redirect to path, if errors found
|
||||
if(isset($_SESSION['errors']))
|
||||
{
|
||||
$this->c->flash->addMessage('error', 'Please correct the errors');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
|
||||
# clean up and make sure that only validated data are stored
|
||||
$data = [ $pluginName => $params[$pluginName]];
|
||||
|
||||
# create write object
|
||||
$writeYaml = new WriteYaml();
|
||||
|
||||
# write the form data into yaml file
|
||||
$writeYaml->updateYaml('settings', 'formdata.yaml', $data);
|
||||
|
||||
# add message and return to original site
|
||||
$this->c->flash->addMessage('formdata', $pluginName);
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateInput($objectType, $objectName, $userInput)
|
||||
{
|
||||
# get settings and start validation
|
||||
$originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName);
|
||||
$userSettings = \Typemill\Settings::getUserSettings();
|
||||
$validate = new Validation();
|
||||
|
||||
if(isset($originalSettings['public']['fields']))
|
||||
{
|
||||
/* flaten the multi-dimensional array with fieldsets to a one-dimensional array */
|
||||
$originalFields = array();
|
||||
foreach($originalSettings['public']['fields'] as $fieldName => $fieldValue)
|
||||
{
|
||||
if(isset($fieldValue['fields']))
|
||||
{
|
||||
foreach($fieldValue['fields'] as $subFieldName => $subFieldValue)
|
||||
{
|
||||
$originalFields[$subFieldName] = $subFieldValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$originalFields[$fieldName] = $fieldValue;
|
||||
}
|
||||
}
|
||||
|
||||
/* take the user input data and iterate over all fields and values */
|
||||
foreach($userInput as $fieldName => $fieldValue)
|
||||
{
|
||||
/* get the corresponding field definition from original plugin settings */
|
||||
$fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false;
|
||||
|
||||
if($fieldDefinition)
|
||||
{
|
||||
/* validate user input for this field */
|
||||
$validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition);
|
||||
}
|
||||
if(!$fieldDefinition && $fieldName != 'active')
|
||||
{
|
||||
$_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\WriteYaml;
|
||||
|
||||
class FormController extends Controller
|
||||
{
|
||||
/*************************************
|
||||
** SAVE THEME- AND PLUGIN-SETTINGS **
|
||||
*************************************/
|
||||
|
||||
public function savePublicForm($request, $response, $args)
|
||||
{
|
||||
if($request->isPost())
|
||||
{
|
||||
$params = $request->getParams();
|
||||
reset($params);
|
||||
$pluginName = key($params);
|
||||
$referer = $request->getHeader('HTTP_REFERER');
|
||||
|
||||
# simple bot check with honeypot
|
||||
if(isset($params[$pluginName]['personal-mail']))
|
||||
{
|
||||
if($params[$pluginName]['personal-mail'] != '')
|
||||
{
|
||||
$this->c->flash->addMessage('publicform', 'bot');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
unset($params[$pluginName]['personal-mail']);
|
||||
}
|
||||
|
||||
#recaptcha check
|
||||
if(isset($params['g-recaptcha-response']))
|
||||
{
|
||||
$recaptchaApi = 'https://www.google.com/recaptcha/api/siteverify';
|
||||
$settings = $this->c->get('settings');
|
||||
$secret = isset($settings['plugins'][$pluginName]['recaptcha_secretkey']) ? $settings['plugins'][$pluginName]['recaptcha_secretkey'] : false;
|
||||
$recaptchaRequest = ['secret' => $secret, 'response' => $params['g-recaptcha-response']];
|
||||
|
||||
# use key 'http' even if you send the request to https://...
|
||||
$options = array(
|
||||
'http' => array(
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($recaptchaRequest),
|
||||
'timeout' => 5
|
||||
)
|
||||
);
|
||||
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($recaptchaApi, false, $context);
|
||||
$result = json_decode($result);
|
||||
|
||||
if ($result === FALSE || $result->success === FALSE)
|
||||
{
|
||||
$this->c->flash->addMessage('publicform', 'bot');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($params[$pluginName]))
|
||||
{
|
||||
# validate the user-input
|
||||
$this->validateInput('plugins', $pluginName, $params[$pluginName]);
|
||||
}
|
||||
|
||||
# check for errors and redirect to path, if errors found
|
||||
if(isset($_SESSION['errors']))
|
||||
{
|
||||
$this->c->flash->addMessage('error', 'Please correct the errors');
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
|
||||
# clean up and make sure that only validated data are stored
|
||||
$data = [ $pluginName => $params[$pluginName]];
|
||||
|
||||
# create write object
|
||||
$writeYaml = new WriteYaml();
|
||||
|
||||
# write the form data into yaml file
|
||||
$writeYaml->updateYaml('settings', 'formdata.yaml', $data);
|
||||
|
||||
# add message and return to original site
|
||||
$this->c->flash->addMessage('formdata', $pluginName);
|
||||
return $response->withRedirect($referer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateInput($objectType, $objectName, $userInput)
|
||||
{
|
||||
# get settings and start validation
|
||||
$originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName);
|
||||
$userSettings = \Typemill\Settings::getUserSettings();
|
||||
$validate = new Validation();
|
||||
|
||||
if(isset($originalSettings['public']['fields']))
|
||||
{
|
||||
/* flaten the multi-dimensional array with fieldsets to a one-dimensional array */
|
||||
$originalFields = array();
|
||||
foreach($originalSettings['public']['fields'] as $fieldName => $fieldValue)
|
||||
{
|
||||
if(isset($fieldValue['fields']))
|
||||
{
|
||||
foreach($fieldValue['fields'] as $subFieldName => $subFieldValue)
|
||||
{
|
||||
$originalFields[$subFieldName] = $subFieldValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$originalFields[$fieldName] = $fieldValue;
|
||||
}
|
||||
}
|
||||
|
||||
/* take the user input data and iterate over all fields and values */
|
||||
foreach($userInput as $fieldName => $fieldValue)
|
||||
{
|
||||
/* get the corresponding field definition from original plugin settings */
|
||||
$fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false;
|
||||
|
||||
if($fieldDefinition)
|
||||
{
|
||||
/* validate user input for this field */
|
||||
$validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition);
|
||||
}
|
||||
if(!$fieldDefinition && $fieldName != 'active')
|
||||
{
|
||||
$_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,236 +1,236 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Models\WriteCache;
|
||||
use Typemill\Models\WriteSitemap;
|
||||
use Typemill\Models\WriteYaml;
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Typemill\Models\VersionCheck;
|
||||
use Typemill\Models\Helpers;
|
||||
use Typemill\Models\Markdown;
|
||||
use Typemill\Events\OnPagetreeLoaded;
|
||||
use Typemill\Events\OnBreadcrumbLoaded;
|
||||
use Typemill\Events\OnItemLoaded;
|
||||
use Typemill\Events\OnOriginalLoaded;
|
||||
use Typemill\Events\OnMarkdownLoaded;
|
||||
use Typemill\Events\OnContentArrayLoaded;
|
||||
use Typemill\Events\OnHtmlLoaded;
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
|
||||
class PageController extends Controller
|
||||
{
|
||||
public function index($request, $response, $args)
|
||||
{
|
||||
/* Initiate Variables */
|
||||
$structure = false;
|
||||
$contentHTML = false;
|
||||
$item = false;
|
||||
$breadcrumb = false;
|
||||
$description = '';
|
||||
$settings = $this->c->get('settings');
|
||||
$pathToContent = $settings['rootPath'] . $settings['contentFolder'];
|
||||
$cache = new WriteCache();
|
||||
$uri = $request->getUri();
|
||||
$base_url = $uri->getBaseUrl();
|
||||
|
||||
try
|
||||
{
|
||||
/* if the cached structure is still valid, use it */
|
||||
if($cache->validate('cache', 'lastCache.txt',600))
|
||||
{
|
||||
$structure = $this->getCachedStructure($cache);
|
||||
}
|
||||
if(!isset($structure) OR !$structure)
|
||||
{
|
||||
/* if not, get a fresh structure of the content folder */
|
||||
$structure = $this->getFreshStructure($pathToContent, $cache, $uri);
|
||||
|
||||
/* 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>';
|
||||
|
||||
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());
|
||||
|
||||
/* check and update the typemill-version in the user settings */
|
||||
$this->updateVersion($uri->getBaseUrl());
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* if the user is on startpage */
|
||||
if(empty($args))
|
||||
{
|
||||
/* check, if there is an index-file in the root of the content folder */
|
||||
$contentMD = file_exists($pathToContent . DIRECTORY_SEPARATOR . 'index.md') ? file_get_contents($pathToContent . DIRECTORY_SEPARATOR . 'index.md') : NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* get the request url */
|
||||
$urlRel = $uri->getBasePath() . '/' . $args['params'];
|
||||
|
||||
/* find the url in the content-item-tree and return the item-object for the file */
|
||||
$item = Folder::getItemForUrl($structure, $urlRel);
|
||||
|
||||
/* if there is still no item, return a 404-page */
|
||||
if(!$item)
|
||||
{
|
||||
return $this->render404($response, array( 'navigation' => $structure, 'settings' => $settings, 'base_url' => $base_url ));
|
||||
}
|
||||
|
||||
/* get breadcrumb for page */
|
||||
$breadcrumb = Folder::getBreadcrumb($structure, $item->keyPathArray);
|
||||
$breadcrumb = $this->c->dispatcher->dispatch('onBreadcrumbLoaded', new OnBreadcrumbLoaded($breadcrumb))->getData();
|
||||
|
||||
/* add the paging to the item */
|
||||
$item = Folder::getPagingForItem($structure, $item);
|
||||
$item = $this->c->dispatcher->dispatch('onItemLoaded', new OnItemLoaded($item))->getData();
|
||||
|
||||
/* check if url is a folder. If so, check if there is an index-file in that folder */
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
$filePath = $pathToContent . $item->path . DIRECTORY_SEPARATOR . 'index.md';
|
||||
}
|
||||
elseif($item->elementType == 'file')
|
||||
{
|
||||
$filePath = $pathToContent . $item->path;
|
||||
}
|
||||
|
||||
/* add the modified date for the file */
|
||||
$item->modified = isset($filePath) ? filemtime($filePath) : false;
|
||||
|
||||
/* read the content of the file */
|
||||
$contentMD = isset($filePath) ? file_get_contents($filePath) : false;
|
||||
}
|
||||
|
||||
# dispatch the original content without plugin-manipulations for case anyone wants to use it
|
||||
$this->c->dispatcher->dispatch('onOriginalLoaded', new OnOriginalLoaded($contentMD));
|
||||
|
||||
$contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData();
|
||||
|
||||
/* initialize parsedown */
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
/* set safe mode to escape javascript and html in markdown */
|
||||
$parsedown->setSafeMode(true);
|
||||
|
||||
/* parse markdown-file to content-array */
|
||||
$contentArray = $parsedown->text($contentMD);
|
||||
$contentArray = $this->c->dispatcher->dispatch('onContentArrayLoaded', new OnContentArrayLoaded($contentArray))->getData();
|
||||
|
||||
/* get the first image from content array */
|
||||
$firstImage = $this->getFirstImage($contentArray);
|
||||
|
||||
$itemUrl = isset($item->urlRel) ? $item->urlRel : false;
|
||||
|
||||
/* parse markdown-content-array to content-string */
|
||||
$contentHTML = $parsedown->markup($contentArray, $itemUrl);
|
||||
$contentHTML = $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData();
|
||||
|
||||
/* extract the h1 headline*/
|
||||
$contentParts = explode("</h1>", $contentHTML);
|
||||
$title = isset($contentParts[0]) ? strip_tags($contentParts[0]) : $settings['title'];
|
||||
|
||||
$contentHTML = isset($contentParts[1]) ? $contentParts[1] : $contentHTML;
|
||||
|
||||
/* create excerpt from content */
|
||||
$excerpt = substr($contentHTML,0,500);
|
||||
|
||||
/* create description from excerpt */
|
||||
$description = isset($excerpt) ? strip_tags($excerpt) : false;
|
||||
if($description)
|
||||
{
|
||||
$description = trim(preg_replace('/\s+/', ' ', $description));
|
||||
$description = substr($description, 0, 300);
|
||||
$lastSpace = strrpos($description, ' ');
|
||||
$description = substr($description, 0, $lastSpace);
|
||||
}
|
||||
|
||||
/* get url and alt-tag for first image, if exists */
|
||||
if($firstImage)
|
||||
{
|
||||
preg_match('#\((.*?)\)#', $firstImage, $img_url);
|
||||
if($img_url[1])
|
||||
{
|
||||
preg_match('#\[(.*?)\]#', $firstImage, $img_alt);
|
||||
|
||||
$firstImage = array('img_url' => $base_url . '/' . $img_url[1], 'img_alt' => $img_alt[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$route = empty($args) && $settings['startpage'] ? '/cover.twig' : '/index.twig';
|
||||
|
||||
return $this->render($response, $route, array('navigation' => $structure, 'content' => $contentHTML, 'item' => $item, 'breadcrumb' => $breadcrumb, 'settings' => $settings, 'title' => $title, 'description' => $description, 'base_url' => $base_url, 'image' => $firstImage ));
|
||||
}
|
||||
|
||||
protected function getCachedStructure($cache)
|
||||
{
|
||||
return $cache->getCache('cache', 'structure.txt');
|
||||
}
|
||||
|
||||
protected function getFreshStructure($pathToContent, $cache, $uri)
|
||||
{
|
||||
/* scan the content of the folder */
|
||||
$structure = Folder::scanFolder($pathToContent);
|
||||
|
||||
/* if there is no content, render an empty page */
|
||||
if(count($structure) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* create an array of object with the whole content of the folder */
|
||||
$structure = Folder::getFolderContentDetails($structure, $uri->getBaseUrl(), $uri->getBasePath());
|
||||
|
||||
/* cache navigation */
|
||||
$cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure);
|
||||
|
||||
return $structure;
|
||||
}
|
||||
|
||||
protected function updateVersion($baseUrl)
|
||||
{
|
||||
/* check the latest public typemill version */
|
||||
$version = new VersionCheck();
|
||||
$latestVersion = $version->checkVersion($baseUrl);
|
||||
|
||||
if($latestVersion)
|
||||
{
|
||||
/* store latest version */
|
||||
\Typemill\Settings::updateSettings(array('latestVersion' => $latestVersion));
|
||||
}
|
||||
}
|
||||
|
||||
protected function getFirstImage(array $contentBlocks)
|
||||
{
|
||||
foreach($contentBlocks as $block)
|
||||
{
|
||||
/* is it a paragraph? */
|
||||
if(isset($block['name']) && $block['name'] == 'p')
|
||||
{
|
||||
if(isset($block['handler']['argument']) && substr($block['handler']['argument'], 0, 2) == '![' )
|
||||
{
|
||||
return $block['handler']['argument'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Models\WriteCache;
|
||||
use Typemill\Models\WriteSitemap;
|
||||
use Typemill\Models\WriteYaml;
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Typemill\Models\VersionCheck;
|
||||
use Typemill\Models\Helpers;
|
||||
use Typemill\Models\Markdown;
|
||||
use Typemill\Events\OnPagetreeLoaded;
|
||||
use Typemill\Events\OnBreadcrumbLoaded;
|
||||
use Typemill\Events\OnItemLoaded;
|
||||
use Typemill\Events\OnOriginalLoaded;
|
||||
use Typemill\Events\OnMarkdownLoaded;
|
||||
use Typemill\Events\OnContentArrayLoaded;
|
||||
use Typemill\Events\OnHtmlLoaded;
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
|
||||
class PageController extends Controller
|
||||
{
|
||||
public function index($request, $response, $args)
|
||||
{
|
||||
/* Initiate Variables */
|
||||
$structure = false;
|
||||
$contentHTML = false;
|
||||
$item = false;
|
||||
$breadcrumb = false;
|
||||
$description = '';
|
||||
$settings = $this->c->get('settings');
|
||||
$pathToContent = $settings['rootPath'] . $settings['contentFolder'];
|
||||
$cache = new WriteCache();
|
||||
$uri = $request->getUri();
|
||||
$base_url = $uri->getBaseUrl();
|
||||
|
||||
try
|
||||
{
|
||||
/* if the cached structure is still valid, use it */
|
||||
if($cache->validate('cache', 'lastCache.txt',600))
|
||||
{
|
||||
$structure = $this->getCachedStructure($cache);
|
||||
}
|
||||
if(!isset($structure) OR !$structure)
|
||||
{
|
||||
/* if not, get a fresh structure of the content folder */
|
||||
$structure = $this->getFreshStructure($pathToContent, $cache, $uri);
|
||||
|
||||
/* 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>';
|
||||
|
||||
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());
|
||||
|
||||
/* check and update the typemill-version in the user settings */
|
||||
$this->updateVersion($uri->getBaseUrl());
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* if the user is on startpage */
|
||||
if(empty($args))
|
||||
{
|
||||
/* check, if there is an index-file in the root of the content folder */
|
||||
$contentMD = file_exists($pathToContent . DIRECTORY_SEPARATOR . 'index.md') ? file_get_contents($pathToContent . DIRECTORY_SEPARATOR . 'index.md') : NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* get the request url */
|
||||
$urlRel = $uri->getBasePath() . '/' . $args['params'];
|
||||
|
||||
/* find the url in the content-item-tree and return the item-object for the file */
|
||||
$item = Folder::getItemForUrl($structure, $urlRel);
|
||||
|
||||
/* if there is still no item, return a 404-page */
|
||||
if(!$item)
|
||||
{
|
||||
return $this->render404($response, array( 'navigation' => $structure, 'settings' => $settings, 'base_url' => $base_url ));
|
||||
}
|
||||
|
||||
/* get breadcrumb for page */
|
||||
$breadcrumb = Folder::getBreadcrumb($structure, $item->keyPathArray);
|
||||
$breadcrumb = $this->c->dispatcher->dispatch('onBreadcrumbLoaded', new OnBreadcrumbLoaded($breadcrumb))->getData();
|
||||
|
||||
/* add the paging to the item */
|
||||
$item = Folder::getPagingForItem($structure, $item);
|
||||
$item = $this->c->dispatcher->dispatch('onItemLoaded', new OnItemLoaded($item))->getData();
|
||||
|
||||
/* check if url is a folder. If so, check if there is an index-file in that folder */
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
$filePath = $pathToContent . $item->path . DIRECTORY_SEPARATOR . 'index.md';
|
||||
}
|
||||
elseif($item->elementType == 'file')
|
||||
{
|
||||
$filePath = $pathToContent . $item->path;
|
||||
}
|
||||
|
||||
/* add the modified date for the file */
|
||||
$item->modified = isset($filePath) ? filemtime($filePath) : false;
|
||||
|
||||
/* read the content of the file */
|
||||
$contentMD = isset($filePath) ? file_get_contents($filePath) : false;
|
||||
}
|
||||
|
||||
# dispatch the original content without plugin-manipulations for case anyone wants to use it
|
||||
$this->c->dispatcher->dispatch('onOriginalLoaded', new OnOriginalLoaded($contentMD));
|
||||
|
||||
$contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData();
|
||||
|
||||
/* initialize parsedown */
|
||||
$parsedown = new ParsedownExtension();
|
||||
|
||||
/* set safe mode to escape javascript and html in markdown */
|
||||
$parsedown->setSafeMode(true);
|
||||
|
||||
/* parse markdown-file to content-array */
|
||||
$contentArray = $parsedown->text($contentMD);
|
||||
$contentArray = $this->c->dispatcher->dispatch('onContentArrayLoaded', new OnContentArrayLoaded($contentArray))->getData();
|
||||
|
||||
/* get the first image from content array */
|
||||
$firstImage = $this->getFirstImage($contentArray);
|
||||
|
||||
$itemUrl = isset($item->urlRel) ? $item->urlRel : false;
|
||||
|
||||
/* parse markdown-content-array to content-string */
|
||||
$contentHTML = $parsedown->markup($contentArray, $itemUrl);
|
||||
$contentHTML = $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData();
|
||||
|
||||
/* extract the h1 headline*/
|
||||
$contentParts = explode("</h1>", $contentHTML);
|
||||
$title = isset($contentParts[0]) ? strip_tags($contentParts[0]) : $settings['title'];
|
||||
|
||||
$contentHTML = isset($contentParts[1]) ? $contentParts[1] : $contentHTML;
|
||||
|
||||
/* create excerpt from content */
|
||||
$excerpt = substr($contentHTML,0,500);
|
||||
|
||||
/* create description from excerpt */
|
||||
$description = isset($excerpt) ? strip_tags($excerpt) : false;
|
||||
if($description)
|
||||
{
|
||||
$description = trim(preg_replace('/\s+/', ' ', $description));
|
||||
$description = substr($description, 0, 300);
|
||||
$lastSpace = strrpos($description, ' ');
|
||||
$description = substr($description, 0, $lastSpace);
|
||||
}
|
||||
|
||||
/* get url and alt-tag for first image, if exists */
|
||||
if($firstImage)
|
||||
{
|
||||
preg_match('#\((.*?)\)#', $firstImage, $img_url);
|
||||
if($img_url[1])
|
||||
{
|
||||
preg_match('#\[(.*?)\]#', $firstImage, $img_alt);
|
||||
|
||||
$firstImage = array('img_url' => $base_url . '/' . $img_url[1], 'img_alt' => $img_alt[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$route = empty($args) && $settings['startpage'] ? '/cover.twig' : '/index.twig';
|
||||
|
||||
return $this->render($response, $route, array('navigation' => $structure, 'content' => $contentHTML, 'item' => $item, 'breadcrumb' => $breadcrumb, 'settings' => $settings, 'title' => $title, 'description' => $description, 'base_url' => $base_url, 'image' => $firstImage ));
|
||||
}
|
||||
|
||||
protected function getCachedStructure($cache)
|
||||
{
|
||||
return $cache->getCache('cache', 'structure.txt');
|
||||
}
|
||||
|
||||
protected function getFreshStructure($pathToContent, $cache, $uri)
|
||||
{
|
||||
/* scan the content of the folder */
|
||||
$structure = Folder::scanFolder($pathToContent);
|
||||
|
||||
/* if there is no content, render an empty page */
|
||||
if(count($structure) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* create an array of object with the whole content of the folder */
|
||||
$structure = Folder::getFolderContentDetails($structure, $uri->getBaseUrl(), $uri->getBasePath());
|
||||
|
||||
/* cache navigation */
|
||||
$cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure);
|
||||
|
||||
return $structure;
|
||||
}
|
||||
|
||||
protected function updateVersion($baseUrl)
|
||||
{
|
||||
/* check the latest public typemill version */
|
||||
$version = new VersionCheck();
|
||||
$latestVersion = $version->checkVersion($baseUrl);
|
||||
|
||||
if($latestVersion)
|
||||
{
|
||||
/* store latest version */
|
||||
\Typemill\Settings::updateSettings(array('latestVersion' => $latestVersion));
|
||||
}
|
||||
}
|
||||
|
||||
protected function getFirstImage(array $contentBlocks)
|
||||
{
|
||||
foreach($contentBlocks as $block)
|
||||
{
|
||||
/* is it a paragraph? */
|
||||
if(isset($block['name']) && $block['name'] == 'p')
|
||||
{
|
||||
if(isset($block['handler']['argument']) && substr($block['handler']['argument'], 0, 2) == '![' )
|
||||
{
|
||||
return $block['handler']['argument'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,76 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\User;
|
||||
use Typemill\Models\Write;
|
||||
|
||||
class SetupController extends Controller
|
||||
{
|
||||
public function show($request, $response, $args)
|
||||
{
|
||||
/* make some checks befor you install */
|
||||
$checkFolder = new Write();
|
||||
|
||||
$systemcheck = array();
|
||||
|
||||
try{ $checkFolder->checkPath('settings'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('settings/users'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('content'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('cache'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('media'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
|
||||
$systemcheck = empty($systemcheck) ? false : $systemcheck;
|
||||
|
||||
return $this->render($response, 'auth/setup.twig', array( 'messages' => $systemcheck ));
|
||||
}
|
||||
|
||||
public function create($request, $response, $args)
|
||||
{
|
||||
if($request->isPost())
|
||||
{
|
||||
$params = $request->getParams();
|
||||
$validate = new Validation();
|
||||
$user = new User();
|
||||
|
||||
/* set user as admin */
|
||||
$params['userrole'] = 'administrator';
|
||||
|
||||
/* get userroles for validation */
|
||||
$userroles = $user->getUserroles();
|
||||
|
||||
/* validate user */
|
||||
if($validate->newUser($params, $userroles))
|
||||
{
|
||||
$userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']);
|
||||
|
||||
/* create initial user */
|
||||
$username = $user->createUser($userdata);
|
||||
|
||||
if($username)
|
||||
{
|
||||
/* login user */
|
||||
$user->login($username);
|
||||
|
||||
# create initial settings file
|
||||
\Typemill\Settings::createSettings();
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('setup.welcome'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->c->flash->addMessage('error', 'Please check your input and try again');
|
||||
return $response->withRedirect($this->c->router->pathFor('setup.show'));
|
||||
}
|
||||
}
|
||||
|
||||
public function welcome($request, $response, $args)
|
||||
{
|
||||
/* store updated settings */
|
||||
\Typemill\Settings::updateSettings(array('welcome' => false));
|
||||
|
||||
return $this->render($response, 'auth/welcome.twig', array());
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Typemill\Models\Validation;
|
||||
use Typemill\Models\User;
|
||||
use Typemill\Models\Write;
|
||||
|
||||
class SetupController extends Controller
|
||||
{
|
||||
public function show($request, $response, $args)
|
||||
{
|
||||
/* make some checks befor you install */
|
||||
$checkFolder = new Write();
|
||||
|
||||
$systemcheck = array();
|
||||
|
||||
try{ $checkFolder->checkPath('settings'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('settings/users'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('content'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('cache'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
try{ $checkFolder->checkPath('media'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
|
||||
|
||||
$systemcheck = empty($systemcheck) ? false : $systemcheck;
|
||||
|
||||
return $this->render($response, 'auth/setup.twig', array( 'messages' => $systemcheck ));
|
||||
}
|
||||
|
||||
public function create($request, $response, $args)
|
||||
{
|
||||
if($request->isPost())
|
||||
{
|
||||
$params = $request->getParams();
|
||||
$validate = new Validation();
|
||||
$user = new User();
|
||||
|
||||
/* set user as admin */
|
||||
$params['userrole'] = 'administrator';
|
||||
|
||||
/* get userroles for validation */
|
||||
$userroles = $user->getUserroles();
|
||||
|
||||
/* validate user */
|
||||
if($validate->newUser($params, $userroles))
|
||||
{
|
||||
$userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']);
|
||||
|
||||
/* create initial user */
|
||||
$username = $user->createUser($userdata);
|
||||
|
||||
if($username)
|
||||
{
|
||||
/* login user */
|
||||
$user->login($username);
|
||||
|
||||
# create initial settings file
|
||||
\Typemill\Settings::createSettings();
|
||||
|
||||
return $response->withRedirect($this->c->router->pathFor('setup.welcome'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->c->flash->addMessage('error', 'Please check your input and try again');
|
||||
return $response->withRedirect($this->c->router->pathFor('setup.show'));
|
||||
}
|
||||
}
|
||||
|
||||
public function welcome($request, $response, $args)
|
||||
{
|
||||
/* store updated settings */
|
||||
\Typemill\Settings::updateSettings(array('welcome' => false));
|
||||
|
||||
return $this->render($response, 'auth/welcome.twig', array());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user