1
0
mirror of https://github.com/typemill/typemill.git synced 2025-08-01 11:50:28 +02:00

V2 first draft

This commit is contained in:
trendschau
2022-10-14 20:24:25 +02:00
parent a5df46c6f7
commit 0b9af2a61f
66 changed files with 24631 additions and 491 deletions

View File

@@ -141,6 +141,7 @@ class User extends WriteYaml
if($user)
{
$user['lastlogin'] = time();
$user['tmpApiKey'] = ;
$_SESSION['user'] = $user['username'];
$_SESSION['role'] = $user['userrole'];

View File

@@ -31,6 +31,33 @@ abstract class Controller
}
/*
protected function setUrlCollection($uri)
{
$scheme = $uri->getScheme();
$authority = $uri->getAuthority();
$protocol = ($scheme ? $scheme . ':' : '') . ($authority ? '//' . $authority : '');
$this->basePath = $this->c->get('basePath');
$this->currentPath = $uri->getPath();
$this->fullBaseUrl = $protocol . $this->basePath;
$this->fullCurrentUrl = $protocol . $this->currentPath;
$this->urlCollection = [
'basePath' => $this->basePath,
'currentPath' => $this->currentPath,
'fullBaseUrl' => $this->fullBaseUrl,
'fullCurrentUrl' => $this->fullCurrentUrl
];
}
/*
# holds the pimple container
protected $c;

View File

@@ -0,0 +1,90 @@
<?php
namespace Typemill\Controllers;
use Typemill\Models\Yaml;
use Typemill\Events\OnSystemnaviLoaded;
# this controller handels data for web and api
# web will use data for twig output
# api will use data for json output
# data controller will provide neutral data
class ControllerData extends Controller
{
protected function getMainNavigation($userrole)
{
$yaml = new Yaml('\Typemill\Models\Storage');
$mainnavi = $yaml->getYaml('system/typemill/settings', 'mainnavi.yaml');
$allowedmainnavi = [];
$acl = $this->c->get('acl');
foreach($mainnavi as $name => $naviitem)
{
if($acl->isAllowed($userrole, $naviitem['aclresource'], $naviitem['aclprivilege']))
{
# not nice: check if the navi-item is active (e.g if segments like "content" or "system" is in current url)
if($name == 'content' && strpos($this->settings['routepath'], 'tm/content'))
{
$naviitem['active'] = true;
}
elseif($name == 'account' && strpos($this->settings['routepath'], 'tm/account'))
{
$naviitem['active'] = true;
}
elseif($name == 'system')
{
$naviitem['active'] = true;
}
$allowedmainnavi[$name] = $naviitem;
}
}
# if system is there, then we do not need the account item
if(isset($allowedmainnavi['system']))
{
unset($allowedmainnavi['account']);
}
# set correct editor mode according to user settings
if(isset($allowedmainnavi['content']) && $this->settings['editor'] == 'raw')
{
$allowedmainnavi['content']['routename'] = "content.raw";
}
return $allowedmainnavi;
}
protected function getSystemNavigation($userrole)
{
$yaml = new Yaml('\Typemill\Models\Storage');
$systemnavi = $yaml->getYaml('system/typemill/settings', 'systemnavi.yaml');
$systemnavi = $this->c->get('dispatcher')->dispatch(new OnSystemnaviLoaded($systemnavi), 'onSystemnaviLoaded')->getData();
$allowedsystemnavi = [];
$acl = $this->c->get('acl');
foreach($systemnavi as $name => $naviitem)
{
# check if the navi-item is active (e.g if segments like "content" or "system" is in current url)
# a bit fragile because url-segment and name/key in systemnavi.yaml and plugins have to be the same
if(strpos($this->settings['routepath'], 'tm/' . $name))
{
$naviitem['active'] = true;
}
if($acl->isAllowed($userrole, $naviitem['aclresource'], $naviitem['aclprivilege']))
{
$allowedsystemnavi[$name] = $naviitem;
}
}
return $allowedsystemnavi;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
<?php
namespace Typemill\Controllers;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
class ControllerSystemApi extends ControllerData
{
public function getSettings(Request $request, Response $response)
{
$response->getBody()->write(json_encode([
'settings' => $this->settings
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
}
public function getSystemNavi(Request $request, Response $response)
{
# won't work because api has no session, instead you have to pass user
$response->getBody()->write(json_encode([
'systemnavi' => $this->getSystemNavigation('member'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
}
public function getMainNavi(Request $request, Response $response)
{
$response->getBody()->write(json_encode([
'mainnavi' => $this->getMainNavigation('member'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
}
}

View File

@@ -10,6 +10,7 @@ class ControllerWeb extends Controller
{
public function __construct(Container $container)
{
/*
parent::__construct($container);
echo '<br>add twig';
@@ -47,8 +48,26 @@ class ControllerWeb extends Controller
return $twig;
});
protected function setUrlCollection($uri)
{
$scheme = $uri->getScheme();
$authority = $uri->getAuthority();
$protocol = ($scheme ? $scheme . ':' : '') . ($authority ? '//' . $authority : '');
$this->currentPath = $uri->getPath();
$this->fullBaseUrl = $protocol . $this->basePath;
$this->fullCurrentUrl = $protocol . $this->currentPath;
$this->urlCollection = [
'basePath' => $this->basePath,
'currentPath' => $this->currentPath,
'fullBaseUrl' => $this->fullBaseUrl,
'fullCurrentUrl' => $this->fullCurrentUrl
];
}
$this->c->get('dispatcher')->dispatch(new OnTwigLoaded(false), 'onTwigLoaded');
*/
}
}

View File

@@ -21,11 +21,10 @@ use Typemill\Events\OnHtmlLoaded;
use Typemill\Events\OnRestrictionsLoaded;
*/
class ControllerWebFrontend extends ControllerWeb
class ControllerWebFrontend extends Controller
{
public function index(Request $request, Response $response)
{
die('hallo');
return $this->c->get('view')->render($response, 'home.twig', [
'title' => 'Typemill Version 2',
'description' => 'Typemill Version 2 wird noch besser als Version 1.'

View File

@@ -5,104 +5,89 @@ namespace Typemill\Controllers;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Slim\Routing\RouteContext;
use Slim\Views\Twig;
use Typemill\Models\Validation;
use Typemill\Models\User;
use Typemill\Models\WriteYaml;
use Typemill\Extensions\ParsedownExtension;
class ControllerWebLogin extends ControllerWeb
class ControllerWebLogin extends Controller
{
# redirect if visit /setup route
public function redirect(Request $request, Response $response)
{
if(isset($_SESSION['login']))
{
return $response->withRedirect($this->c->router->pathFor('content.raw'));
return $response->withHeader('Location', $this->routeParser->urlFor('content.raw'))->withStatus(302);
}
else
{
return $response->withRedirect($this->c->router->pathFor('auth.show'));
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
}
}
/**
* 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)
public function show(Request $request, Response $response)
{
return $this->c->get('view')->render($response, 'login.twig', [
return $this->c->get('view')->render($response, 'auth/login.twig', [
#'captcha' => $this->checkIfAddCaptcha(),
#'url' => $this->urlCollection,
]);
# $settings = $this->c->get('settings');
# return $this->render($response, '/auth/login.twig', ['settings' => $settings]);
}
/**
* 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)
{
if( ( null !== $request->getattribute('csrf_result') ) OR ( $request->getattribute('csrf_result') === false ) )
{
$this->c->flash->addMessage('error', 'The form has a timeout, please try again.');
return $response->withRedirect($this->c->router->pathFor('auth.show'));
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'));
}
/* authentication */
$params = $request->getParams();
$input = $request->getParsedBody();
$validation = new Validation();
$settings = $this->c->get('settings');
if($validation->signin($params))
if($validation->signin($input))
{
$user = new User();
$userdata = $user->getUser($params['username']);
if($userdata && password_verify($params['password'], $userdata['password']))
if(!$user->setUserWithPassword($input['username']))
{
# return error
}
$userdata = $user->getUserData();
if($userdata && password_verify($input['password'], $userdata['password']))
{
# check if user has confirmed the account
if(isset($userdata['optintoken']) && $userdata['optintoken'])
{
$this->c->flash->addMessage('error', 'Your registration is not confirmed yet. Please check your e-mails and use the confirmation link.');
return $response->withRedirect($this->c->router->pathFor('auth.show'));
$this->c->get('flash')->addMessage('error', 'Your registration is not confirmed yet. Please check your e-mails and use the confirmation link.');
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
}
$user->login($userdata['username']);
$user->login();
return $response->withHeader('Location', $this->routeParser->urlFor('settings.show'))->withStatus(302);
/*
# if user is allowed to view content-area
if($this->c->acl->hasRole($userdata['userrole']) && $this->c->acl->isAllowed($userdata['userrole'], 'content', 'view'))
$acl = $this->c->get('acl');
if($acl->hasRole($userdata['userrole']) && $acl->isAllowed($userdata['userrole'], 'content', 'view'))
{
$settings = $this->c->get('settings');
$editor = (isset($settings['editor']) && $settings['editor'] == 'visual') ? 'visual' : 'raw';
$editor = (isset($this->settings['editor']) && $this->settings['editor'] == 'visual') ? 'visual' : 'raw';
return $response->withRedirect($this->c->router->pathFor('content.' . $editor));
return $response->withHeader('Location', $this->routeParser->urlFor('content.' . $editor))->withStatus(302);
}
return $response->withRedirect($this->c->router->pathFor('user.account'));
return $response->withHeader('Location', $this->routeParser->urlFor('user.account'))->withStatus(302);
*/
}
}
if(isset($this->settings['securitylog']) && $this->settings['securitylog'])
{
\Typemill\Models\Helpers::addLogEntry('wrong login');
\Typemill\Static\Helpers::addLogEntry('wrong login');
}
$this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.');
return $response->withRedirect($this->c->router->pathFor('auth.show'));
$this->c->get('flash')->addMessage('error', 'Ups, wrong password or username, please try again.');
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
}
}

View File

@@ -2,9 +2,10 @@
namespace Typemill\Extensions;
use Twig\Extension\AbstractExtension;
use Slim\Csrf\Guard;
class TwigCsrfExtension extends \Twig\Extension\AbstractExtension
class TwigCsrfExtension extends AbstractExtension
{
protected $csrf;
@@ -22,8 +23,8 @@ class TwigCsrfExtension extends \Twig\Extension\AbstractExtension
public function csrf()
{
$csrf = '<p>TokenNameValue: '. $this->csrf->getTokenName() .'</p><input type="hidden" name="' . $this->csrf->getTokenNameKey(). '" value="' . $this->csrf->getTokenName() . '">
<input type="hidden" name="' . $this->csrf->getTokenValueKey(). '" value="' . $this->csrf->getTokenValue(). '">';
$csrf = '<p>TokenNameValue: '. $this->csrf->getTokenName() .'</p><input type="hidden" id="csrf_name" name="' . $this->csrf->getTokenNameKey(). '" value="' . $this->csrf->getTokenName() . '">
<input type="hidden" id="csrf_value" name="' . $this->csrf->getTokenValueKey(). '" value="' . $this->csrf->getTokenValue(). '">';
return $csrf;
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Typemill\Extensions;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Typemill\Models\WriteYaml;
class TwigLanguageExtension extends AbstractExtension
{
protected $labels;
public function __construct($labels)
{
$this->labels = $labels;
}
public function getFilters()
{
return [
new TwigFilter('translate', [$this, 'translate'] ),
];
}
public function getFunctions()
{
return [
new TwigFunction('translate', array($this, 'translate' ))
];
}
public function translate( $label, $labels_from_plugin = NULL )
{
# replaces spaces, dots, comma and dash with underscores
$string = str_replace(" ", "_", $label);
$string = str_replace(".", "_", $string);
$string = str_replace(",", "_", $string);
$string = str_replace("-", "_", $string);
# transforms to uppercase
$string = strtoupper( $string );
# translates the string
if(isset($labels_from_plugin))
{
$translated_label = isset($labels_from_plugin[$string]) ? $labels_from_plugin[$string] : null;
}
else
{
$translated_label = isset($this->labels[$string]) ? $this->labels[$string] : null;
}
# if the string is not present, set the original string
if( empty($translated_label) )
{
$translated_label = $label;
}
# returns the string in the set language
return $translated_label;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Typemill\Extensions;
use Twig\Extension\AbstractExtension;
class TwigUrlExtension extends AbstractExtension
{
protected $uri;
protected $basepath;
protected $scheme;
protected $authority;
protected $protocol;
public function __construct($uri, $basepath)
{
$this->uri = $uri;
$this->basepath = $basepath;
$this->scheme = $uri->getScheme();
$this->authority = $uri->getAuthority();
$this->protocol = ($this->scheme ? $this->scheme . ':' : '') . ($this->authority ? '//' . $this->authority : '');
}
public function getFunctions()
{
return [
new \Twig\TwigFunction('base_url', array($this, 'baseUrl' )),
new \Twig\TwigFunction('current_url', array($this, 'currentUrl' )),
new \Twig\TwigFunction('current_path', array($this, 'currentPath' ))
];
}
public function baseUrl()
{
return $this->protocol . $this->basepath;
}
public function currentUrl()
{
return $this->protocol . $this->uri->getPath();
}
public function currentPath()
{
return $this->uri->getPath();
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace Typemill\Extensions;
use Twig\Extension\AbstractExtension;
class TwigUserExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new \Twig\TwigFunction('is_role', array($this, 'isRole' )),
new \Twig\TwigFunction('get_role', array($this, 'getRole' )),
new \Twig\TwigFunction('get_username', array($this, 'getUsername' )),
new \Twig\TwigFunction('is_loggedin', array($this, 'isLoggedin' ))
];
}
public function isLoggedin()
{
if(isset($_SESSION['login']) && $_SESSION['login'])
{
return true;
}
return false;
}
public function isRole($role)
{
if(isset($_SESSION['role']) && $_SESSION['role'] == $role)
{
return true;
}
return false;
}
public function getRole()
{
if(isset($_SESSION['role']))
{
return $_SESSION['role'];
}
return false;
}
public function getUsername()
{
if(isset($_SESSION['user']))
{
return $_SESSION['user'];
}
return false;
}
}

View File

@@ -1,56 +0,0 @@
<?php
namespace Typemill\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Routing\RouteContext;
class CreateSession implements MiddlewareInterface
{
protected $sessionSegments = [];
protected $routepath = false;
public function __construct($session_segments, $routepath)
{
$this->sessionSegments = $session_segments;
$this->routepath = $routepath;
}
public function process(Request $request, RequestHandler $handler) :response
{
foreach($this->sessionSegments as $segment)
{
if(substr( $this->routepath, 0, strlen($segment) ) === ltrim($segment, '/'))
{
echo '<br>Create Session';
// configure session
ini_set('session.cookie_httponly', 1 );
ini_set('session.use_strict_mode', 1);
ini_set('session.cookie_samesite', 'lax');
/*
if($uri->getScheme() == 'https')
{
ini_set('session.cookie_secure', 1);
session_name('__Secure-typemill-session');
}
else
{
session_name('typemill-session');
}
*/
// start session
session_start();
$request = $request->withAttribute('session', $_SESSION);
}
}
return $handler->handle($request);
}
}

View File

@@ -1,41 +0,0 @@
<?php
namespace Typemill\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Psr\Http\Server\MiddlewareInterface;
use Slim\Routing\RouteContext;
use Slim\Csrf\Guard;
class CsrfProtection implements MiddlewareInterface
{
protected $container;
protected $responseFactory;
public function __construct($container, $responseFactory)
{
$this->container = $container;
$this->responseFactory = $responseFactory;
}
public function process(Request $request, RequestHandler $handler) :response
{
if(is_array($request->getAttribute('session')))
{
echo '<br> csrf protection';
$responseFactory = $this->responseFactory;
# Register Middleware On Container
$this->container->set('csrf', function () use ($responseFactory)
{
return new Guard($responseFactory);
});
}
return $handler->handle($request);
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace Typemill\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Psr\Http\Server\MiddlewareInterface;
use Slim\Routing\RouteContext;
use Slim\Csrf\Guard;
class CsrfProtectionToMiddleware
{
protected $container;
public function __construct($container)
{
$this->container = $container;
}
public function __invoke(Request $request, RequestHandler $handler)
{
if(is_array($request->getAttribute('session')))
{
echo '<br> csrf protection to middleware';
return $this->container->get('csrf');
}
}
}

View File

@@ -2,31 +2,24 @@
namespace Typemill\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Psr\Http\Server\MiddlewareInterface;
use Slim\Flash\Messages;
use Slim\Views\Twig;
class FlashMessages implements MiddlewareInterface
{
protected $container;
public function __construct($container)
class FlashMessages
{
public function __construct(Twig $view)
{
$this->container = $container;
$this->view = $view;
}
public function process(Request $request, RequestHandler $handler) :response
public function __invoke(Request $request, RequestHandler $handler)
{
if(is_array($request->getAttribute('session')))
if(isset($_SESSION['slimFlash']) && is_array($_SESSION['slimFlash']))
{
echo '<br> flash messages';
$this->container->set('flash', function(){
return new Messages();
});
$this->view->getEnvironment()->addGlobal('flash', $_SESSION['slimFlash']);
unset($_SESSION['slimFlash']);
}
return $handler->handle($request);

View File

@@ -11,7 +11,7 @@ class JsonBodyParser implements MiddlewareInterface
{
public function process(Request $request, RequestHandler $handler) :response
{
echo '<br> JSON Body parser';
#echo '<br> JSON Body parser';
$contentType = $request->getHeaderLine('Content-Type');

View File

@@ -2,12 +2,11 @@
namespace Typemill\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Server\MiddlewareInterface;
use Slim\Routing\RouteParser;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Psr\Http\Server\MiddlewareInterface;
# use Slim\Routing\RouteContext;
use Slim\Routing\RouteParser;
use Slim\Psr7\Response;
class RedirectIfAuthenticated implements MiddlewareInterface
{
@@ -17,18 +16,25 @@ class RedirectIfAuthenticated implements MiddlewareInterface
$this->settings = $settings;
}
public function process(Request $request, RequestHandler $handler) :response
{
$response = $handler->handle($request);
public function process(Request $request, RequestHandler $handler) :Response
{
$editor = (isset($this->settings['editor']) && $this->settings['editor'] == 'visual') ? 'visual' : 'raw';
if(isset($_SESSION['login']))
$authenticated = (
(isset($_SESSION['username'])) &&
(isset($_SESSION['login']))
)
? true : false;
if($authenticated)
{
return $response->withHeader('Location', $this->router->pathFor('content.' . $editor))->withStatus(302);
# $response = $response->withRedirect($this->router->pathFor('content.' . $editor));
$response = new Response();
return $response->withHeader('Location', $this->router->urlFor('content.' . $editor))->withStatus(302);
}
$response = $handler->handle($request);
return $response;
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Typemill\Middleware;
use Psr\Http\Server\MiddlewareInterface;
use Slim\Routing\RouteParser;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Psr7\Response;
class RedirectIfUnauthenticated implements MiddlewareInterface
{
public function __construct(RouteParser $router)
{
$this->router = $router;
}
public function process(Request $request, RequestHandler $handler) :response
{
$authenticated = (
(isset($_SESSION['username'])) &&
(isset($_SESSION['login']))
)
? true : false;
if(!$authenticated)
{
# this executes only middleware code and not code from route
$response = new Response();
return $response->withHeader('Location', $this->router->urlFor('auth.show'))->withStatus(302);
}
# this executes code from routes first and then executes middleware
$response = $handler->handle($request);
return $response;
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace Typemill\Middleware;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Routing\RouteContext;
use Slim\Psr7\Response;
class RestrictApiAccess
{
public function __invoke(Request $request, RequestHandler $handler)
{
$routeContext = RouteContext::fromRequest($request);
$baseURL = $routeContext->getBasePath();
if ($request->hasHeader('X-Session-Auth')) {
session_start();
$authenticated = (
(isset($_SESSION['username'])) &&
(isset($_SESSION['userrole'])) &&
(isset($_SESSION['login']))
)
? true : false;
if($authenticated)
{
$response = $handler->handle($request);
return $response;
}
}
# elseif ($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') {
# advantage: all xhr-calls to the api will be session based
# no direct calls from javascript possible
# only from server
# }
$user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : false;
$apikey = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : false;
if($user && $apikey)
{
# get user
# check if user has tmpApiKey
# check if user has permanentApiKey
# check if user has tmpApiKey
# check if tmpApiKey has expired
# check if user keys are correct
$response = $handler->handle($request);
return $response;
}
$response = new Response();
$response->getBody()->write('Zugriff nicht erlaubt.');
return $response->withStatus(401);
}
}

View File

@@ -0,0 +1,336 @@
<?php
namespace Typemill\Models;
class Storage
{
protected $basepath;
public $error = false;
public function __construct()
{
$this->basepath = getcwd() . DIRECTORY_SEPARATOR;
}
public function checkFolder($folder)
{
$folderpath = $this->basepath . $folder;
if(!is_dir($folderpath) OR !is_writable($folderpath))
{
$this->error = "The folder $folder does not exist or is not writable.";
return false;
}
return true;
}
public function createFolder($folder)
{
$folderpath = $this->basepath . $folder;
if(is_dir($folderpath))
{
return true;
}
if(!mkdir($folderpath, 0755, true))
{
$this->error = "Could not create folder $folder.";
return false;
}
return true;
}
public function checkFile($folder, $filename)
{
if(!file_exists($this->basepath . $folder . DIRECTORY_SEPARATOR . $filename))
{
$this->error = "The file $filename in folder $folder does not exist.";
return false;
}
return true;
}
public function writeFile($folder, $filename, $data)
{
if(!$this->checkFolder($folder))
{
if(!$this->createFolder($folder))
{
return false;
}
}
$filepath = $this->basepath . $folder . DIRECTORY_SEPARATOR . $filename;
$openfile = @fopen($filepath, "w");
if(!$openfile)
{
$this->error = "Could not open and read the file $filename in folder $folder.";
return true;
}
fwrite($openfile, $data);
fclose($openfile);
return true;
}
public function getFile($folder, $filename)
{
if($this->checkFile($folder, $filename))
{
# ??? should be with basepath???
$fileContent = file_get_contents($folder . DIRECTORY_SEPARATOR . $filename);
return $fileContent;
}
return false;
}
public function renameFile($folder, $oldname, $newname)
{
$oldFilePath = $this->basepath . $folder . DIRECTORY_SEPARATOR . $oldname;
$newFilePath = $this->basepath . $folder . DIRECTORY_SEPARATOR . $newname;
if(!file_exists($oldFilePath))
{
return false;
}
if(!rename($oldFilePath, $newFilePath))
{
return false;
}
return true;
}
public function deleteFile($filepath)
{
if($this->checkFileWithPath($filepath))
{
unlink($this->basePath . $filepath);
return true;
}
return false;
}
public function getError()
{
return $this->error;
}
/*
public function checkPath($folder)
{
$folderPath = $this->basepath . $folder;
if(!is_dir($folderPath))
{
if(@mkdir($folderPath, 0774, true))
{
return true;
}
else
{
throw new \Exception("The folder '{$folderPath}' is missing and we could not create it. Please create the folder manually on your server.");
# return false;
}
}
if(@is_writable($folderPath))
{
return true;
}
else
{
throw new \Exception("Please make the folder '{$folderPath}' writable.");
# return false;
}
return true;
}
/*
public function checkFile($folder, $file)
{
if(!file_exists($this->basePath . $folder . DIRECTORY_SEPARATOR . $file))
{
return false;
}
return true;
}
public function checkFileWithPath($filepath)
{
if(!file_exists($this->basePath . $filepath))
{
return false;
}
return true;
}
public function writeFile($folder, $file, $data)
{
if($this->checkPath($folder))
{
$filePath = $this->basePath . $folder . DIRECTORY_SEPARATOR . $file;
$openFile = @fopen($filePath, "w");
if(!$openFile)
{
return false;
}
fwrite($openFile, $data);
fclose($openFile);
return true;
}
return false;
}
public function getFile($folderName, $fileName)
{
if($this->checkFile($folderName, $fileName))
{
$fileContent = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName);
return $fileContent;
}
return false;
}
public function getFileWithPath($filepath)
{
if($this->checkFileWithPath($filepath))
{
$fileContent = file_get_contents($filepath);
return $fileContent;
}
return false;
}
public function deleteFileWithPath($filepath)
{
if($this->checkFileWithPath($filepath))
{
unlink($this->basePath . $filepath);
return true;
}
return false;
}
public function renameFile($folder, $oldname, $newname)
{
$oldFilePath = $this->basePath . $folder . DIRECTORY_SEPARATOR . $oldname;
$newFilePath = $this->basePath . $folder . DIRECTORY_SEPARATOR . $newname;
if(!file_exists($oldFilePath))
{
return false;
}
if(@rename($oldFilePath, $newFilePath))
{
return true;
}
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');
# set new order as string
$newOrder = ($index < 10) ? '0' . $index : $index;
# create new path with foldername or filename but without file-type
# $newPath = $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . str_replace(" ", "-", $item->name);
$newPath = $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . $item->slug;
if($item->elementType == 'folder')
{
$oldPath = $this->basePath . 'content' . $item->path;
if(@rename($oldPath, $newPath))
{
return true;
}
return false;
}
# create old path but without filetype
$oldPath = substr($item->path, 0, strpos($item->path, "."));
$oldPath = $this->basePath . 'content' . $oldPath;
$result = true;
foreach($filetypes as $filetype)
{
$oldFilePath = $oldPath . '.' . $filetype;
$newFilePath = $newPath . '.' . $filetype;
#check if file with filetype exists and rename
if($oldFilePath != $newFilePath && file_exists($oldFilePath))
{
if(@rename($oldFilePath, $newFilePath))
{
$result = $result;
}
else
{
$result = false;
}
}
}
return $result;
}
*/
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Typemill\Models;
class StorageWrapper
{
public $object;
public function __construct(string $classname)
{
if (!class_exists($classname))
{
throw new \RuntimeException(sprintf('Callable class %s does not exist', $classname));
}
$this->object = new $classname();
}
function __call($method, $args)
{
if(!method_exists($this->object, $method))
{
throw new \RuntimeException(sprintf('Callable method %s does not exist', $method));
}
# Invoke original method on our proxied object
return call_user_func_array([$this->object, $method], $args);
}
}

View File

@@ -0,0 +1,485 @@
<?php
namespace Typemill\Models;
use Typemill\Models\Yaml;
class User
{
private $userDir;
private $yaml;
private $user = false;
private $password = false;
public $error = false;
public function __construct()
{
$this->userDir = getcwd() . '/system/settings/users';
$this->yaml = new Yaml('\Typemill\Models\Storage');
}
public function setUser(string $username)
{
if(!$this->user)
{
$this->user = $this->yaml->getYaml('settings/users', $username . '.yaml');
if(!$this->user)
{
$this->error = 'User not found';
return false;
}
# store password in private property so it is not accessible outside
$this->password = $this->user['password'];
# delete password from public userdata
unset($this->user['password']);
}
return $this;
}
public function setUserWithPassword(string $username)
{
if(!$this->user)
{
$this->user = $this->yaml->getYaml('settings/users', $username . '.yaml');
if(!$this->user)
{
$this->error = 'User not found.';
return false;
}
}
return $this;
}
public function getError()
{
return $this->error;
}
public function getUserData()
{
return $this->user;
}
public function getAllUsers()
{
# check if users directory exists
if(!is_dir($this->userDir))
{
$this->error = 'Directory $this->userDir does not exist.';
return false;
}
# get all user files
$userfiles = array_diff(scandir($this->userDir), array('..', '.', '.logins', 'tmuserindex-mail.txt', 'tmuserindex-role.txt'));
$usernames = [];
if(!empty($userfiles))
{
foreach($userfiles as $key => $userfile)
{
$usernames[] = str_replace('.yaml', '', $userfile);
}
usort($usernames, 'strnatcasecmp');
}
return $usernames;
}
public function createUser(array $params)
{
$params['password'] = $this->generatePassword($params['password']);
if($this->yaml->updateYaml('settings/users', $params['username'] . '.yaml', $params))
{
$this->deleteUserIndex();
return $params['username'];
}
$this->error = $this->yaml->getError();
return false;
}
public function unsetFromUser(array $keys)
{
if(empty($keys) OR !$this->user)
{
$this->error = 'Keys are empty or user does not exist.';
return false;
}
foreach($keys as $key)
{
if(isset($this->user[$key]))
{
unset($this->user[$key]);
}
}
$this->yaml->updateYaml('settings/users', $this->user['username'] . '.yaml', $this->user);
return true;
}
public function updateUser()
{
# add password back to userdata before you store/update user
if($this->password)
{
$this->user['password'] = $this->password;
}
if($this->yaml->updateYaml('settings/users', $this->user['username'] . '.yaml', $this->user))
{
return true;
}
$this->error = $this->yaml->getError();
return false;
}
public function updateUserWithInput(array $input)
{
if(!isset($input['username']) OR !$this->user)
{
return false;
}
# make sure new password is not stored
if(isset($input['newpassword']))
{
unset($input['newpassword']);
}
# make sure password is set correctly
if(isset($input['password']))
{
if(empty($input['password']))
{
unset($input['password']);
}
else
{
$input['password'] = $this->generatePassword($input['password']);
}
}
# set old password back to original userdate
if($this->password)
{
$this->user['password'] = $this->password;
}
# overwrite old userdata with new userdata
$updatedUser = array_merge($this->user, $input);
# cleanup data here
$this->updateYaml('settings/users', $this->user['username'] . '.yaml', $updatedUser);
$this->deleteUserIndex();
# if user updated his own profile, update session data
if(isset($_SESSION['username']) && $_SESSION['username'] == $input['username'])
{
$_SESSION['userrole'] = $updatedUser['userrole'];
if(isset($updatedUser['firstname']))
{
$_SESSION['firstname'] = $updatedUser['firstname'];
}
if(isset($updatedUser['lastname']))
{
$_SESSION['lastname'] = $updatedUser['lastname'];
}
}
return $this->user['username'];
}
public function deleteUser(string $username)
{
if($this->getUser($username))
{
unlink('settings/users/' . $username . '.yaml');
$this->deleteUserIndex();
return true;
}
return false;
}
public function login()
{
if($this->user)
{
$this->user['lastlogin'] = time();
# $this->user['internalApiKey'] = bin2hex(random_bytes(32));
$_SESSION['username'] = $this->user['username'];
# $_SESSION['userrole'] = $this->user['userrole'];
$_SESSION['login'] = $this->user['lastlogin'];
/*
if(isset($this->user['firstname']))
{
$_SESSION['firstname'] = $this->user['firstname'];
}
if(isset($this->user['lastname']))
{
$_SESSION['lastname'] = $this->user['lastname'];
}
*/
if(isset($this->user['recovertoken']) OR isset($this->user['recoverdate']))
{
$this->unsetFromUser($this->user['username'], ['recovertoken', 'recoverdate']);
}
# update user last login
$this->updateUser();
}
}
public function generatePassword($password)
{
return \password_hash($password, PASSWORD_DEFAULT, ['cost' => 10]);
}
public function getBasicAuth()
{
$basicauth = $this->user['username'] . ":" . $this->user['internalApiKey'];
return base64_encode($basicauth);
}
# accepts email with or without asterix and returns userdata
public function findUsersByEmail($email)
{
$usernames = [];
# Make sure that we scan only the first 11 files even if there are some thousand users.
if ($dh = opendir($this->userDir))
{
$count = 1;
$exclude = array('..', '.', '.logins', 'tmuserindex-mail.txt', 'tmuserindex-role.txt');
while ( ($userfile = readdir($dh)) !== false && $count <= 11 )
{
if(in_array($userfile, $exclude)){ continue; }
$usernames[] = str_replace('.yaml', '', $userfile);
$count++;
}
closedir($dh);
}
$countusers = count($usernames);
if($countusers == 0)
{
return false;
}
# use a simple dirty search if there are less than 10 users (only in use for new user registrations)
if($countusers <= 10)
{
foreach($usernames as $username)
{
$userdata = $this->getSecureUser($username);
if($userdata['email'] == $email)
{
return $userdata;
}
}
return false;
}
# if there are more than 10 users, search with an index
$usermails = $this->getUserMailIndex();
# search with starting asterix, ending asterix or without asterix
if($email[0] == '*')
{
$userdata = [];
$search = substr($email, 1);
$length = strlen($search);
foreach($usermails as $usermail => $username)
{
if(substr($usermail, -$length) == $search)
{
$userdata[] = $username;
}
}
$userdata = empty($userdata) ? false : $userdata;
return $userdata;
}
elseif(substr($email, -1) == '*')
{
$userdata = [];
$search = substr($email, 0, -1);
$length = strlen($search);
foreach($usermails as $usermail => $username)
{
if(substr($usermail, 0, $length) == $search)
{
$userdata[] = $username;
}
}
$userdata = empty($userdata) ? false : $userdata;
return $userdata;
}
elseif(isset($usermails[$email]))
{
$userdata[] = $usermails[$email];
return $userdata;
}
return false;
}
public function getUserMailIndex()
{
if(file_exists($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-mail.txt'))
{
# unserialize and return the file
$usermailindex = unserialize(file_get_contents($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-mail.txt'));
if($usermailindex)
{
return $usermailindex;
}
}
$usernames = $this->getUsers();
$usermailindex = [];
foreach($usernames as $username)
{
$userdata = $this->getSecureUser($username);
$usermailindex[$userdata['email']] = $username;
}
file_put_contents($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-mail.txt', serialize($usermailindex));
return $usermailindex;
}
# accepts email with or without asterix and returns usernames
public function findUsersByRole($role)
{
/*
# get all user files
$usernames = $this->getUsers();
$countusers = count($usernames);
if($countusers == 0)
{
return false;
}
# use a simple dirty search if there are less than 10 users (not in use right now)
if($countusers <= 4)
{
$userdata = [];
foreach($usernames as $key => $username)
{
$userdetails = $this->getSecureUser($username);
if($userdetails['userrole'] == $role)
{
$userdata[] = $userdetails;
}
}
if(empty($userdata))
{
return false;
}
return $userdata;
}
*/
$userroles = $this->getUserRoleIndex();
if(isset($userroles[$role]))
{
return $userroles[$role];
}
return false;
}
public function getUserRoleIndex()
{
if(file_exists($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-role.txt'))
{
# unserialize and return the file
$userroleindex = unserialize(file_get_contents($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-role.txt'));
if($userroleindex)
{
return $userroleindex;
}
}
$usernames = $this->getUsers();
$userroleindex = [];
foreach($usernames as $username)
{
$userdata = $this->getSecureUser($username);
$userroleindex[$userdata['userrole']][] = $username;
}
file_put_contents($this->userDir . DIRECTORY_SEPARATOR . 'tmuserindex-role.txt', serialize($userroleindex));
return $userroleindex;
}
protected function deleteUserIndex()
{
$userDir = __DIR__ . '/../../settings/users';
if(file_exists($userDir . DIRECTORY_SEPARATOR . 'tmuserindex-mail.txt'))
{
# read and return the file
unlink($userDir . DIRECTORY_SEPARATOR . 'tmuserindex-mail.txt');
}
}
# deprecated
public function getSecureUser(string $username)
{
$user = $this->getYaml('settings/users', $username . '.yaml');
unset($user['password']);
return $user;
}
}

View File

@@ -0,0 +1,609 @@
<?php
namespace Typemill\Models;
use Typemill\Models\User;
use Valitron\Validator;
class Validation
{
/**
* Constructor with custom validation rules
*
* @param obj $db the database connection.
*/
public function __construct()
{
$user = new User();
Validator::langDir(getcwd() . '/system/vendor/vlucas/valitron/lang'); // always set langDir before lang.
Validator::lang('en');
Validator::addRule('values_allowed', function($field, $value, array $params, array $fields) use ($user)
{
$badvalues = array_diff($value, $params[0]);
if(empty($badvalues)){ return true; }
return false;
}, 'invalid values');
Validator::addRule('image_types', function($field, $value, array $params, array $fields) use ($user)
{
$allowed = ['jpg', 'jpeg', 'png', 'webp', 'svg'];
$pathinfo = pathinfo($value);
$extension = strtolower($pathinfo['extension']);
if(in_array($extension, $allowed)){ return true; }
return false;
}, 'only jpg, jpeg, png, webp, svg allowed');
# checks if email is available if user is created
Validator::addRule('emailAvailable', function($field, $value, array $params, array $fields) use ($user)
{
$email = trim($value);
if($user->findUsersByEmail($email)){ return false; }
return true;
}, 'taken');
# checks if email is available if userdata is updated
Validator::addRule('emailChanged', function($field, $value, array $params, array $fields) use ($user)
{
$userdata = $user->getSecureUser($fields['username']);
if($userdata['email'] == $value){ return true; } # user has not updated his email
$email = trim($value);
if($user->findUsersByEmail($email)){ return false; }
return true;
}, 'taken');
# checks if username is free when create new user
Validator::addRule('userAvailable', function($field, $value, array $params, array $fields) use ($user)
{
$activeUser = $user->getUser($value);
$inactiveUser = $user->getUser("_" . $value);
if($activeUser OR $inactiveUser){ return false; }
return true;
}, 'taken');
# checks if user exists when userdata is updated
Validator::addRule('userExists', function($field, $value, array $params, array $fields) use ($user)
{
$userdata = $user->getUser($value);
if($userdata){ return true; }
return false;
}, 'does not exist');
Validator::addRule('iplist', function($field, $value, array $params, array $fields) use ($user)
{
$iplist = explode(",", $value);
foreach($iplist as $ip)
{
if( filter_var( trim($ip), \FILTER_VALIDATE_IP) === false)
{
return false;
}
}
return true;
}, 'contains one or more invalid ip-adress.');
Validator::addRule('customfields', function($field, $customfields, array $params, array $fields) use ($user)
{
if(empty($customfields))
{
return true;
}
foreach($customfields as $key => $value)
{
if(!isset($key) OR empty($key) OR (preg_match('/^([a-z0-9])+$/i', $key) == false) )
{
return false;
}
if (!isset($value) OR empty($value) OR ( $value != strip_tags($value) ) )
{
return false;
}
}
return true;
}, 'some fields are empty or contain invalid values.');
Validator::addRule('checkPassword', function($field, $value, array $params, array $fields) use ($user)
{
$userdata = $user->getUser($fields['username']);
if($userdata && password_verify($value, $userdata['password'])){ return true; }
return false;
}, 'wrong password');
Validator::addRule('navigation', function($field, $value, array $params, array $fields)
{
$format = '/[@#^*()=\[\]{};:"\\|,.<>\/]/';
if ( preg_match($format, $value))
{
return false;
}
return true;
}, 'contains special characters');
Validator::addRule('noSpecialChars', function($field, $value, array $params, array $fields)
{
$format = '/[!@#$%^&*()_+=\[\]{};\':"\\|,.<>\/?]/';
if ( preg_match($format, $value))
{
return false;
}
return true;
}, 'contains special characters');
Validator::addRule('noHTML', function($field, $value, array $params, array $fields)
{
if ( $value == strip_tags($value) )
{
return true;
}
return false;
}, 'contains html');
Validator::addRule('markdownSecure', function($field, $value, array $params, array $fields)
{
/* strip out code blocks and blockquotes */
$value = preg_replace('/`{4,}[\s\S]+?`{4,}/', '', $value);
$value = preg_replace('/`{3,}[\s\S]+?`{3,}/', '', $value);
$value = preg_replace('/`{2,}[\s\S]+?`{2,}/', '', $value);
$value = preg_replace('/`{1,}[\s\S]+?`{1,}/', '', $value);
$value = preg_replace('/>[\s\S]+?[\n\r]/', '', $value);
if ( $value == strip_tags($value) )
{
return true;
}
return false;
}, 'not secure. For code please use markdown `inline-code` or ````fenced code blocks````.');
}
# return valitron standard object
public function returnValidator(array $params)
{
return new Validator($params);
}
/**
* validation for signup form
*
* @param array $params with form data.
* @return obj $v the validation object passed to a result method.
*/
public function signin(array $params)
{
$v = new Validator($params);
$v->rule('required', ['username', 'password'])->message("Required");
$v->rule('alphaNum', 'username')->message("Invalid characters");
$v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20");
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
if($v->validate())
{
return true;
}
else
{
return false;
}
}
/**
* validation for signup form
*
* @param array $params with form data.
* @return obj $v the validation object passed to a result method.
*/
public function newUser(array $params, $userroles)
{
$v = new Validator($params);
$v->rule('required', ['username', 'email', 'password'])->message("required");
$v->rule('alphaNum', 'username')->message("invalid characters");
$v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20");
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
$v->rule('userAvailable', 'username')->message("User already exists");
$v->rule('noHTML', 'firstname')->message(" contains HTML");
$v->rule('lengthBetween', 'firstname', 2, 40);
$v->rule('noHTML', 'lastname')->message(" contains HTML");
$v->rule('lengthBetween', 'lastname', 2, 40);
$v->rule('email', 'email')->message("e-mail is invalid");
$v->rule('emailAvailable', 'email')->message("Email already taken");
$v->rule('in', 'userrole', $userroles);
return $this->validationResult($v);
}
public function existingUser(array $params, $userroles)
{
$v = new Validator($params);
$v->rule('required', ['username', 'email', 'userrole'])->message("required");
$v->rule('alphaNum', 'username')->message("invalid");
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
$v->rule('userExists', 'username')->message("user does not exist");
$v->rule('noHTML', 'firstname')->message(" contains HTML");
$v->rule('lengthBetween', 'firstname', 2, 40);
$v->rule('noHTML', 'lastname')->message(" contains HTML");
$v->rule('lengthBetween', 'lastname', 2, 40);
$v->rule('email', 'email')->message("e-mail is invalid");
$v->rule('emailChanged', 'email')->message("Email already taken");
$v->rule('in', 'userrole', $userroles);
return $this->validationResult($v);
}
public function username($username)
{
$v = new Validator($username);
$v->rule('alphaNum', 'username')->message("Only alpha-numeric characters allowed");
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
return $this->validationResult($v);
}
/**
* validation for changing the password
*
* @param array $params with form data.
* @return obj $v the validation object passed to a result method.
*/
public function newPassword(array $params)
{
$v = new Validator($params);
$v->rule('required', ['password', 'newpassword']);
$v->rule('lengthBetween', 'newpassword', 5, 20);
$v->rule('checkPassword', 'password')->message("Password is wrong");
return $this->validationResult($v);
}
/**
* validation for password recovery
*
* @param array $params with form data.
* @return obj $v the validation object passed to a result method.
*/
public function recoverPassword(array $params)
{
$v = new Validator($params);
$v->rule('required', ['password', 'passwordrepeat']);
$v->rule('lengthBetween', 'password', 5, 20);
$v->rule('equals', 'passwordrepeat', 'password');
return $this->validationResult($v);
}
/**
* validation for system settings
*
* @param array $params with form data.
* @return obj $v the validation object passed to a result method.
*/
public function settings(array $params, array $copyright, array $formats, $name = false)
{
$v = new Validator($params);
$v->rule('required', ['title', 'author', 'copyright', 'year', 'editor']);
$v->rule('lengthBetween', 'title', 2, 50);
$v->rule('lengthBetween', 'author', 2, 50);
$v->rule('noHTML', 'title');
# $v->rule('regex', 'title', '/^[\pL0-9_ \-]*$/u');
$v->rule('regex', 'author', '/^[\pL_ \-]*$/u');
$v->rule('integer', 'year');
$v->rule('length', 'year', 4);
$v->rule('length', 'langattr', 2);
$v->rule('in', 'editor', ['raw', 'visual']);
$v->rule('values_allowed', 'formats', $formats);
$v->rule('in', 'copyright', $copyright);
$v->rule('noHTML', 'restrictionnotice');
$v->rule('lengthBetween', 'restrictionnotice', 2, 1000 );
$v->rule('email', 'recoverfrom');
$v->rule('noHTML', 'recoversubject');
$v->rule('lengthBetween', 'recoversubject', 2, 80 );
$v->rule('noHTML', 'recovermessage');
$v->rule('lengthBetween', 'recovermessage', 2, 1000 );
$v->rule('iplist', 'trustedproxies');
return $this->validationResult($v, $name);
}
/**
* validation for content editor
*
* @param array $params with form data.
* @return true or $v->errors with array of errors to use in json-response
*/
public function editorInput(array $params)
{
$v = new Validator($params);
$v->rule('required', ['title', 'content', 'url']);
$v->rule('lengthBetween', 'title', 2, 100);
$v->rule('noHTML', 'title');
$v->rule('markdownSecure', 'content');
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
}
public function blockInput(array $params)
{
$v = new Validator($params);
$v->rule('required', ['markdown', 'block_id', 'url']);
$v->rule('markdownSecure', 'markdown');
$v->rule('regex', 'block_id', '/^[0-9.]+$/i');
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
}
/**
* validation for resort navigation
*
* @param array $params with form data.
* @return true or $v->errors with array of errors to use in json-response
*/
public function navigationSort(array $params)
{
$v = new Validator($params);
$v->rule('required', ['item_id', 'parent_id_from', 'parent_id_to']);
$v->rule('regex', 'item_id', '/^[0-9.]+$/i');
$v->rule('regex', 'parent_id_from', '/^[a-zA-Z0-9.]+$/i');
$v->rule('regex', 'parent_id_to', '/^[a-zA-Z0-9.]+$/i');
$v->rule('integer', 'index_new');
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
}
/**
* validation for new navigation items
*
* @param array $params with form data.
* @return true or $v->errors with array of errors to use in json-response
*/
public function navigationItem(array $params)
{
$v = new Validator($params);
$v->rule('required', ['folder_id', 'item_name', 'type', 'url']);
$v->rule('regex', 'folder_id', '/^[0-9.]+$/i');
# $v->rule('noSpecialChars', 'item_name');
$v->rule('navigation', 'item_name');
$v->rule('lengthBetween', 'item_name', 1, 60);
$v->rule('in', 'type', ['file', 'folder']);
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
}
public function navigationBaseItem(array $params)
{
$v = new Validator($params);
$v->rule('required', ['item_name', 'type', 'url']);
# $v->rule('noSpecialChars', 'item_name');
$v->rule('navigation', 'item_name');
$v->rule('lengthBetween', 'item_name', 1, 40);
$v->rule('in', 'type', ['file', 'folder']);
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
}
/**
* validation for dynamic fields ( settings for themes and plugins)
*
* @param string $fieldName with the name of the field.
* @param array or string $fieldValue with the values of the field.
* @param string $objectName with the name of the plugin or theme.
* @param array $fieldDefinitions with the field definitions as multi-dimensional array.
* @return obj $v the validation object passed to a result method.
*/
public function objectField($fieldName, $fieldValue, $objectName, $fieldDefinitions, $skiprequired = NULL)
{
$v = new Validator(array($fieldName => $fieldValue));
if(isset($fieldDefinitions['required']) && !$skiprequired)
{
$v->rule('required', $fieldName);
}
if(isset($fieldDefinitions['maxlength']))
{
$v->rule('lengthMax', $fieldName, $fieldDefinitions['maxlength']);
}
if(isset($fieldDefinitions['max']))
{
$v->rule('max', $fieldName, $fieldDefinitions['max']);
}
if(isset($fieldDefinitions['min']))
{
$v->rule('min', $fieldName, $fieldDefinitions['min']);
}
if(isset($fieldDefinitions['pattern']))
{
$v->rule('regex', $fieldName, '/^' . $fieldDefinitions['pattern'] . '$/');
}
switch($fieldDefinitions['type'])
{
case "select":
/* create array with option keys as value */
$options = array();
foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; }
$v->rule('in', $fieldName, $options);
break;
case "radio":
$v->rule('in', $fieldName, $fieldDefinitions['options']);
break;
case "checkboxlist":
if(isset($fieldValue) && is_array($fieldValue))
{
/* create array with option keys as value */
$options = array();
foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; }
/* loop over input values and check, if the options of the field definitions (options for checkboxlist) contains the key (input from user, key is used as value, value is used as label) */
foreach($fieldValue as $key => $value)
{
$v->rule('in', $key, $options);
}
}
break;
case "color":
$v->rule('regex', $fieldName, '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/');
break;
case "email":
$v->rule('email', $fieldName);
break;
case "date":
$v->rule('date', $fieldName);
break;
case "checkbox":
if(isset($fieldDefinitions['required']))
{
$v->rule('accepted', $fieldName);
}
break;
case "url":
$v->rule('url', $fieldName);
$v->rule('lengthMax', $fieldName, 200);
break;
case "text":
$v->rule('noHTML', $fieldName);
$v->rule('lengthMax', $fieldName, 500);
# $v->rule('regex', $fieldName, '/^[\pL0-9_ \-\.\?\!\/\:]*$/u');
break;
case "textarea":
# it understands array, json, yaml
if(is_array($fieldValue))
{
$v = $this->checkArray($fieldValue, $v);
}
else
{
$v->rule('noHTML', $fieldName);
$v->rule('lengthMax', $fieldName, 10000);
}
break;
case "paragraph":
$v->rule('noHTML', $fieldName);
$v->rule('lengthMax', $fieldName, 10000);
break;
case "password":
$v->rule('lengthMax', $fieldName, 100);
break;
case "image":
$v->rule('noHTML', $fieldName);
$v->rule('lengthMax', $fieldName, 1000);
$v->rule('image_types', $fieldName);
break;
case "customfields":
$v->rule('array', $fieldName);
$v->rule('customfields', $fieldName);
break;
default:
$v->rule('lengthMax', $fieldName, 1000);
$v->rule('regex', $fieldName, '/^[\pL0-9_ \-]*$/u');
}
return $this->validationResult($v, $objectName);
}
/**
* result for validation
*
* @param obj $v the validation object.
* @return bool
*/
public function checkArray($arrayvalues, $v)
{
foreach($arrayvalues as $key => $value)
{
if(is_array($value))
{
$this->checkArray($value, $v);
}
$v->rule('noHTML', $value);
$v->rule('lengthMax', $value, 1000);
}
return $v;
}
public function validationResult($v, $name = false)
{
if($v->validate())
{
return true;
}
else
{
if($name == 'meta')
{
return $v->errors();
}
elseif($name)
{
if(isset($_SESSION['errors'][$name]))
{
foreach ($v->errors() as $key => $val)
{
$_SESSION['errors'][$name][$key] = $val;
break;
}
}
else
{
$_SESSION['errors'][$name] = $v->errors();
}
}
else
{
$_SESSION['errors'] = $v->errors();
}
return false;
}
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Typemill\Models;
class Yaml extends StorageWrapper
{
/**
* Get the a yaml file.
* @param string $fileName is the name of the Yaml Folder.
* @param string $yamlFileName is the name of the Yaml File.
*/
public function getYaml($folderName, $yamlFileName)
{
$yaml = $this->getFile($folderName, $yamlFileName);
if($yaml)
{
return \Symfony\Component\Yaml\Yaml::parse($yaml);
}
return false;
}
/**
* Writes a yaml file.
* @param string $fileName is the name of the Yaml Folder.
* @param string $yamlFileName is the name of the Yaml File.
* @param array $contentArray is the content as an array.
*/
public function updateYaml($folderName, $yamlFileName, $contentArray)
{
$yaml = \Symfony\Component\Yaml\Yaml::dump($contentArray,6);
if($this->writeFile($folderName, $yamlFileName, $yaml))
{
return true;
}
return false;
}
}

View File

@@ -2,8 +2,89 @@
namespace Typemill\Static;
use Typemill\Models\StorageWrapper;
class Helpers{
public static 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;
}
public static function addLogEntry($action)
{
$line = self::getUserIP();
$line .= ';' . date("Y-m-d H:i:s");
$line .= ';' . $action;
$storage = new StorageWrapper('\Typemill\Models\Storage');
$logfile = $storage->getFile('cache', 'securitylog.txt');
if($logfile)
{
$logfile .= $line . PHP_EOL;
}
else
{
$logfile = $line . PHP_EOL;
}
$storage->writeFile('cache', 'securitylog.txt', $logfile);
}
public static function array_sort($array, $on, $order=SORT_ASC)
{
$new_array = array();
$sortable_array = array();
if (count($array) > 0) {
foreach ($array as $k => $v) {
if (is_array($v)) {
foreach ($v as $k2 => $v2) {
if ($k2 == $on) {
$sortable_array[$k] = $v2;
}
}
} else {
$sortable_array[$k] = $v;
}
}
switch ($order) {
case SORT_ASC:
asort($sortable_array);
break;
case SORT_DESC:
arsort($sortable_array);
break;
}
foreach ($sortable_array as $k => $v) {
$new_array[] = $array[$k];
}
}
return $new_array;
}
public static function printTimer($timer)
{
$lastTime = NULL;

View File

@@ -1,24 +0,0 @@
<?php
namespace Typemill\Static;
class Languages
{
public static function whichLanguage()
{
# Check which languages are available
$langs = [];
$path = __DIR__ . '/author/languages/*.yaml';
foreach (glob($path) as $filename)
{
$langs[] = basename($filename,'.yaml');
}
# Detect browser language
$accept_lang = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2) : false;
$lang = in_array($accept_lang, $langs) ? $accept_lang : 'en';
return $lang;
}
}

View File

@@ -4,8 +4,9 @@ namespace Typemill\Static;
class Plugins
{
public static function loadPlugins($rootpath)
public static function loadPlugins()
{
$rootpath = getcwd();
$pluginFolder = self::scanPluginFolder($rootpath);
$classNames = [];

View File

@@ -10,11 +10,11 @@ class Session
foreach($sessionSegments as $segment)
{
echo '<br>' . $segment;
echo '<br>' . $routepath;
#echo '<br>' . $segment;
#echo '<br>' . $routepath;
if(substr( $routepath, 0, strlen($segment) ) === ltrim($segment, '/'))
{
echo '<br>Create Session';
#echo '<br>Create Session';
# configure session
ini_set('session.cookie_httponly', 1 );

View File

@@ -4,8 +4,9 @@ namespace Typemill\Static;
class Settings
{
public static function loadSettings($rootpath)
public static function loadSettings()
{
$rootpath = getcwd();
$defaultsettings = self::getDefaultSettings($rootpath);
$usersettings = self::getUserSettings($rootpath);

View File

@@ -0,0 +1,110 @@
<?php
namespace Typemill\Static;
use Typemill\Models\Yaml;
class Translations
{
public static function loadTranslations($settings)
{
$yaml = new Yaml($settings['storage']);
$urlsegments = explode('/',trim($settings['routepath'],'/'));
$environment = 'frontend';
if( ($urlsegments[0] === 'tm' OR $urlsegments[0] === 'setup') )
{
$environment = 'admin';
}
$language = self::whichLanguage();
if(isset($settings['language']))
{
$language = $settings['language'];
}
$theme_translations = [];
$system_translations = [];
$plugins_translations = [];
# theme labels selected according to the environment: admin or user
$theme_language_folder = 'themes' . DIRECTORY_SEPARATOR . $settings['theme'] . DIRECTORY_SEPARATOR . 'languages' . DIRECTORY_SEPARATOR . $environment . DIRECTORY_SEPARATOR;
$theme_language_file = $language . '.yaml';
if (file_exists($theme_language_folder . $theme_language_file))
{
$theme_translations = $yaml->getYaml($theme_language_folder, $theme_language_file);
}
if($environment == 'admin')
{
$system_language_folder = 'system' . DIRECTORY_SEPARATOR . 'typemill' . DIRECTORY_SEPARATOR . 'author' . DIRECTORY_SEPARATOR . 'translations' . DIRECTORY_SEPARATOR;
$system_language_file = $language . '.yaml';
if(file_exists($system_language_folder . $system_language_file))
{
$system_translations = $yaml->getYaml($system_language_folder, $system_language_file);
}
# Next change, to provide labels for the admin and user environments.
# There may be plugins that only work in the user environment, only in the admin environment, or in both environments.
$plugin_labels = [];
if(isset($settings['plugins']) && !empty($settings['plugins']))
{
foreach($settings['plugins'] as $plugin => $config)
{
if($config['active'] == 'on')
{
$plugin_language_folder = 'plugins' . DIRECTORY_SEPARATOR . $plugin . DIRECTORY_SEPARATOR . 'languages' . DIRECTORY_SEPARATOR;
$plugin_language_file = $language . '.yaml';
if (file_exists($plugin_language_folder . $plugin_language_file))
{
$plugins_translations[$plugin] = $yaml->getYaml($plugin_language_folder, $plugin_language_file);
}
}
}
foreach($plugins_translations as $key => $value)
{
if(is_array($value))
{
$plugins_translations = array_merge($plugins_translations, $value);
}
}
}
}
$translations = [];
if(!empty($plugins_translations))
{
$translations = array_merge($translations, $plugins_translations);
}
if(!empty($system_translations))
{
$translations = array_merge($translations, $system_translations);
}
if(!empty($theme_translations))
{
$translations = array_merge($translations, $theme_translations);
}
return $translations;
}
public static function whichLanguage()
{
# Check which languages are available
$langs = [];
$path = __DIR__ . '/author/languages/*.yaml';
foreach (glob($path) as $filename)
{
$langs[] = basename($filename,'.yaml');
}
# Detect browser language
$accept_lang = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2) : false;
$lang = in_array($accept_lang, $langs) ? $accept_lang : 'en';
return $lang;
}
}

View File

@@ -0,0 +1,83 @@
{% extends 'layouts/layoutAuth.twig' %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="flex justify-end">
<div class="bg-rose-600 text-white min-h-screen w-1/2 flex justify-center items-center">
<div class="max-w-md content-center">
<h1 class="text-6xl py-5">Login</h1>
<form method="POST" action="{{ url_for("auth.login") }}" autocomplete="off">
<fieldset class="">
<div class="my-2 {{ errors.username ? ' errors' : '' }}">
<label for="username">{{ 'Username'|translate }} <abbr title="{{ 'required'|translate }}">*</abbr></label>
<input
type="text"
name="username"
value="{{ old.username }}"
class="form-control block w-full px-3 py-1.5 text-base font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none"
required>
{% if errors.signup_username %}
<span class="">{{ errors.username|first }}</span>
{% endif %}
</div>
<div class="my-2 {{ errors.password ? ' errors' : '' }}">
<label for="password">{{ 'Password'|translate }} <abbr title="{{ 'required'|translate }}">*</abbr></label>
<input
type="password"
name="password"
autoomplete="off"
class="form-control block w-full px-3 py-1.5 text-base font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none"
required>
{% if errors.password %}
<span class="error">{{ errors.password|first }}</span>
{% endif %}
</div>
<div class="personal-mail hidden">
<label>Personal Mail</label>
<input type="text" name="personal-honey-mail">
</div>
<input
type="submit"
value="{{ 'Login'|translate }}"
class="block w-full mt-6 px-3 py-3 border-0 font-medium text-xs leading-tight uppercase bg-white text-rose-600 pointer hover:bg-gray-200 cursor-pointer focus:outline-none focus:ring-0 transition duration-150 ease-in-out"
/>
</fieldset>
<div id="loginarea" class="hidden">{{ csrf() | raw }}</div>
{% if settings.recoverpw %}
{% else %}
<div class=""></div>
{% endif %}
</form>
</div>
</div>
<div class="content-center w-1/2 flex justify-center items-center">
<div class="max-w-md content-center">
<h2>Latest from Typemill</h2>
<p>Hey, get the latest news from Typemill please!</p>
<p>base_url(): {{ base_url() }}</p>
<p>current_url(): {{ current_url() }}</p>
<p>current_path(): {{ current_path() }}</p>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,17 @@
/********************
* SVG ICONS *
********************/
.icon {
display: inline-block;
width: 1em;
height: 1em;
stroke-width: 0;
stroke: currentColor;
fill: currentColor;
}
.icon.baseline{
top: 0.125em;
position: relative;
}

View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,296 @@
const { createApp } = Vue
createApp({
delimiters: ['${', '}'],
data() {
return {
message: 'Add system forms with vue here',
root: document.getElementById("main").dataset.url,
currentTab: 'System',
tabs: ['System', 'Media', 'Editor', 'Access', 'Password Recovery', 'Advanced'],
formDefinitions: [],
formData: [],
formErrors: {},
formErrorsReset: {},
item: false,
userroles: false,
saved: false,
}
},
computed: {
currentTabComponent: function () {
if(this.currentTab == 'Content')
{
editor.showEditor = 'show';
posts.showPosts = 'show';
}
else
{
editor.showEditor = 'hidden';
posts.showPosts = 'hidden';
return 'tab-' + this.currentTab.toLowerCase()
}
}
},
}).mount('#systemsettings')
/*
let system = new Vue({
delimiters: ['${', '}'],
el: '#wrong',
data: function () {
return {
root: document.getElementById("main").dataset.url,
currentTab: 'Content',
tabs: ['Content'],
formDefinitions: [],
formData: [],
formErrors: {},
formErrorsReset: {},
item: false,
userroles: false,
saved: false,
}
},
computed: {
currentTabComponent: function () {
if(this.currentTab == 'Content')
{
editor.showEditor = 'show';
posts.showPosts = 'show';
}
else
{
editor.showEditor = 'hidden';
posts.showPosts = 'hidden';
return 'tab-' + this.currentTab.toLowerCase()
}
}
},
mounted: function(){
var self = this;
myaxios.get('/api/v1/article/metaobject',{
params: {
'url': document.getElementById("path").value,
'csrf_name': document.getElementById("csrf_name").value,
'csrf_value': document.getElementById("csrf_value").value,
}
})
.then(function (response) {
var formdefinitions = response.data.metadefinitions;
for (var key in formdefinitions) {
if (formdefinitions.hasOwnProperty(key)) {
self.tabs.push(key);
self.formErrors[key] = false;
}
}
self.formErrorsReset = self.formErrors;
self.formDefinitions = formdefinitions;
self.formData = response.data.metadata;
self.userroles = response.data.userroles;
self.item = response.data.item;
if(self.item.elementType == "folder" && self.item.contains == "posts")
{
posts.posts = self.item.folderContent;
posts.folderid = self.item.keyPath;
}
else
{
posts.posts = false;
}
})
.catch(function (error)
{
if(error.response)
{
}
});
/* update single value or array
this.$set(this.someObject, 'b', 2) *
FormBus.$on('forminput', formdata => {
this.$set(this.formData[this.currentTab], formdata.name, formdata.value);
});
/* update values that are objects
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 }) *
FormBus.$on('forminputobject', formdata => {
this.formData[this.currentTab][formdata.name] = Object.assign({}, this.formData[this.currentTab][formdata.name], formdata.value);
});
},
methods: {
saveForm: function()
{
this.saved = false;
self = this;
myaxios.post('/api/v1/article/metadata',{
'url': document.getElementById("path").value,
'csrf_name': document.getElementById("csrf_name").value,
'csrf_value': document.getElementById("csrf_value").value,
'tab': self.currentTab,
'data': self.formData[self.currentTab]
})
.then(function (response) {
self.saved = true;
self.formErrors = self.formErrorsReset;
if(response.data.structure)
{
navi.items = response.data.structure;
}
var item = response.data.item;
if(item.elementType == "folder" && item.contains == "posts")
{
posts.posts = item.folderContent;
posts.folderid = item.keyPath;
}
else
{
posts.posts = false;
}
})
.catch(function (error)
{
if(error.response)
{
self.formErrors = error.response.data.errors;
}
if(error.response.data.errors.message)
{
publishController.errors.message = error.response.data.errors.message;
}
});
},
}
});
Vue.component('tab-meta', {
props: ['saved', 'errors', 'formdata', 'schema', 'userroles'],
data: function () {
return {
slug: false,
originalSlug: false,
slugerror: false,
disabled: "disabled",
}
},
template: '<section><form>' +
'<div v-if="slug !== false"><div class="large relative">' +
'<label>Slug / Name in URL</label><input type="text" v-model="slug" pattern="[a-z0-9\- ]" @input="changeSlug()"><button @click.prevent="storeSlug()" :disabled="disabled" class="button slugbutton bn br2 bg-tm-green white absolute">change slug</button>' +
'<div v-if="slugerror" class="f6 tm-red mt1">{{ slugerror }}</div>' +
'</div></div>' +
'<div v-for="(field, index) in schema.fields">' +
'<fieldset v-if="field.type == \'fieldset\'" class="fs-formbuilder"><legend>{{field.legend}}</legend>' +
'<component v-for="(subfield, index) in field.fields "' +
' :key="index"' +
' :is="selectComponent(subfield)"' +
' :errors="errors"' +
' :name="index"' +
' :userroles="userroles"' +
' v-model="formdata[index]"' +
' v-bind="subfield">' +
'</component>' +
'</fieldset>' +
'<component v-else' +
' :key="index"' +
' :is="selectComponent(field)"' +
' :errors="errors"' +
' :name="index"' +
' :userroles="userroles"' +
' v-model="formdata[index]"' +
' v-bind="field">' +
'</component>' +
'</div>' +
'<div v-if="saved" class="metasubmit"><div class="metaSuccess">{{ \'Saved successfully\'|translate }}</div></div>' +
'<div v-if="errors" class="metasubmit"><div class="metaErrors">{{ \'Please correct the errors above\'|translate }}</div></div>' +
'<div class="metasubmit"><input type="submit" @click.prevent="saveInput" :value="\'save\'|translate"></input></div>' +
'</form></section>',
mounted: function()
{
if(this.$parent.item.slug != '')
{
this.slug = this.$parent.item.slug;
this.originalSlug = this.slug;
}
},
methods: {
selectComponent: function(field)
{
return 'component-'+field.type;
},
saveInput: function()
{
this.$emit('saveform');
},
changeSlug: function()
{
if(this.slug == this.originalSlug)
{
this.slugerror = false;
this.disabled = "disabled";
return;
}
if(this.slug == '')
{
this.slugerror = 'empty slugs are not allowed';
this.disabled = "disabled";
return;
}
this.slug = this.slug.replace(/ /g, '-');
if(this.slug.match(/^[a-z0-9\-]*$/))
{
this.slugerror = false;
this.disabled = false;
}
else
{
this.slugerror = 'Only lowercase a-z and 0-9 and "-" is allowed for slugs.';
this.disabled = "disabled";
}
},
storeSlug: function()
{
if(this.slug.match(/^[a-z0-9\-]*$/) && this.slug != this.originalSlug)
{
var self = this;
myaxios.post('/api/v1/article/rename',{
'url': document.getElementById("path").value,
'csrf_name': document.getElementById("csrf_name").value,
'csrf_value': document.getElementById("csrf_value").value,
'slug': this.slug,
})
.then(function (response)
{
window.location.replace(response.data.url);
})
.catch(function (error)
{
if(error.response.data.errors.message)
{
publishController.errors.message = error.response.data.errors.message;
}
});
}
}
}
})
*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Welcome to your new TYPEMILL website" />
<meta name="robots" content="noindex" />
<meta name="msapplication-TileColor" content="#F9F8F6" />
<meta name="msapplication-TileImage" content="{{ base_url() }}/system/author/img/favicon-144.png" />
<link rel="icon" type="image/png" href="{{ base_url() }}/system/author/img/favicon-16.png" sizes="16x16" />
<link rel="icon" type="image/png" href="{{ base_url() }}/system/author/img/favicon-32.png" sizes="32x32" />
<link rel="apple-touch-icon" sizes="72x72" href="{{ base_url() }}/system/author/img/favicon-72.png" />
<link rel="apple-touch-icon" sizes="114x114" href="{{ base_url() }}/system/author/img/favicon-114.png" />
<link rel="apple-touch-icon" sizes="144x144" href="{{ base_url() }}/system/author/img/favicon-144.png" />
<link rel="apple-touch-icon" sizes="180x180" href="{{ base_url() }}/system/author/img/favicon-180.png" />
<link rel="stylesheet" href="{{ base_url() }}/system/typemill/author/css/output.css?v={{ settings.version }}" />
{{ assets.renderCSS() }}
</head>
<body class="text-base">
<svg style="position: absolute; width: 0; height: 0; overflow: hidden" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon-bookmark-o" viewBox="0 0 20 28">
<title>bookmark-o</title>
<path d="M18 4h-16v19.406l8-7.672 1.391 1.328 6.609 6.344v-19.406zM18.188 2c0.234 0 0.469 0.047 0.688 0.141 0.688 0.266 1.125 0.906 1.125 1.609v20.141c0 0.703-0.438 1.344-1.125 1.609-0.219 0.094-0.453 0.125-0.688 0.125-0.484 0-0.938-0.172-1.297-0.5l-6.891-6.625-6.891 6.625c-0.359 0.328-0.812 0.516-1.297 0.516-0.234 0-0.469-0.047-0.688-0.141-0.688-0.266-1.125-0.906-1.125-1.609v-20.141c0-0.703 0.438-1.344 1.125-1.609 0.219-0.094 0.453-0.141 0.688-0.141h16.375z"></path>
</symbol>
</defs>
</svg>
{% include 'partials/flash.twig' %}
<div class="">
{% block content %}{% endblock %}
</div>
</body>
</html>

View File

@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Configure your TYPEMILL website"/>
<meta name="msapplication-TileColor" content="#F9F8F6" />
<meta name="msapplication-TileImage" content="{{ base_url() }}/system/author/img/favicon-144.png" />
<link rel="icon" type="image/png" href="{{ base_url() }}/system/author/img/favicon-16.png" sizes="16x16" />
<link rel="icon" type="image/png" href="{{ base_url() }}/system/author/img/favicon-32.png" sizes="32x32" />
<link rel="apple-touch-icon" sizes="72x72" href="{{ base_url() }}/system/author/img/favicon-72.png" />
<link rel="apple-touch-icon" sizes="114x114" href="{{ base_url() }}/system/author/img/favicon-114.png" />
<link rel="apple-touch-icon" sizes="144x144" href="{{ base_url() }}/system/author/img/favicon-144.png" />
<link rel="apple-touch-icon" sizes="180x180" href="{{ base_url() }}/system/author/img/favicon-180.png" />
<link rel="stylesheet" href="{{ base_url() }}/system/typemill/author/css/output.css?v={{ settings.version }}" />
<link rel="stylesheet" href="{{ base_url() }}/system/typemill/author/css/custom.css?v={{ settings.version }}" />
<!-- <link rel="stylesheet" href="{{ base_url() }}/system/author/css/tachyons.min.css?v={{ settings.version }}" />
<link rel="stylesheet" href="{{ base_url() }}/system/author/css/style.css?v={{ settings.version }}" />
-->
{{ assets.renderCSS() }}
</head>
<body class="bg-stone-100">
{% include 'partials/symbols.twig' %}
<header class="border-b-2 border-stone-200">
{% include 'partials/mainNavi.twig' %}
</header>
{% include 'partials/flash.twig' %}
<div class="max-w-6xl m-auto mt-7 flex justify-between" id="main" data-url="{{ base_url() }}">
<aside class="w-1/4">
{% include 'partials/systemNavi.twig' %}
</aside>
<article class="w-3/4 bg-stone-50 drop-shadow-md">
{% block content %}{% endblock %}
</article>
</div>
<footer class="max-w-6xl m-auto bg-stone-50">
Typemill version xyz
</footer>
{{ csrf() | raw }}
<script>
var data = {{ jsdata | json_encode() | raw }}
var eventBus = false;
</script>
<script src="{{ base_url() }}/system/typemill/author/js/axios.min.js?v={{ settings.version }}"></script>
<script>
const tmaxios = axios.create();
tmaxios.defaults.baseURL = "{{ base_url() }}";
/* tmaxios.defaults.headers.common['Authorization'] = "Basic {{ basicauth }}"; */
tmaxios.defaults.headers.common['X-Session-Auth'] = "true";
</script>
<!-- <script src="{{ base_url() }}/system/typemill/author/js/autosize.min.js?v={{ settings.version }}"></script> -->
<script src="{{ base_url() }}/system/typemill/author/js/vue.js?v={{ settings.version }}"></script>
<!--
<script>
const FormBus = new Vue();
</script>
<script src="{{ base_url() }}/system/author/js/vue-shared.js?v={{ settings.version }}"></script>
<script src="{{ base_url() }}/system/author/js/author.js?v={{ settings.version }}"></script>
<script src="{{ base_url() }}/system/author/js/typemillutils.js?v={{ settings.version }}"></script>
<script>
typemillUtilities.start()
</script>
-->
{% block javascript %}{% endblock %}
{{ assets.renderJS() }}
</body>
</html>

View File

@@ -1,56 +0,0 @@
{% block title %}Login{% endblock %}
{% block content %}
<div class="setupWrapper">
<div class="setupContent">
</div>
<div class="authformWrapper">
<form method="POST" action="{{ url_for("auth.login") }}" autocomplete="off">
<fieldset class="auth">
<div class="formElement{{ errors.username ? ' errors' : '' }}">
<input type="text" name="username" value="{{ old.username }}" required>
{% if errors.signup_username %}
<span class="error">{{ errors.username | first }}</span>
{% endif %}
</div>
<div class="formElement{{ errors.password ? ' errors' : '' }}">
<input type="password" name="password" required autoomplete="off">
{% if errors.password %}
<span class="error">{{ errors.password | first }}</span>
{% endif %}
</div>
<div class="personal-mail">
<label>Personal Mail</label>
<input type="text" name="personal-honey-mail">
</div>
</fieldset>
<div class="loginarea" id="loginarea">
{{ csrf() | raw }}
</div>
</form>
</div>
{% if settings.recoverpw %}
{% else %}
<div class="setupContent">
</div>
{% endif %}
</div>
<footer></footer>
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% if flash.info %}
<div class="alert alert-info" id="flash-message">
{{ flash.info | first }}
</div>
{% endif %}
{% if flash.error %}
<div class="alert alert-error" id="flash-message">
{{ flash.error | first }}
</div>
{% endif %}

View File

@@ -0,0 +1,12 @@
<nav class="max-w-6xl m-auto flex justify-between">
<div class="p-3 text-2xl font-bold">
<a href="{{ base_url }}/tm/content/{{ settings.editor }}">Typemill</a>
</div>
<ul class="flex border-l-2 border-stone-200">
{% for name,navitem in mainnavi %}
<li class="border-r-2 border-stone-200">
<a class="inline-block px-4 pt-4 pb-3 border-b-4 hover:border-stone-700 hover:bg-stone-50 focus:bg-stone-50 active:bg-stone-50{{ navitem.active ? ' bg-stone-50 border-stone-700' : ' border-stone-100' }}" href="{{ url_for(navitem.routename) }}">{{ translate(navitem.title)|capitalize }}</a>
</li>
{% endfor %}
</ul>
</nav>

View File

@@ -0,0 +1,182 @@
<svg style="position: absolute; width: 0; height: 0; overflow: hidden" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon-external-link" viewBox="0 0 28 28">
<title>{{ translate('EXTERNAL_LINK') }}</title>
<path d="M22 14.5v5c0 2.484-2.016 4.5-4.5 4.5h-13c-2.484 0-4.5-2.016-4.5-4.5v-13c0-2.484 2.016-4.5 4.5-4.5h11c0.281 0 0.5 0.219 0.5 0.5v1c0 0.281-0.219 0.5-0.5 0.5h-11c-1.375 0-2.5 1.125-2.5 2.5v13c0 1.375 1.125 2.5 2.5 2.5h13c1.375 0 2.5-1.125 2.5-2.5v-5c0-0.281 0.219-0.5 0.5-0.5h1c0.281 0 0.5 0.219 0.5 0.5zM28 1v8c0 0.547-0.453 1-1 1-0.266 0-0.516-0.109-0.703-0.297l-2.75-2.75-10.187 10.187c-0.094 0.094-0.234 0.156-0.359 0.156s-0.266-0.063-0.359-0.156l-1.781-1.781c-0.094-0.094-0.156-0.234-0.156-0.359s0.063-0.266 0.156-0.359l10.187-10.187-2.75-2.75c-0.187-0.187-0.297-0.438-0.297-0.703 0-0.547 0.453-1 1-1h8c0.547 0 1 0.453 1 1z"></path>
</symbol>
<symbol id="icon-file-text-o" viewBox="0 0 24 28">
<title>{{ translate('TEXT_FILE') }}</title>
<path d="M22.937 5.938c0.578 0.578 1.062 1.734 1.062 2.562v18c0 0.828-0.672 1.5-1.5 1.5h-21c-0.828 0-1.5-0.672-1.5-1.5v-25c0-0.828 0.672-1.5 1.5-1.5h14c0.828 0 1.984 0.484 2.562 1.062zM16 2.125v5.875h5.875c-0.094-0.266-0.234-0.531-0.344-0.641l-4.891-4.891c-0.109-0.109-0.375-0.25-0.641-0.344zM22 26v-16h-6.5c-0.828 0-1.5-0.672-1.5-1.5v-6.5h-12v24h20zM6 12.5c0-0.281 0.219-0.5 0.5-0.5h11c0.281 0 0.5 0.219 0.5 0.5v1c0 0.281-0.219 0.5-0.5 0.5h-11c-0.281 0-0.5-0.219-0.5-0.5v-1zM17.5 16c0.281 0 0.5 0.219 0.5 0.5v1c0 0.281-0.219 0.5-0.5 0.5h-11c-0.281 0-0.5-0.219-0.5-0.5v-1c0-0.281 0.219-0.5 0.5-0.5h11zM17.5 20c0.281 0 0.5 0.219 0.5 0.5v1c0 0.281-0.219 0.5-0.5 0.5h-11c-0.281 0-0.5-0.219-0.5-0.5v-1c0-0.281 0.219-0.5 0.5-0.5h11z"></path>
</symbol>
<symbol id="icon-cog" viewBox="0 0 24 28">
<title>{{ translate('COG') }}</title>
<path d="M16 14c0-2.203-1.797-4-4-4s-4 1.797-4 4 1.797 4 4 4 4-1.797 4-4zM24 12.297v3.469c0 0.234-0.187 0.516-0.438 0.562l-2.891 0.438c-0.172 0.5-0.359 0.969-0.609 1.422 0.531 0.766 1.094 1.453 1.672 2.156 0.094 0.109 0.156 0.25 0.156 0.391s-0.047 0.25-0.141 0.359c-0.375 0.5-2.484 2.797-3.016 2.797-0.141 0-0.281-0.063-0.406-0.141l-2.156-1.687c-0.453 0.234-0.938 0.438-1.422 0.594-0.109 0.953-0.203 1.969-0.453 2.906-0.063 0.25-0.281 0.438-0.562 0.438h-3.469c-0.281 0-0.531-0.203-0.562-0.469l-0.438-2.875c-0.484-0.156-0.953-0.344-1.406-0.578l-2.203 1.672c-0.109 0.094-0.25 0.141-0.391 0.141s-0.281-0.063-0.391-0.172c-0.828-0.75-1.922-1.719-2.578-2.625-0.078-0.109-0.109-0.234-0.109-0.359 0-0.141 0.047-0.25 0.125-0.359 0.531-0.719 1.109-1.406 1.641-2.141-0.266-0.5-0.484-1.016-0.641-1.547l-2.859-0.422c-0.266-0.047-0.453-0.297-0.453-0.562v-3.469c0-0.234 0.187-0.516 0.422-0.562l2.906-0.438c0.156-0.5 0.359-0.969 0.609-1.437-0.531-0.75-1.094-1.453-1.672-2.156-0.094-0.109-0.156-0.234-0.156-0.375s0.063-0.25 0.141-0.359c0.375-0.516 2.484-2.797 3.016-2.797 0.141 0 0.281 0.063 0.406 0.156l2.156 1.672c0.453-0.234 0.938-0.438 1.422-0.594 0.109-0.953 0.203-1.969 0.453-2.906 0.063-0.25 0.281-0.438 0.562-0.438h3.469c0.281 0 0.531 0.203 0.562 0.469l0.438 2.875c0.484 0.156 0.953 0.344 1.406 0.578l2.219-1.672c0.094-0.094 0.234-0.141 0.375-0.141s0.281 0.063 0.391 0.156c0.828 0.766 1.922 1.734 2.578 2.656 0.078 0.094 0.109 0.219 0.109 0.344 0 0.141-0.047 0.25-0.125 0.359-0.531 0.719-1.109 1.406-1.641 2.141 0.266 0.5 0.484 1.016 0.641 1.531l2.859 0.438c0.266 0.047 0.453 0.297 0.453 0.562z"></path>
</symbol>
<symbol id="icon-power-off" viewBox="0 0 24 28">
<title>{{ translate('POWER_OFF') }}</title>
<path d="M24 14c0 6.609-5.391 12-12 12s-12-5.391-12-12c0-3.797 1.75-7.297 4.797-9.578 0.891-0.672 2.141-0.5 2.797 0.391 0.672 0.875 0.484 2.141-0.391 2.797-2.031 1.531-3.203 3.859-3.203 6.391 0 4.406 3.594 8 8 8s8-3.594 8-8c0-2.531-1.172-4.859-3.203-6.391-0.875-0.656-1.062-1.922-0.391-2.797 0.656-0.891 1.922-1.062 2.797-0.391 3.047 2.281 4.797 5.781 4.797 9.578zM14 2v10c0 1.094-0.906 2-2 2s-2-0.906-2-2v-10c0-1.094 0.906-2 2-2s2 0.906 2 2z"></path>
</symbol>
<symbol id="icon-minus" viewBox="0 0 22 28">
<title>{{ translate('DELETE') }}</title>
<path d="M22 11.5v3c0 0.828-0.672 1.5-1.5 1.5h-19c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h19c0.828 0 1.5 0.672 1.5 1.5z"></path>
</symbol>
<symbol id="icon-plus" viewBox="0 0 22 28">
<title>{{ translate('ADD') }}</title>
<path d="M22 11.5v3c0 0.828-0.672 1.5-1.5 1.5h-6.5v6.5c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-6.5h-6.5c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h6.5v-6.5c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5v6.5h6.5c0.828 0 1.5 0.672 1.5 1.5z"></path>
</symbol>
<symbol id="icon-close" viewBox="0 0 22 28">
<title>{{ translate('DELETE_CLOSE') }}</title>
<path d="M20.281 20.656c0 0.391-0.156 0.781-0.438 1.062l-2.125 2.125c-0.281 0.281-0.672 0.438-1.062 0.438s-0.781-0.156-1.062-0.438l-4.594-4.594-4.594 4.594c-0.281 0.281-0.672 0.438-1.062 0.438s-0.781-0.156-1.062-0.438l-2.125-2.125c-0.281-0.281-0.438-0.672-0.438-1.062s0.156-0.781 0.438-1.062l4.594-4.594-4.594-4.594c-0.281-0.281-0.438-0.672-0.438-1.062s0.156-0.781 0.438-1.062l2.125-2.125c0.281-0.281 0.672-0.438 1.062-0.438s0.781 0.156 1.062 0.438l4.594 4.594 4.594-4.594c0.281-0.281 0.672-0.438 1.062-0.438s0.781 0.156 1.062 0.438l2.125 2.125c0.281 0.281 0.438 0.672 0.438 1.062s-0.156 0.781-0.438 1.062l-4.594 4.594 4.594 4.594c0.281 0.281 0.438 0.672 0.438 1.062z"></path>
</symbol>
<symbol id="icon-home" viewBox="0 0 26 28">
<title>{{ translate('HOME') }}</title>
<path d="M22 15.5v7.5c0 0.547-0.453 1-1 1h-6v-6h-4v6h-6c-0.547 0-1-0.453-1-1v-7.5c0-0.031 0.016-0.063 0.016-0.094l8.984-7.406 8.984 7.406c0.016 0.031 0.016 0.063 0.016 0.094zM25.484 14.422l-0.969 1.156c-0.078 0.094-0.203 0.156-0.328 0.172h-0.047c-0.125 0-0.234-0.031-0.328-0.109l-10.813-9.016-10.813 9.016c-0.109 0.078-0.234 0.125-0.375 0.109-0.125-0.016-0.25-0.078-0.328-0.172l-0.969-1.156c-0.172-0.203-0.141-0.531 0.063-0.703l11.234-9.359c0.656-0.547 1.719-0.547 2.375 0l3.813 3.187v-3.047c0-0.281 0.219-0.5 0.5-0.5h3c0.281 0 0.5 0.219 0.5 0.5v6.375l3.422 2.844c0.203 0.172 0.234 0.5 0.063 0.703z"></path>
</symbol>
<symbol id="icon-arrows-v" viewBox="0 0 12 28">
<title>{{ translate('MOVE_VERTICAL') }}</title>
<path d="M11 5c0 0.547-0.453 1-1 1h-2v16h2c0.547 0 1 0.453 1 1 0 0.266-0.109 0.516-0.297 0.703l-4 4c-0.187 0.187-0.438 0.297-0.703 0.297s-0.516-0.109-0.703-0.297l-4-4c-0.187-0.187-0.297-0.438-0.297-0.703 0-0.547 0.453-1 1-1h2v-16h-2c-0.547 0-1-0.453-1-1 0-0.266 0.109-0.516 0.297-0.703l4-4c0.187-0.187 0.438-0.297 0.703-0.297s0.516 0.109 0.703 0.297l4 4c0.187 0.187 0.297 0.438 0.297 0.703z"></path>
</symbol>
<symbol id="icon-folder-o" viewBox="0 0 26 28">
<title>{{ translate('FOLDER') }}</title>
<path d="M24 20.5v-11c0-0.828-0.672-1.5-1.5-1.5h-11c-0.828 0-1.5-0.672-1.5-1.5v-1c0-0.828-0.672-1.5-1.5-1.5h-5c-0.828 0-1.5 0.672-1.5 1.5v15c0 0.828 0.672 1.5 1.5 1.5h19c0.828 0 1.5-0.672 1.5-1.5zM26 9.5v11c0 1.922-1.578 3.5-3.5 3.5h-19c-1.922 0-3.5-1.578-3.5-3.5v-15c0-1.922 1.578-3.5 3.5-3.5h5c1.922 0 3.5 1.578 3.5 3.5v0.5h10.5c1.922 0 3.5 1.578 3.5 3.5z"></path>
</symbol>
<symbol id="icon-upload" viewBox="0 0 26 28">
<title>{{ translate('UPLOAD') }}</title>
<path d="M20 23c0-0.547-0.453-1-1-1s-1 0.453-1 1 0.453 1 1 1 1-0.453 1-1zM24 23c0-0.547-0.453-1-1-1s-1 0.453-1 1 0.453 1 1 1 1-0.453 1-1zM26 19.5v5c0 0.828-0.672 1.5-1.5 1.5h-23c-0.828 0-1.5-0.672-1.5-1.5v-5c0-0.828 0.672-1.5 1.5-1.5h6.672c0.422 1.156 1.531 2 2.828 2h4c1.297 0 2.406-0.844 2.828-2h6.672c0.828 0 1.5 0.672 1.5 1.5zM20.922 9.375c-0.156 0.375-0.516 0.625-0.922 0.625h-4v7c0 0.547-0.453 1-1 1h-4c-0.547 0-1-0.453-1-1v-7h-4c-0.406 0-0.766-0.25-0.922-0.625-0.156-0.359-0.078-0.797 0.219-1.078l7-7c0.187-0.203 0.453-0.297 0.703-0.297s0.516 0.094 0.703 0.297l7 7c0.297 0.281 0.375 0.719 0.219 1.078z"></path>
</symbol>
<symbol id="icon-image" viewBox="0 0 32 32">
<title>{{ translate('IMAGE') }}</title>
<path d="M29.996 4c0.001 0.001 0.003 0.002 0.004 0.004v23.993c-0.001 0.001-0.002 0.003-0.004 0.004h-27.993c-0.001-0.001-0.003-0.002-0.004-0.004v-23.993c0.001-0.001 0.002-0.003 0.004-0.004h27.993zM30 2h-28c-1.1 0-2 0.9-2 2v24c0 1.1 0.9 2 2 2h28c1.1 0 2-0.9 2-2v-24c0-1.1-0.9-2-2-2v0z"></path>
<path d="M26 9c0 1.657-1.343 3-3 3s-3-1.343-3-3 1.343-3 3-3 3 1.343 3 3z"></path>
<path d="M28 26h-24v-4l7-12 8 10h2l7-6z"></path>
</symbol>
<symbol id="icon-exclamation-circle" viewBox="0 0 24 28">
<title>{{ translate('NOTICE') }}</title>
<path d="M12 2c6.625 0 12 5.375 12 12s-5.375 12-12 12-12-5.375-12-12 5.375-12 12-12zM14 21.484v-2.969c0-0.281-0.219-0.516-0.484-0.516h-3c-0.281 0-0.516 0.234-0.516 0.516v2.969c0 0.281 0.234 0.516 0.516 0.516h3c0.266 0 0.484-0.234 0.484-0.516zM13.969 16.109l0.281-9.703c0-0.109-0.047-0.219-0.156-0.281-0.094-0.078-0.234-0.125-0.375-0.125h-3.437c-0.141 0-0.281 0.047-0.375 0.125-0.109 0.063-0.156 0.172-0.156 0.281l0.266 9.703c0 0.219 0.234 0.391 0.531 0.391h2.891c0.281 0 0.516-0.172 0.531-0.391z"></path>
</symbol>
<symbol id="icon-paperclip" viewBox="0 0 22 28">
<title>{{ translate('PAPERCLIP') }}</title>
<path d="M21.938 21.641c0 2.438-1.859 4.297-4.297 4.297-1.375 0-2.703-0.594-3.672-1.563l-12.141-12.125c-1.109-1.125-1.766-2.656-1.766-4.234 0-3.313 2.609-5.953 5.922-5.953 1.594 0 3.125 0.641 4.266 1.766l9.453 9.469c0.094 0.094 0.156 0.219 0.156 0.344 0 0.328-0.875 1.203-1.203 1.203-0.141 0-0.266-0.063-0.359-0.156l-9.469-9.484c-0.75-0.734-1.766-1.203-2.828-1.203-2.219 0-3.938 1.797-3.938 4 0 1.062 0.438 2.078 1.188 2.828l12.125 12.141c0.594 0.594 1.422 0.984 2.266 0.984 1.328 0 2.312-0.984 2.312-2.312 0-0.859-0.391-1.672-0.984-2.266l-9.078-9.078c-0.25-0.234-0.594-0.375-0.938-0.375-0.594 0-1.047 0.438-1.047 1.047 0 0.344 0.156 0.672 0.391 0.922l6.406 6.406c0.094 0.094 0.156 0.219 0.156 0.344 0 0.328-0.891 1.219-1.219 1.219-0.125 0-0.25-0.063-0.344-0.156l-6.406-6.406c-0.625-0.609-0.984-1.469-0.984-2.328 0-1.719 1.344-3.062 3.063-3.062 0.875 0 1.719 0.359 2.328 0.984l9.078 9.078c0.984 0.969 1.563 2.297 1.563 3.672z"></path>
</symbol>
<symbol id="icon-play" viewBox="0 0 32 32">
<title>{{ translate('VIDEO') }}</title>
<path d="M30.662 5.003c-4.488-0.645-9.448-1.003-14.662-1.003s-10.174 0.358-14.662 1.003c-0.86 3.366-1.338 7.086-1.338 10.997s0.477 7.63 1.338 10.997c4.489 0.645 9.448 1.003 14.662 1.003s10.174-0.358 14.662-1.003c0.86-3.366 1.338-7.086 1.338-10.997s-0.477-7.63-1.338-10.997zM12 22v-12l10 6-10 6z"></path>
</symbol>
<symbol id="icon-quotes-left" viewBox="0 0 32 32">
<title>{{ translate('QUOTES') }}</title>
<path d="M7.031 14c3.866 0 7 3.134 7 7s-3.134 7-7 7-7-3.134-7-7l-0.031-1c0-7.732 6.268-14 14-14v4c-2.671 0-5.182 1.040-7.071 2.929-0.364 0.364-0.695 0.751-0.995 1.157 0.357-0.056 0.724-0.086 1.097-0.086zM25.031 14c3.866 0 7 3.134 7 7s-3.134 7-7 7-7-3.134-7-7l-0.031-1c0-7.732 6.268-14 14-14v4c-2.671 0-5.182 1.040-7.071 2.929-0.364 0.364-0.695 0.751-0.995 1.157 0.358-0.056 0.724-0.086 1.097-0.086z"></path>
</symbol>
<symbol id="icon-list-numbered" viewBox="0 0 32 32">
<title>{{ translate('NUMBERED_LIST') }}</title>
<path d="M12 26h20v4h-20zM12 14h20v4h-20zM12 2h20v4h-20zM6 0v8h-2v-6h-2v-2zM4 16.438v1.563h4v2h-6v-4.563l4-1.875v-1.563h-4v-2h6v4.563zM8 22v10h-6v-2h4v-2h-4v-2h4v-2h-4v-2z"></path>
</symbol>
<symbol id="icon-list2" viewBox="0 0 32 32">
<title>{{ translate('BULLET_LIST') }}</title>
<path d="M12 2h20v4h-20v-4zM12 14h20v4h-20v-4zM12 26h20v4h-20v-4zM0 4c0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.209-1.791 4-4 4s-4-1.791-4-4zM0 16c0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.209-1.791 4-4 4s-4-1.791-4-4zM0 28c0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.209-1.791 4-4 4s-4-1.791-4-4z"></path>
</symbol>
<symbol id="icon-link" viewBox="0 0 32 32">
<title>{{ translate('LINK') }}</title>
<path d="M13.757 19.868c-0.416 0-0.832-0.159-1.149-0.476-2.973-2.973-2.973-7.81 0-10.783l6-6c1.44-1.44 3.355-2.233 5.392-2.233s3.951 0.793 5.392 2.233c2.973 2.973 2.973 7.81 0 10.783l-2.743 2.743c-0.635 0.635-1.663 0.635-2.298 0s-0.635-1.663 0-2.298l2.743-2.743c1.706-1.706 1.706-4.481 0-6.187-0.826-0.826-1.925-1.281-3.094-1.281s-2.267 0.455-3.094 1.281l-6 6c-1.706 1.706-1.706 4.481 0 6.187 0.635 0.635 0.635 1.663 0 2.298-0.317 0.317-0.733 0.476-1.149 0.476z"></path>
<path d="M8 31.625c-2.037 0-3.952-0.793-5.392-2.233-2.973-2.973-2.973-7.81 0-10.783l2.743-2.743c0.635-0.635 1.664-0.635 2.298 0s0.635 1.663 0 2.298l-2.743 2.743c-1.706 1.706-1.706 4.481 0 6.187 0.826 0.826 1.925 1.281 3.094 1.281s2.267-0.455 3.094-1.281l6-6c1.706-1.706 1.706-4.481 0-6.187-0.635-0.635-0.635-1.663 0-2.298s1.663-0.635 2.298 0c2.973 2.973 2.973 7.81 0 10.783l-6 6c-1.44 1.44-3.355 2.233-5.392 2.233z"></path>
</symbol>
<symbol id="icon-bold" viewBox="0 0 32 32">
<title>{{ translate('BOLD') }}</title>
<path d="M22.121 15.145c1.172-1.392 1.879-3.188 1.879-5.145 0-4.411-3.589-8-8-8h-10v28h12c4.411 0 8-3.589 8-8 0-2.905-1.556-5.453-3.879-6.855zM12 6h3.172c1.749 0 3.172 1.794 3.172 4s-1.423 4-3.172 4h-3.172v-8zM16.969 26h-4.969v-8h4.969c1.827 0 3.313 1.794 3.313 4s-1.486 4-3.313 4z"></path>
</symbol>
<symbol id="icon-italic" viewBox="0 0 32 32">
<title>{{ translate('ITALIC') }}</title>
<path d="M28 2v2h-4l-10 24h4v2h-14v-2h4l10-24h-4v-2z"></path>
</symbol>
<symbol id="icon-pagebreak" viewBox="0 0 32 32">
<title>{{ translate('HORIZONTAL_LINE') }}</title>
<path d="M8 12v-12h24v12h-2v-10h-20v10zM32 18v14h-24v-14h2v12h20v-12zM16 14h4v2h-4zM10 14h4v2h-4zM22 14h4v2h-4zM28 14h4v2h-4zM0 9l6 6-6 6z"></path>
</symbol>
<symbol id="icon-table2" viewBox="0 0 32 32">
<title>{{ translate('TABLE') }}</title>
<path d="M0 2v28h32v-28h-32zM12 20v-6h8v6h-8zM20 22v6h-8v-6h8zM20 6v6h-8v-6h8zM10 6v6h-8v-6h8zM2 14h8v6h-8v-6zM22 14h8v6h-8v-6zM22 12v-6h8v6h-8zM2 22h8v6h-8v-6zM22 28v-6h8v6h-8z"></path>
</symbol>
<symbol id="icon-pilcrow" viewBox="0 0 32 32">
<title>{{ translate('PARAGRAPH') }}</title>
<path d="M12 0h16v4h-4v28h-4v-28h-4v28h-4v-16c-4.418 0-8-3.582-8-8s3.582-8 8-8z"></path>
</symbol>
<symbol id="icon-embed" viewBox="0 0 32 32">
<title>{{ translate('CODE') }}</title>
<path d="M18 23l3 3 10-10-10-10-3 3 7 7z"></path>
<path d="M14 9l-3-3-10 10 10 10 3-3-7-7z"></path>
</symbol>
<symbol id="icon-header" viewBox="0 0 28 28">
<title>{{ translate('HEADLINE') }}</title>
<path d="M26.281 26c-1.375 0-2.766-0.109-4.156-0.109-1.375 0-2.75 0.109-4.125 0.109-0.531 0-0.781-0.578-0.781-1.031 0-1.391 1.563-0.797 2.375-1.328 0.516-0.328 0.516-1.641 0.516-2.188l-0.016-6.109c0-0.172 0-0.328-0.016-0.484-0.25-0.078-0.531-0.063-0.781-0.063h-10.547c-0.266 0-0.547-0.016-0.797 0.063-0.016 0.156-0.016 0.313-0.016 0.484l-0.016 5.797c0 0.594 0 2.219 0.578 2.562 0.812 0.5 2.656-0.203 2.656 1.203 0 0.469-0.219 1.094-0.766 1.094-1.453 0-2.906-0.109-4.344-0.109-1.328 0-2.656 0.109-3.984 0.109-0.516 0-0.75-0.594-0.75-1.031 0-1.359 1.437-0.797 2.203-1.328 0.5-0.344 0.516-1.687 0.516-2.234l-0.016-0.891v-12.703c0-0.75 0.109-3.156-0.594-3.578-0.781-0.484-2.453 0.266-2.453-1.141 0-0.453 0.203-1.094 0.75-1.094 1.437 0 2.891 0.109 4.328 0.109 1.313 0 2.641-0.109 3.953-0.109 0.562 0 0.781 0.625 0.781 1.094 0 1.344-1.547 0.688-2.312 1.172-0.547 0.328-0.547 1.937-0.547 2.5l0.016 5c0 0.172 0 0.328 0.016 0.5 0.203 0.047 0.406 0.047 0.609 0.047h10.922c0.187 0 0.391 0 0.594-0.047 0.016-0.172 0.016-0.328 0.016-0.5l0.016-5c0-0.578 0-2.172-0.547-2.5-0.781-0.469-2.344 0.156-2.344-1.172 0-0.469 0.219-1.094 0.781-1.094 1.375 0 2.75 0.109 4.125 0.109 1.344 0 2.688-0.109 4.031-0.109 0.562 0 0.781 0.625 0.781 1.094 0 1.359-1.609 0.672-2.391 1.156-0.531 0.344-0.547 1.953-0.547 2.516l0.016 14.734c0 0.516 0.031 1.875 0.531 2.188 0.797 0.5 2.484-0.141 2.484 1.219 0 0.453-0.203 1.094-0.75 1.094z"></path>
</symbol>
<symbol id="icon-list-alt" viewBox="0 0 28 28">
<title>{{ translate('TABLE_OF_CONTENTS') }}</title>
<path d="M6 18.5v1c0 0.266-0.234 0.5-0.5 0.5h-1c-0.266 0-0.5-0.234-0.5-0.5v-1c0-0.266 0.234-0.5 0.5-0.5h1c0.266 0 0.5 0.234 0.5 0.5zM6 14.5v1c0 0.266-0.234 0.5-0.5 0.5h-1c-0.266 0-0.5-0.234-0.5-0.5v-1c0-0.266 0.234-0.5 0.5-0.5h1c0.266 0 0.5 0.234 0.5 0.5zM6 10.5v1c0 0.266-0.234 0.5-0.5 0.5h-1c-0.266 0-0.5-0.234-0.5-0.5v-1c0-0.266 0.234-0.5 0.5-0.5h1c0.266 0 0.5 0.234 0.5 0.5zM24 18.5v1c0 0.266-0.234 0.5-0.5 0.5h-15c-0.266 0-0.5-0.234-0.5-0.5v-1c0-0.266 0.234-0.5 0.5-0.5h15c0.266 0 0.5 0.234 0.5 0.5zM24 14.5v1c0 0.266-0.234 0.5-0.5 0.5h-15c-0.266 0-0.5-0.234-0.5-0.5v-1c0-0.266 0.234-0.5 0.5-0.5h15c0.266 0 0.5 0.234 0.5 0.5zM24 10.5v1c0 0.266-0.234 0.5-0.5 0.5h-15c-0.266 0-0.5-0.234-0.5-0.5v-1c0-0.266 0.234-0.5 0.5-0.5h15c0.266 0 0.5 0.234 0.5 0.5zM26 21.5v-13c0-0.266-0.234-0.5-0.5-0.5h-23c-0.266 0-0.5 0.234-0.5 0.5v13c0 0.266 0.234 0.5 0.5 0.5h23c0.266 0 0.5-0.234 0.5-0.5zM28 4.5v17c0 1.375-1.125 2.5-2.5 2.5h-23c-1.375 0-2.5-1.125-2.5-2.5v-17c0-1.375 1.125-2.5 2.5-2.5h23c1.375 0 2.5 1.125 2.5 2.5z"></path>
</symbol>
<symbol id="icon-dots-two-vertical" viewBox="0 0 20 20">
<title>{{ translate('DEFINITION') }}</title>
<path d="M10.001 8.2c1.215 0 2.199-0.986 2.199-2.2s-0.984-2.2-2.199-2.2c-1.215 0-2.201 0.985-2.201 2.2s0.986 2.2 2.201 2.2zM10.001 11.8c-1.215 0-2.201 0.985-2.201 2.2s0.986 2.2 2.201 2.2c1.215 0 2.199-0.985 2.199-2.2s-0.984-2.2-2.199-2.2z"></path>
</symbol>
<symbol id="icon-check" viewBox="0 0 20 20">
<title>{{ translate('CHECK') }}</title>
<path d="M8.294 16.998c-0.435 0-0.847-0.203-1.111-0.553l-3.573-4.721c-0.465-0.613-0.344-1.486 0.27-1.951 0.615-0.467 1.488-0.344 1.953 0.27l2.351 3.104 5.911-9.492c0.407-0.652 1.267-0.852 1.921-0.445s0.854 1.266 0.446 1.92l-6.984 11.21c-0.242 0.391-0.661 0.635-1.12 0.656-0.022 0.002-0.042 0.002-0.064 0.002z"></path>
</symbol>
<symbol id="icon-cross" viewBox="0 0 20 20">
<title>{{ translate('CROSS') }}</title>
<path d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"></path>
</symbol>
<symbol id="icon-trash-o" viewBox="0 0 22 28">
<title>{{ translate('TRASH') }}</title>
<path d="M8 11.5v9c0 0.281-0.219 0.5-0.5 0.5h-1c-0.281 0-0.5-0.219-0.5-0.5v-9c0-0.281 0.219-0.5 0.5-0.5h1c0.281 0 0.5 0.219 0.5 0.5zM12 11.5v9c0 0.281-0.219 0.5-0.5 0.5h-1c-0.281 0-0.5-0.219-0.5-0.5v-9c0-0.281 0.219-0.5 0.5-0.5h1c0.281 0 0.5 0.219 0.5 0.5zM16 11.5v9c0 0.281-0.219 0.5-0.5 0.5h-1c-0.281 0-0.5-0.219-0.5-0.5v-9c0-0.281 0.219-0.5 0.5-0.5h1c0.281 0 0.5 0.219 0.5 0.5zM18 22.813v-14.812h-14v14.812c0 0.75 0.422 1.188 0.5 1.188h13c0.078 0 0.5-0.438 0.5-1.188zM7.5 6h7l-0.75-1.828c-0.047-0.063-0.187-0.156-0.266-0.172h-4.953c-0.094 0.016-0.219 0.109-0.266 0.172zM22 6.5v1c0 0.281-0.219 0.5-0.5 0.5h-1.5v14.812c0 1.719-1.125 3.187-2.5 3.187h-13c-1.375 0-2.5-1.406-2.5-3.125v-14.875h-1.5c-0.281 0-0.5-0.219-0.5-0.5v-1c0-0.281 0.219-0.5 0.5-0.5h4.828l1.094-2.609c0.313-0.766 1.25-1.391 2.078-1.391h5c0.828 0 1.766 0.625 2.078 1.391l1.094 2.609h4.828c0.281 0 0.5 0.219 0.5 0.5z"></path>
</symbol>
<symbol id="icon-info" viewBox="0 0 32 32">
<title>{{ translate('INFO') }}</title>
<path d="M14 9.5c0-0.825 0.675-1.5 1.5-1.5h1c0.825 0 1.5 0.675 1.5 1.5v1c0 0.825-0.675 1.5-1.5 1.5h-1c-0.825 0-1.5-0.675-1.5-1.5v-1z"></path>
<path d="M20 24h-8v-2h2v-6h-2v-2h6v8h2z"></path>
<path d="M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM16 29c-7.18 0-13-5.82-13-13s5.82-13 13-13 13 5.82 13 13-5.82 13-13 13z"></path>
</symbol>
<symbol id="icon-eye-blocked" viewBox="0 0 32 32">
<title>{{ translate('EYE_BLOCKED') }}</title>
<path d="M29.561 0.439c-0.586-0.586-1.535-0.586-2.121 0l-6.318 6.318c-1.623-0.492-3.342-0.757-5.122-0.757-6.979 0-13.028 4.064-16 10 1.285 2.566 3.145 4.782 5.407 6.472l-4.968 4.968c-0.586 0.586-0.586 1.535 0 2.121 0.293 0.293 0.677 0.439 1.061 0.439s0.768-0.146 1.061-0.439l27-27c0.586-0.586 0.586-1.536 0-2.121zM13 10c1.32 0 2.44 0.853 2.841 2.037l-3.804 3.804c-1.184-0.401-2.037-1.521-2.037-2.841 0-1.657 1.343-3 3-3zM3.441 16c1.197-1.891 2.79-3.498 4.67-4.697 0.122-0.078 0.246-0.154 0.371-0.228-0.311 0.854-0.482 1.776-0.482 2.737 0 1.715 0.54 3.304 1.459 4.607l-1.904 1.904c-1.639-1.151-3.038-2.621-4.114-4.323z"></path>
<path d="M24 13.813c0-0.849-0.133-1.667-0.378-2.434l-10.056 10.056c0.768 0.245 1.586 0.378 2.435 0.378 4.418 0 8-3.582 8-8z"></path>
<path d="M25.938 9.062l-2.168 2.168c0.040 0.025 0.079 0.049 0.118 0.074 1.88 1.199 3.473 2.805 4.67 4.697-1.197 1.891-2.79 3.498-4.67 4.697-2.362 1.507-5.090 2.303-7.889 2.303-1.208 0-2.403-0.149-3.561-0.439l-2.403 2.403c1.866 0.671 3.873 1.036 5.964 1.036 6.978 0 13.027-4.064 16-10-1.407-2.81-3.504-5.2-6.062-6.938z"></path>
</symbol>
<symbol id="icon-search" viewBox="0 0 26 28">
<title>{{ translate('SEARCH') }}</title>
<path d="M18 13c0-3.859-3.141-7-7-7s-7 3.141-7 7 3.141 7 7 7 7-3.141 7-7zM26 26c0 1.094-0.906 2-2 2-0.531 0-1.047-0.219-1.406-0.594l-5.359-5.344c-1.828 1.266-4.016 1.937-6.234 1.937-6.078 0-11-4.922-11-11s4.922-11 11-11 11 4.922 11 11c0 2.219-0.672 4.406-1.937 6.234l5.359 5.359c0.359 0.359 0.578 0.875 0.578 1.406z"></path>
</symbol>
<symbol id="icon-cancel-circle" viewBox="0 0 32 32">
<title>{{ translate('CANCEL') }}</title>
<path d="M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM16 29c-7.18 0-13-5.82-13-13s5.82-13 13-13 13 5.82 13 13-5.82 13-13 13z"></path>
<path d="M21 8l-5 5-5-5-3 3 5 5-5 5 3 3 5-5 5 5 3-3-5-5 5-5z"></path>
</symbol>
<symbol id="icon-bookmark-o" viewBox="0 0 20 28">
<title> translate('BOOKMARK')</title>
<path d="M18 4h-16v19.406l8-7.672 1.391 1.328 6.609 6.344v-19.406zM18.188 2c0.234 0 0.469 0.047 0.688 0.141 0.688 0.266 1.125 0.906 1.125 1.609v20.141c0 0.703-0.438 1.344-1.125 1.609-0.219 0.094-0.453 0.125-0.688 0.125-0.484 0-0.938-0.172-1.297-0.5l-6.891-6.625-6.891 6.625c-0.359 0.328-0.812 0.516-1.297 0.516-0.234 0-0.469-0.047-0.688-0.141-0.688-0.266-1.125-0.906-1.125-1.609v-20.141c0-0.703 0.438-1.344 1.125-1.609 0.219-0.094 0.453-0.141 0.688-0.141h16.375z"></path>
</symbol>
<symbol id="icon-user" viewBox="0 0 20 28">
<path d="M20 21.859c0 2.281-1.5 4.141-3.328 4.141h-13.344c-1.828 0-3.328-1.859-3.328-4.141 0-4.109 1.016-8.859 5.109-8.859 1.266 1.234 2.984 2 4.891 2s3.625-0.766 4.891-2c4.094 0 5.109 4.75 5.109 8.859zM16 8c0 3.313-2.688 6-6 6s-6-2.688-6-6 2.688-6 6-6 6 2.688 6 6z"></path>
</symbol>
<symbol id="icon-group" viewBox="0 0 30 28">
<path d="M9.266 14c-1.625 0.047-3.094 0.75-4.141 2h-2.094c-1.563 0-3.031-0.75-3.031-2.484 0-1.266-0.047-5.516 1.937-5.516 0.328 0 1.953 1.328 4.062 1.328 0.719 0 1.406-0.125 2.078-0.359-0.047 0.344-0.078 0.688-0.078 1.031 0 1.422 0.453 2.828 1.266 4zM26 23.953c0 2.531-1.672 4.047-4.172 4.047h-13.656c-2.5 0-4.172-1.516-4.172-4.047 0-3.531 0.828-8.953 5.406-8.953 0.531 0 2.469 2.172 5.594 2.172s5.063-2.172 5.594-2.172c4.578 0 5.406 5.422 5.406 8.953zM10 4c0 2.203-1.797 4-4 4s-4-1.797-4-4 1.797-4 4-4 4 1.797 4 4zM21 10c0 3.313-2.688 6-6 6s-6-2.688-6-6 2.688-6 6-6 6 2.688 6 6zM30 13.516c0 1.734-1.469 2.484-3.031 2.484h-2.094c-1.047-1.25-2.516-1.953-4.141-2 0.812-1.172 1.266-2.578 1.266-4 0-0.344-0.031-0.688-0.078-1.031 0.672 0.234 1.359 0.359 2.078 0.359 2.109 0 3.734-1.328 4.062-1.328 1.984 0 1.937 4.25 1.937 5.516zM28 4c0 2.203-1.797 4-4 4s-4-1.797-4-4 1.797-4 4-4 4 1.797 4 4z"></path>
</symbol>
<symbol id="icon-wrench" viewBox="0 0 26 28">
<path d="M6 23c0-0.547-0.453-1-1-1s-1 0.453-1 1 0.453 1 1 1 1-0.453 1-1zM16.063 16.438l-10.656 10.656c-0.359 0.359-0.875 0.578-1.406 0.578s-1.047-0.219-1.422-0.578l-1.656-1.687c-0.375-0.359-0.594-0.875-0.594-1.406s0.219-1.047 0.594-1.422l10.641-10.641c0.812 2.047 2.453 3.687 4.5 4.5zM25.969 9.641c0 0.516-0.187 1.156-0.359 1.656-0.984 2.781-3.656 4.703-6.609 4.703-3.859 0-7-3.141-7-7s3.141-7 7-7c1.141 0 2.625 0.344 3.578 0.984 0.156 0.109 0.25 0.25 0.25 0.438 0 0.172-0.109 0.344-0.25 0.438l-4.578 2.641v3.5l3.016 1.672c0.516-0.297 4.141-2.578 4.453-2.578s0.5 0.234 0.5 0.547z"></path>
</symbol>
<symbol id="icon-plug" viewBox="0 0 28 28">
<path d="M27.422 7.078c0.766 0.781 0.766 2.047 0 2.828l-6.266 6.25 2.344 2.344-2.5 2.5c-3.422 3.422-8.641 3.906-12.516 1.344l-5.656 5.656h-2.828v-2.828l5.656-5.656c-2.562-3.875-2.078-9.094 1.344-12.516l2.5-2.5 2.344 2.344 6.25-6.266c0.781-0.766 2.047-0.766 2.828 0 0.781 0.781 0.781 2.063 0 2.828l-6.25 6.266 3.656 3.656 6.266-6.25c0.781-0.781 2.047-0.781 2.828 0z"></path>
</symbol>
<symbol id="icon-paint-brush" viewBox="0 0 28 28">
<path d="M25.234 0c1.422 0 2.734 1.062 2.734 2.547 0 0.828-0.328 1.625-0.703 2.359-1.219 2.312-5.313 9.953-7.266 11.75-0.953 0.891-2.078 1.422-3.406 1.422-2.641 0-4.797-2.25-4.797-4.875 0-1.25 0.516-2.469 1.437-3.313l9.969-9.047c0.547-0.5 1.266-0.844 2.031-0.844zM11.031 16.156c0.812 1.578 2.297 2.766 4.016 3.219l0.016 1.109c0.094 4.453-3 7.516-7.469 7.516-5.297 0-7.594-4.219-7.594-9.016 0.578 0.391 2.594 2 3.25 2 0.391 0 0.719-0.219 0.859-0.578 1.328-3.469 3.406-4.094 6.922-4.25z"></path>
</symbol>
<symbol id="icon-enlarge2" viewBox="0 0 32 32">
<path d="M32 0v13l-5-5-6 6-3-3 6-6-5-5zM14 21l-6 6 5 5h-13v-13l5 5 6-6z"></path>
</symbol>
<symbol id="icon-shrink2" viewBox="0 0 32 32">
<path d="M14 18v13l-5-5-6 6-3-3 6-6-5-5zM32 3l-6 6 5 5h-13v-13l5 5 6-6z"></path>
</symbol>
<symbol id="icon-square-brackets" viewBox="0 0 21 21">
<path d="M 4.791 18.885 L 4.791 20.518 L 0 20.518 L 0 0 L 4.791 0 L 4.791 1.622 L 2.052 1.622 L 2.052 18.885 L 4.791 18.885 Z M 20.958 0 L 20.958 20.518 L 16.178 20.518 L 16.178 18.885 L 18.906 18.885 L 18.906 1.622 L 16.178 1.622 L 16.178 0 L 20.958 0 Z M 6.542 4.952 A 1.326 1.326 0 0 0 6.404 5.11 Q 6.102 5.516 6.102 6.166 A 2.167 2.167 0 0 0 6.15 6.638 A 1.453 1.453 0 0 0 6.553 7.38 A 1.472 1.472 0 0 0 7.616 7.82 A 1.702 1.702 0 0 0 7.626 7.82 A 1.445 1.445 0 0 0 8.669 7.385 Q 9.109 6.95 9.109 6.166 A 2.149 2.149 0 0 0 9.058 5.685 A 1.429 1.429 0 0 0 8.658 4.958 A 1.482 1.482 0 0 0 7.595 4.522 Q 6.982 4.522 6.542 4.952 Z M 12.311 4.952 A 1.326 1.326 0 0 0 12.173 5.11 Q 11.87 5.516 11.87 6.166 A 2.167 2.167 0 0 0 11.919 6.638 A 1.453 1.453 0 0 0 12.321 7.38 A 1.472 1.472 0 0 0 13.385 7.82 A 1.702 1.702 0 0 0 13.394 7.82 A 1.445 1.445 0 0 0 14.437 7.385 Q 14.878 6.95 14.878 6.166 A 2.149 2.149 0 0 0 14.827 5.685 A 1.429 1.429 0 0 0 14.427 4.958 A 1.482 1.482 0 0 0 13.363 4.522 Q 12.751 4.522 12.311 4.952 Z M 9.06 14.192 A 1.427 1.427 0 0 0 8.653 13.455 Q 8.196 13.02 7.584 13.02 A 1.442 1.442 0 0 0 6.542 13.449 A 1.326 1.326 0 0 0 6.404 13.607 Q 6.102 14.013 6.102 14.663 A 2.679 2.679 0 0 0 6.102 14.698 Q 6.105 14.959 6.16 15.18 A 1.407 1.407 0 0 0 6.542 15.866 A 1.455 1.455 0 0 0 7.595 16.296 Q 8.207 16.296 8.658 15.866 A 1.405 1.405 0 0 0 9.056 15.154 A 2.131 2.131 0 0 0 9.109 14.663 A 2.134 2.134 0 0 0 9.06 14.192 Z M 14.829 14.192 A 1.427 1.427 0 0 0 14.421 13.455 Q 13.965 13.02 13.353 13.02 A 1.442 1.442 0 0 0 12.311 13.449 A 1.326 1.326 0 0 0 12.173 13.607 Q 11.87 14.013 11.87 14.663 A 2.679 2.679 0 0 0 11.87 14.698 Q 11.874 14.959 11.928 15.18 A 1.407 1.407 0 0 0 12.311 15.866 A 1.455 1.455 0 0 0 13.363 16.296 Q 13.976 16.296 14.427 15.866 A 1.405 1.405 0 0 0 14.825 15.154 A 2.131 2.131 0 0 0 14.878 14.663 A 2.134 2.134 0 0 0 14.829 14.192 Z" />
</symbol>
{{ assets.renderSvg() }}
<defs>
</svg>

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,14 @@
<nav id="sidebar-menu" class="sidebar-menu">
<div id="mobile-menu" class="menu-action">{{ translate('Menu') }} <span class="button-arrow"></span></div>
<ul class="mr-4">
{% for name,navitem in systemnavi %}
<li class="mb-1">
<a class="block p-2 border-l-4 border-slate-200 hover:bg-stone-50 hover:border-cyan-500{{ navitem.active ? ' active' : '' }}" href="{{ url_for(navitem.routename) }}">
<svg class="icon {{ navitem.icon }} mr-2"><use xlink:href="#{{ navitem.icon }}"></use></svg> {{ translate(name) }}
</a>
</li>
{% endfor %}
</ul>
</nav>

View File

@@ -0,0 +1,34 @@
{% extends 'layouts/layoutSystem.twig' %}
{% block title %}{{ translate('System Settings') }}{% endblock %}
{% block content %}
<div class="formWrapper">
<h1>{{ translate('System') }} </h1>
<div id="systemsettings" v-cloak>
<systemsettings :userdata="userdata">${ message }</systemsettings>
</div>
</div>
{% endblock %}
{% block javascript %}
<script src="{{ base_url() }}/system/typemill/author/js/vue-system.js?v={{ settings.version }}"></script>
<script>
tmaxios.get('/api/v1/mainnavi',{
'csrf_name': document.getElementById("csrf_name").value,
'csrf_value': document.getElementById("csrf_value").value,
})
.then(function (response) {
console.info(response);
})
.catch(function (error) {
console.info(error);
});
</script>
{% endblock %}

View File

@@ -0,0 +1,268 @@
# German (Deutsche)
# Author:
ACCOUNT: Account
ACTIVE: Aktiv
ACTUAL_PASSWORD: Aktuelles Passwort
ADD: hinzufügen
ADD_CONTENT_BLOCK: + Inhalts-Block
ADD_DEFINITION: + Definition
ADD_FILE: + Seite
ADD_FOLDER: + Ordner
ADD_FOLDER_TO_BASE_LEVEL: + Ordner in Basis-Ebene
ADD_ITEM: + Element
ADD_LEFT_COLUMN: + Linke Spalte
ADD_RIGHT_COLUMN: + Rechte Spalte
ADD_ROW_ABOVE: + Reihe oberhalb
ADD_ROW_BELOW: + Reihe unterhalb
ADMINISTRATOR: administrator
ALL: All visitors
ALL_USERS: Alle Nutzer
ALTERNATIVE_TEXT_FOR_THE_HERO_IMAGE: Alternativ-Text für das Hero-Image
ALT_TEXT: Alt-Text
AUTHOR: Autor
AUTHOR_DESCRIPTION_(MARKDOWN): Autoren-Beschreibung (Markdown)
BACK_TO_STARTPAGE: Zurück zur Startseite
BOLD: fett
BOTTOM: Unten
BROWSE: WÄHLEN
BULLET_LIST: Auflistung
BY: von
CANCEL: abbrechen
CAN_BE_USED_FOR_AUTHOR_LINE_IN_FRONTEND_: Kann als Autorenzeile im Frontend benutzt werden.
CAPTION: Bild-Unterschrift
CELL: Zelle
CENTER: Mitte
CHECK: prüfen
CHOOSE_FILE: Datei wählen
CLASS: Class
CLOSE_LIBRARY: Medialib schließen
CODE: Code
COG: Einstellungen
CONTENT: Inhalt
COPYRIGHT: Copyright
CREATED_AT_(READONLY): Erstellt am (nur lesend)
CREATED_AT_(READ_ONLY): Erstellt am (nur lesend)
CREATE_NEW_USER: + Neuen Nutzer
CREATE_USER: + Nutzer
CROSS: Kreuz
CUSTOM_CSS: Eigenes CSS
DEFINITION: Definitions-Liste
DEFINITION_LIST: Definitions-Liste
DELETE: löschen
DELETE_CLOSE: löschen
DELETE_COLUMN: Spalte löschen
DELETE_CONTENT_BLOCK: Inhaltsblock löschen
DELETE_PAGE: Seite löschen
DELETE_ROW: Reihe löschen
DELETE_USER: Nutzer löschen
DESCRIPTION: Beschreibung
DISCARD: Verwerfen
DISCARD_CHANGES: Änderungen verwerfen
DO_YOU_REALLY_WANT_TO_DELETE_THE_USER: Soll der Nutzer wirklich gelöscht werden?
DO_YOU_REALLY_WANT_TO_DELETE_THIS_PAGE: Soll die Seite wirklich gelöscht werden?
DO_YOU_WANT_TO_DISCARD_YOUR_CHANGES_AND_SET_THE_CONTENT_BACK_TO_THE_LIVE_VERSION: Sollen die Änderungen wirklich verworfen und der Inhalt auf den Live-Zustand zurückgesetzt werden?
DRAFT: Entwurf
DRAG_A_PICTURE_OR_CLICK_TO_SELECT: Bild hochladen
DUTCH__FLEMISH: Dänisch, Flämisch
EDIT: editieren
EDITOR: editor
EDIT_USER: Nutzer bearbeiten
ENGLISH: Englisch
EXTERNAL_LINK: externer Link
E_G_: z.B.
E_MAIL: E-Mail
FAVICON: Favicon
FILE: Datei
FILES: Dateien
FIRST_NAME: Vorname
FOLDER: Ordner
FORGOT_PASSWORD: Passwort vergessen
FORMAT: Format
FRENCH: French
GENERAL_PRESENTATION: Generelle Darstellung
GERMAN: Deutsch
GOOGLE_SITEMAP: Google Sitemap
HAS_EDIT_RIGHTS_FOR_THIS_ARTICLE_: Hat Schreib-Rechte für diesen Artikel.
HEAD: Kopf
HEADLINE: Überschrift
HEADLINE_ANCHORS: Überschriften-Anker
HERO_IMAGE: Hero Image
HIDE: Verbergen
HIDE_PAGE_FROM_NAVIGATION: Seite in Navigation verbergen
HOME: Home
HOMEPAGE: Homepage
HORIZONTAL_LINE: Horizontale Linie
HR: Horizontale Linie
IF_NOT_FILLED__THE_DESCRIPTION_IS_EXTRACTED_FROM_CONTENT_: Wenn leer wird die Beschreibung vom Inhalt extrahiert.
IMAGE: Bild
IMAGES: Bilder
IMAGE_URL: Bild URL
IMAGE_URL_(READ_ONLY): Bild URL (nur lesend)
ITALIAN: Italienisch
ITALIC: kursiv
LANGUAGE: Sprache
LANGUAGE_ADMIN: Sprache (Admin-UI)
LANGUAGE_ATTR: Sprach-Attribut (website)
LAST_MODIFIED_LIVE_(READONLY): Zuletzt live geändert (nur lesend)
LAST_NAME: Zuname
LEFT: Links
LICENCE: Lizenz
LINK: Link
LINK_TO_VIDEO: Link zum Video
LOGIN: Anmelden
LOGO: Logo
LOGOUT: Abmelden
MANUAL_DATE: Manuelles Datum
MARKDOWN: Markdown
MAXIMUM_SIZE_FOR_AN_IMAGE_IS_5_MB__HERO_IMAGES_ARE_NOT_SUPPORTED_BY_ALL_THEMES_: Die maximale Größe beträgt 5 MB. Hero-Images werden nicht von allen Themes unterstützt.
MEMBER: Member
MENU: Menü
META: Meta
META_DESCRIPTION: Meta description
META_TITLE: Meta title
MISSING_REQUIREMENTS: Fehlende Anforderungen
MOVE_VERTICAL: vertikal verschieben
NAVIGATION_TITLE: Navigationstitel
NEW_PASSWORD: Neues Passwort
NONE: Keine
NOTICE: Hinweis
NOT_EDITABLE: Nicht editierbar
NO_DESCRIPTION: Keine Beschreibung
NO_PREVIEW: Keine Vorschau
NO_SETTINGS: Keine Einstellungen
NUMBERED_LIST: Aufzählung
OLIST: olist
ONLINE: online
ONLY_THE_FOLLOWING_SPECIAL_CHARACTERS_ARE_ALLOWED: Nur die folgenden Sonderzeichen sind erlaubt
OWNER_(USERNAME): Besitzer (username)
PARAGRAPH: Absatz
PASSWORD: Passwort
PLEASE_CONFIRM: Bitte bestätigen
PLEASE_CORRECT_THE_ERRORS_ABOVE: Bitte korrigiere die Fehler oben
PLUGINS: Plugins
PLUGIN_STORE: Plugin Store
POWER_OFF: Energie aus
PROFILE_IMAGE: Profil-Bild
PUBLISH: Publizieren
QUOTE: Zitat
QUOTES: Zitate
RAW: raw
RAW_CONTENT_EDITOR: Raw Content Editor
RAW_MARKDOWN_EDITOR: Raw Markdown Editor
RAW_MODE: Raw Modus
RAW_USERDATA_(READONLY_FOR_ADMINS): Raw Nutzerdaten (nur lesend für Admins)
READONLY: Nur lesend
REGISTERED_USERS_ONLY: Nur registrierte Nutzer
REMEMBER_TO_BOOKMARK_THIS_PAGE: Bookmark nicht vergessen
REQUIRED: erforderlich
RIGHT: Recht
ROLE: Rolle
RUSSIAN: Russisch
SAVE: speichern
SAVED_SUCCESSFULLY: Erfolgreich gespeichert
SAVE_ALL_SETTINGS: Alle Einstellungen speichern
SAVE_THEME: Theme speichern
SELECT_FROM_MEDIALIB: Aus Medialib wählen
SETTINGS: Einstellungen
SETTINGS_ARE_STORED: Einstellungen sind gespeichert
SETUP: Setup
SHOW_ANCHORS_NEXT_TO_HEADLINES: Anker neben Überschrift anzeigen
STANDARD_EDITOR_MODE: Standard Editor-Modus
START: Start
SYSTEM: System
TABLE: Tabelle
TABLE_OF_CONTENTS: Inhaltsverzeichnis
TAKEN_FROM_YOUR_USER_ACCOUNT_IF_SET_: Vom Nutzer-Account genommen falls vorhanden.
TERM: Bedingung
TEXT_FILE: Text-Datei
THEMES: Themes
THEME_STORE: Theme Store
THE_FORMAT_BUTTONS: Die Formatierungs-Buttons
TITLE: Titel
TOC: IHV
TOP: Oben
TYPEMILL_DESCRIPTION: The standard theme for Typemill. Responsive, minimal and without any dependencies. It uses the system fonts Calibri and Helvetica. No JavaScript is used.
ULIST: ulist
UNKNOWN: Unbekannt
UPDATE_USER: Nutzer aktualisieren
UPLOAD: hochladen
UPLOAD_AN_IMAGE: Bild hochladen
UPLOAD_FILE: Datei hochladen
UPS__WRONG_PASSWORD_OR_USERNAME__PLEASE_TRY_AGAIN_: Ups, wrong password or username, please try again.
USED_AS_FALLBACK_WHEN_NO_MANUAL_DATE_IS_SET_: Wird alternativ zum manuellen Datum genutzt.
USER: Nutzer
USERNAME: Nutzername
USERNAME_(READ_ONLY): Nutzername (nur lesend)
USERS: Nutzer
USE_2_TO_20_CHARACTERS: 2 bis 20 Anschläge erlaubt.
USE_2_TO_40_CHARACTERS: 2 to 40 Anschläge erlaubt.
USE_A_VALID_LANGUAGE_ATTRIBUTE: Gültiges Language Attribut erforderlich.
USE_A_VALID_YEAR: Gültiges Jahr erforderlich.
VIDEO: Video
VIEW_SITE: Zur Webseite
VISUAL: visuell
VISUAL_CONTENT_EDITOR: Visueller Inhalts-Editor
VISUAL_EDITOR: Visueller Editor
VISUAL_MARKDOWN_EDITOR: Visueller Markdown Editor
VISUAL_MODE: visueller Modus
WAIT: warte
WEB: Web
WEBSITE_TITLE: Webseiten-Titel
WEBSITE_VISIBLE_FOR: Website sichtbar für
WRITING: Schreiben
YEAR: Jahr
YOU_CAN_OVERWRITE_THE_THEME_CSS_WITH_YOUR_OWN_CSS_HERE_: Du kannst das CSS des Themes hier überschreiben.
ADD_NEW_FEATURES_TO_YOUR_WEBSITE_WITH_PLUGINS_AND_CONFIGURE_THEM_: Füge mit Plugins neue Funktionen hinzu.
BY_THE: von der
CHOOSE_A_THEME_FOR_YOUR_WEBSITE_AND_CONFIGURE_THE_THEME_DETAILS_: Richte ein Theme für deine Webseite ein.
CODED_WITH: Entwickelt mit
COMMUNITY: Community
CONFIGURE_YOUR_WEBSITE: Zur Autorenoberfläche
DOCS: Dokumentation
GET_HELP: Hilfe erhalten
GIVE_YOUR_NEW_WEBSITE_A_NAME__ADD_THE_AUTHOR_AND_CHOOSE_A_COPYRIGHT_: Konfiguriere das System.
HURRA: Hurra
IF_YOU_HAVE_ANY_QUESTIONS__PLEASE_READ_THE: Wenn du Fragen hast, lies bitte die
NEXT_STEP: Nächster Schritt
OR_OPEN_A_NEW_ISSUE_ON: oder erstelle ein neues Issue auf
SETUP_WELCOME: Setup Willkommen
TRENDSCHAU_DIGITAL: Trendschau Digital
VISIT_THE_AUTHOR_PANEL_AND_SETUP_YOUR_NEW_WEBSITE__YOU_CAN_CONFIGURE_THE_SYSTEM__CHOOSE_THEMES_AND_ADD_PLUGINS_: Besuche die Autorenoberfläche und richte die Seite ein. Du kannst das System konfigurieren, Themes auswählen and Plugins hinzufügen.
YOUR_ACCOUNT_HAS_BEEN_CREATED_AND_YOU_ARE_LOGGED_IN_NOW_: Dein Account wurde erstellt und du bist jetzt angemeldet.
ACCESS_CONTROL: Access Control
ACTIVATE_INDIVIDUAL_RESTRICTIONS_FOR_PAGES_IN_THE_META_TAB_OF_EACH_PAGE_: Aktiviere die individuellen Seiten-Beschränkungen im Meta-Tab einer jeden Seite.
ADD_ONE_OR_MORE_USERNAMES_SEPARATED_WITH_COMMA_: Ein oder mehrere Nutzernamen mit Komma getrennt.
CUT_RESTRICTED_CONTENT_AFTER_THE_FIRST_HR_ELEMENT_ON_A_PAGE_(PER_DEFAULT_CONTENT_WILL_BE_CUT_AFTER_TITLE)_: Verberge den Inhalt nach dem ersten Trennstrich (andernfalls werden alle Inhalte nach dem Titel verborgen).
FOR_ACCESS_THE_USER_MUST_HAVE_THIS_MINIMUM_ROLE: Für einen Zugriff muss der Nutzer diese minimale Rolle haben
LIMIT_THE_ACCESS_FOR_THE_WHOLE_WEBSITE_OR_FOR_EACH_PAGE_INDIVIDUALLY__IF_YOU_ACTIVATE_THE_WEBSITE_RESTRICTION_OR_THE_PAGE_RESTRICTIONS__THEN_SESSIONS_WILL_BE_USED_IN_FRONTEND_: Begrenze den Zugriff auf die gesamte Webseite oder für jede Seite individuell. Wenn die Website-Beschränkungen oder die Seiten-Beschränkungen aktiviert werden, dann werden im Frontend Sessions genutzt.
ONLY_THE_FOLLOWING_USERS_HAVE_ACCESS: Nur die folgenden Nutzer haben Zugriff
PAGE_RESTRICTIONS___ACTIVATE: Seiten-Beschränkungen - Aktivieren
PAGE_RESTRICTIONS___CUT_RESTRICTED_CONTENT: Seiten-Beschränkungen - Verberge die Inhalte
PAGE_RESTRICTIONS___NOTICE: Seiten-Beschränkungen - Hinweis-Text
PAGE_RESTRICTIONS___WRAP_NOTICE_INTO_A_BOX: Seiten-Beschränkungen - Hinweis in Box-Design
SELECT_THE_LOWEST_USERROLE__HIGHER_ROLES_WILL_HAVE_ACCESS_TOO_: Wähle die niedrigste Nutzerrolle. Höhere Nutzerrollen haben ebenfalls Zugriff.
SHOW_THE_WEBSITE_ONLY_TO_AUTHENTICATED_USERS_AND_REDIRECT_ALL_OTHER_USERS_TO_THE_LOGIN_PAGE_: Zeige die Webseite nur authentifizierten Nutzern und leite alle anderen Nutzer zur Login-Seite um.
USE_MARKDOWN: Markdown erlaubt
WEBSITE_RESTRICTION: Website Beschränkungen
WRAP_THE_RESTRICTION_NOTICE_ABOVE_INTO_A_NOTICE_4_ELEMENT_(WHICH_CAN_BE_DESIGNED_AS_SPECIAL_BOX): Gestalte den Hinweis als Notiz-Box (das Design hängt vom Theme ab)
ACTIVATE_CACHE_FOR_TWIG_TEMPLATES: Aktiviere den Cache für Twig-Templates
ADD_MORE_URL_SCHEMES_FOR_EXTERNAL_LINKS_E_G__LIKE_DICT://_(COMMA_SEPARATED_LIST): URL Schemes für externe Links hinzufügen, z.B. dict:// (mit Komma getrennte Liste)
CLEAR_CACHE: Cache reinigen
DELETE_ALL_CACHE_FILES: Alle Dateien im Cache löschen
DEVELOPER: Entwickler
DISABLE_HEADERS: Header deaktivieren
DISABLE_TYPEMILL_HEADERS_AND_SEND_YOUR_OWN: Typemill Header deaktivieren und eigene Header senden
DISPLAY_APPLICATION_ERRORS: Zeige Fehler-Reports der Applikation
ERROR_REPORTING: Fehler-Reports
IF_YOU_ADD_A_VALUE_FOR_THE_HEIGHT__THEN_THE_IMAGE_WILL_BE_CROPPED_: Wenn eine Angabe für die Höhe gemacht wird, wird das Bild ausgeschnitten
PROXY: Proxy
STANDARD_HEIGHT_FOR_IMAGES: Standard-Höhe für Bilder
STANDARD_WIDTH_FOR_IMAGES: Standard-Breite für Bilder
THE_FOLLOWING_OPTIONS_ARE_ONLY_FOR_DEVELOPERS: Die folgenden Einstellungen sind nur für Entwickler und erfahrene Administratoren. Ändere nur Einstellungen, die du wirklich verstehst. Beispielsweise dürfen niemals die Fehler-Berichte im Frontend dauerhaft aktiviert werden, sondern nur für kurze Fehler-Analysen.
THIS_APPLIES_ONLY_FOR_FUTURE_IMAGES_IN_THE_CONTENT_AREA_: Die Einstellung greift nur bei Bildern im Content-Bereich, die in der Zukunft hochgeladen werden.
TRUSTED_IPS_FOR_PROXY_(COMMA_SEPARATED): Vertrauenswürdige IPs für Proxy (mit Komma getrennte Liste)
TWIG_CACHE: Twig Cache
USE_X_FORWARDED_HEADERS: Nutze die X-Forwarded Headers

View File

@@ -0,0 +1,268 @@
# English
# Author: Sebastian Schuermanns
ACCOUNT: Account
ACTIVE: Active
ACTUAL_PASSWORD: Actual Password
ADD: add
ADD_CONTENT_BLOCK: add content-block
ADD_DEFINITION: add definition
ADD_FILE: add file
ADD_FOLDER: add folder
ADD_FOLDER_TO_BASE_LEVEL: add folder to base level
ADD_ITEM: add item
ADD_LEFT_COLUMN: add left column
ADD_RIGHT_COLUMN: add right column
ADD_ROW_ABOVE: add row above
ADD_ROW_BELOW: add row below
ADMINISTRATOR: administrator
ALL: all
ALL_USERS: all users
ALTERNATIVE_TEXT_FOR_THE_HERO_IMAGE: alternative text for the hero image
ALT_TEXT: alt-text
AUTHOR: author
AUTHOR_DESCRIPTION_(MARKDOWN): author-description (markdown)
BACK_TO_STARTPAGE: back to startpage
BOLD: bold
BOTTOM: bottom
BROWSE: BROWSE
BULLET_LIST: bullet list
BY: by
CANCEL: cancel
CAN_BE_USED_FOR_AUTHOR_LINE_IN_FRONTEND_: Can be used for author line in frontend.
CAPTION: caption
CELL: cell
CENTER: center
CHECK: check
CHOOSE_FILE: choose file
CLASS: class
CLOSE_LIBRARY: close library
CODE: code
COG: cog
CONTENT: content
COPYRIGHT: copyright
CREATED_AT_(READONLY): Created at (read only)
CREATED_AT_(READ_ONLY): Created at (readonly)
CREATE_NEW_USER: Create new user
CREATE_USER: Create user
CROSS: cross
CUSTOM_CSS: Custom CSS
DEFINITION: definition list
DEFINITION_LIST: definition list
DELETE: delete
DELETE_CLOSE: delete/close
DELETE_COLUMN: delete column
DELETE_CONTENT_BLOCK: delete content-block
DELETE_PAGE: delete page
DELETE_ROW: delete row
DELETE_USER: delete user
DESCRIPTION: description
DISCARD: discard
DISCARD_CHANGES: discard changes
DO_YOU_REALLY_WANT_TO_DELETE_THE_USER: Do you really want to delete the user
DO_YOU_REALLY_WANT_TO_DELETE_THIS_PAGE: Do you really want to delete this page?
DO_YOU_WANT_TO_DISCARD_YOUR_CHANGES_AND_SET_THE_CONTENT_BACK_TO_THE_LIVE_VERSION: Do you want to discard your changes and set the content back to the live version?
DRAFT: draft
DRAG_A_PICTURE_OR_CLICK_TO_SELECT: upload an image
DUTCH__FLEMISH: Dutch, Flemish
EDIT: edit
EDITOR: editor
EDIT_USER: edit user
ENGLISH: English
EXTERNAL_LINK: external-link
E_G_: e.g.
E_MAIL: e-mail
FAVICON: favicon
FILE: file
FILES: Files
FIRST_NAME: first name
FOLDER: folder
FORGOT_PASSWORD: forgot password
FORMAT: format
FRENCH: French
GENERAL_PRESENTATION: General Presentation
GERMAN: German
GOOGLE_SITEMAP: google sitemap
HAS_EDIT_RIGHTS_FOR_THIS_ARTICLE_: Has edit rights for this article.
HEAD: Head
HEADLINE: Headline
HEADLINE_ANCHORS: Headline anchors
HERO_IMAGE: Hero image
HIDE: Hide
HIDE_PAGE_FROM_NAVIGATION: Hide page from navigation
HOME: home
HOMEPAGE: Homepage
HORIZONTAL_LINE: Horizontal Line
HR: hr
IF_NOT_FILLED__THE_DESCRIPTION_IS_EXTRACTED_FROM_CONTENT_: If not filled, the description is extracted from content.
IMAGE: Image
IMAGES: Images
IMAGE_URL: Image URL
IMAGE_URL_(READ_ONLY): Image URL (read only)
ITALIAN: Italian
ITALIC: italic
LANGUAGE: Language
LANGUAGE_ADMIN: Language (admin-ui)
LANGUAGE_ATTR: Language Attribute (website)
LAST_MODIFIED_LIVE_(READONLY): Last modified live (readonly)
LAST_NAME: Last Name
LEFT: Left
LICENCE: Licence
LINK: Link
LINK_TO_VIDEO: Link to video
LOGIN: Login
LOGO: Logo
LOGOUT: Logout
MANUAL_DATE: Manual date
MARKDOWN: markdown
MAXIMUM_SIZE_FOR_AN_IMAGE_IS_5_MB__HERO_IMAGES_ARE_NOT_SUPPORTED_BY_ALL_THEMES_: Maximum size for an image is 5 MB. Hero images are not supported by all themes.
MEMBER: member
MENU: Menu
META: meta
META_DESCRIPTION: Meta description
META_TITLE: Meta title
MISSING_REQUIREMENTS: Missing Requirements
MOVE_VERTICAL: move vertical
NAVIGATION_TITLE: Navigation Title
NEW_PASSWORD: New Password
NONE: None
NOTICE: Notice
NOT_EDITABLE: not editable
NO_DESCRIPTION: No description
NO_PREVIEW: No Preview
NO_SETTINGS: No Settings
NUMBERED_LIST: Numbered List
OLIST: olist
ONLINE: online
ONLY_THE_FOLLOWING_SPECIAL_CHARACTERS_ARE_ALLOWED: Only the following special characters are allowed
OWNER_(USERNAME): owner (username)
PARAGRAPH: Paragraph
PASSWORD: Password
PLEASE_CONFIRM: Please confirm
PLEASE_CORRECT_THE_ERRORS_ABOVE: Please correct the errors above
PLUGINS: Plugins
PLUGIN_STORE: Plugin Store
POWER_OFF: power-off
PROFILE_IMAGE: Profile-Image
PUBLISH: Publish
QUOTE: Quote
QUOTES: Quote
RAW: raw
RAW_CONTENT_EDITOR: Raw Content Editor
RAW_MARKDOWN_EDITOR: Raw Markdown Editor
RAW_MODE: raw mode
RAW_USERDATA_(READONLY_FOR_ADMINS): Raw Userdata (readonly for admins)
READONLY: Readonly
REGISTERED_USERS_ONLY: registered users only
REMEMBER_TO_BOOKMARK_THIS_PAGE: Remember to bookmark this page
REQUIRED: Required
RIGHT: Right
ROLE: Role
RUSSIAN: Russian
SAVE: Save
SAVED_SUCCESSFULLY: Saved successfully
SAVE_ALL_SETTINGS: Save All Settings
SAVE_THEME: Save Theme
SELECT_FROM_MEDIALIB: select from medialib
SETTINGS: Settings
SETTINGS_ARE_STORED: Settings are stored
SETUP: Setup
SHOW_ANCHORS_NEXT_TO_HEADLINES: Show anchors next to headlines
STANDARD_EDITOR_MODE: Standard Editor Mode
START: Start
SYSTEM: System
TABLE: Table
TABLE_OF_CONTENTS: Table of Contents
TAKEN_FROM_YOUR_USER_ACCOUNT_IF_SET_: Taken from your user account if set.
TERM: term
TEXT_FILE: text-file
THEMES: Themes
THEME_STORE: Theme Store
THE_FORMAT_BUTTONS: The Format Buttons
TITLE: Title
TOC: toc
TOP: Top
TYPEMILL_DESCRIPTION: The standard theme for Typemill. Responsive, minimal and without any dependencies. It uses the system fonts Calibri and Helvetica. No JavaScript is used.
ULIST: ulist
UNKNOWN: Unknown
UPDATE_USER: Update User
UPLOAD: upload
UPLOAD_AN_IMAGE: upload an image
UPLOAD_FILE: Upload a file
UPS__WRONG_PASSWORD_OR_USERNAME__PLEASE_TRY_AGAIN_: Ups, wrong password or username, please try again.
USED_AS_FALLBACK_WHEN_NO_MANUAL_DATE_IS_SET_: Used as fallback when no manual date is set.
USER: User
USERNAME: Username
USERNAME_(READ_ONLY): Username (read only)
USERS: Users
USE_2_TO_20_CHARACTERS: Use 2 to 20 characters.
USE_2_TO_40_CHARACTERS: Use 2 to 40 characters.
USE_A_VALID_LANGUAGE_ATTRIBUTE: Use a valid language attribute
USE_A_VALID_YEAR: Use a valid year
VIDEO: Video
VIEW_SITE: View Site
VISUAL: visual
VISUAL_CONTENT_EDITOR: Visual Content Editor
VISUAL_EDITOR: Visual Editor
VISUAL_MARKDOWN_EDITOR: Visual Markdown Editor
VISUAL_MODE: visual mode
WAIT: wait
WEB: Web
WEBSITE_TITLE: Website Title
WEBSITE_VISIBLE_FOR: Website visible for
WRITING: Writing
YEAR: Year
YOU_CAN_OVERWRITE_THE_THEME_CSS_WITH_YOUR_OWN_CSS_HERE_: You can overwrite the theme-css with your own css here.
ADD_NEW_FEATURES_TO_YOUR_WEBSITE_WITH_PLUGINS_AND_CONFIGURE_THEM_: Add new features to your website with plugins and configure them.
BY_THE: by the
CHOOSE_A_THEME_FOR_YOUR_WEBSITE_AND_CONFIGURE_THE_THEME_DETAILS_: Choose a theme for your website and configure the theme details.
CODED_WITH: Coded with
COMMUNITY: community
CONFIGURE_YOUR_WEBSITE: Configure your website
DOCS: docs
GET_HELP: Get help
GIVE_YOUR_NEW_WEBSITE_A_NAME__ADD_THE_AUTHOR_AND_CHOOSE_A_COPYRIGHT_: Give your new website a name, add the author and choose a copyright.
HURRA: Hurra
IF_YOU_HAVE_ANY_QUESTIONS__PLEASE_READ_THE: If you have any questions, please read the
NEXT_STEP: Next step
OR_OPEN_A_NEW_ISSUE_ON: or open a new issue on
SETUP_WELCOME: Setup Welcome
TRENDSCHAU_DIGITAL: Trendschau Digital
VISIT_THE_AUTHOR_PANEL_AND_SETUP_YOUR_NEW_WEBSITE__YOU_CAN_CONFIGURE_THE_SYSTEM__CHOOSE_THEMES_AND_ADD_PLUGINS_: Visit the author panel and setup your new website. You can configure the system, choose themes and add plugins.
YOUR_ACCOUNT_HAS_BEEN_CREATED_AND_YOU_ARE_LOGGED_IN_NOW_: Your account has been created and you are logged in now.
ACCESS_CONTROL: Access Control
ACTIVATE_INDIVIDUAL_RESTRICTIONS_FOR_PAGES_IN_THE_META_TAB_OF_EACH_PAGE_: Activate individual restrictions for pages in the meta-tab of each page.
ADD_ONE_OR_MORE_USERNAMES_SEPARATED_WITH_COMMA_: Add one or more usernames separated with comma.
CUT_RESTRICTED_CONTENT_AFTER_THE_FIRST_HR_ELEMENT_ON_A_PAGE_(PER_DEFAULT_CONTENT_WILL_BE_CUT_AFTER_TITLE)_: Cut restricted content after the first hr-element on a page (per default content will be cut after title).
FOR_ACCESS_THE_USER_MUST_HAVE_THIS_MINIMUM_ROLE: For access the user must have this minimum role
LIMIT_THE_ACCESS_FOR_THE_WHOLE_WEBSITE_OR_FOR_EACH_PAGE_INDIVIDUALLY__IF_YOU_ACTIVATE_THE_WEBSITE_RESTRICTION_OR_THE_PAGE_RESTRICTIONS__THEN_SESSIONS_WILL_BE_USED_IN_FRONTEND_: Limit the access for the whole website or for each page individually. If you activate the website restriction or the page restrictions, then sessions will be used in frontend.
ONLY_THE_FOLLOWING_USERS_HAVE_ACCESS: Only the following users have access
PAGE_RESTRICTIONS___ACTIVATE: Page Restrictions - Activate
PAGE_RESTRICTIONS___CUT_RESTRICTED_CONTENT: Page Restrictions - Cut Restricted Content
PAGE_RESTRICTIONS___NOTICE: Page Restrictions - Notice
PAGE_RESTRICTIONS___WRAP_NOTICE_INTO_A_BOX: Page Restrictions - Wrap Notice into a Box
SELECT_THE_LOWEST_USERROLE__HIGHER_ROLES_WILL_HAVE_ACCESS_TOO_: Select the lowest userrole. Higher roles will have access too.
SHOW_THE_WEBSITE_ONLY_TO_AUTHENTICATED_USERS_AND_REDIRECT_ALL_OTHER_USERS_TO_THE_LOGIN_PAGE_: Show the website only to authenticated users and redirect all other users to the login page.
USE_MARKDOWN: use markdown
WEBSITE_RESTRICTION: Website Restriction
WRAP_THE_RESTRICTION_NOTICE_ABOVE_INTO_A_NOTICE_4_ELEMENT_(WHICH_CAN_BE_DESIGNED_AS_SPECIAL_BOX): Wrap the restriction notice above into a notice-4 element (which can be designed as special box)
ACTIVATE_CACHE_FOR_TWIG_TEMPLATES: Activate Cache for Twig Templates
ADD_MORE_URL_SCHEMES_FOR_EXTERNAL_LINKS_E_G__LIKE_DICT://_(COMMA_SEPARATED_LIST): Add more url schemes for external links e.g. like dict:// (comma separated list)
CLEAR_CACHE: Clear Cache
DELETE_ALL_CACHE_FILES: Delete all cache files
DEVELOPER: Developer
DISABLE_HEADERS: Disable Headers
DISABLE_TYPEMILL_HEADERS_AND_SEND_YOUR_OWN: Disable Typemill Headers And Send Your Own
DISPLAY_APPLICATION_ERRORS: Display Application Errors
ERROR_REPORTING: Error Reporting
IF_YOU_ADD_A_VALUE_FOR_THE_HEIGHT__THEN_THE_IMAGE_WILL_BE_CROPPED_: If you add a value for the height, then the image will be cropped.
PROXY: Proxy
STANDARD_HEIGHT_FOR_IMAGES: Standard height for images
STANDARD_WIDTH_FOR_IMAGES: Standard width for images
THE_FOLLOWING_OPTIONS_ARE_ONLY_FOR_DEVELOPERS: The following options are only for developers and experienced administrators. Only change the options if you really understand them. For example, never activate the error reporting for a live website, use this option only for bug-fixing.
THIS_APPLIES_ONLY_FOR_FUTURE_IMAGES_IN_THE_CONTENT_AREA_: This applies only for future images in the content area.
TRUSTED_IPS_FOR_PROXY_(COMMA_SEPARATED): Trusted IPs for proxy (comma separated)
TWIG_CACHE: Twig Cache
USE_X_FORWARDED_HEADERS: Use X-Forwarded Headers

View File

@@ -0,0 +1,268 @@
# French (Français)
# Author:
ACCOUNT: Compte
ACTIVE: Actif
ACTUAL_PASSWORD: Mot de passe actuel
ADD: Ajouter
ADD_CONTENT_BLOCK: ajouter un bloc
ADD_DEFINITION: ajouter une définition
ADD_FILE: Ajouter un article
ADD_FOLDER: Ajouter un dossier
ADD_FOLDER_TO_BASE_LEVEL: Ajouter un dossier de premier niveau
ADD_ITEM: Ajouter un élément
ADD_LEFT_COLUMN: Ajouter une colonne à gauche
ADD_RIGHT_COLUMN: Ajouter une colonne à droite
ADD_ROW_ABOVE: Ajouter une rangée en haut
ADD_ROW_BELOW: Ajouter une rangée en bas
ADMINISTRATOR: administrateur
ALL: tous
ALL_USERS: Tous les utilisateurs
ALTERNATIVE_TEXT_FOR_THE_HERO_IMAGE: Texte alternatif pour la bannière d'accueil
ALT_TEXT: Texte alternatif
AUTHOR: Auteur
AUTHOR_DESCRIPTION_(MARKDOWN): Auteur-Description (Markdown)
BACK_TO_STARTPAGE: Revenir à la page d'accueil
BOLD: Gras
BOTTOM: En bas
BROWSE: Parcourir
BULLET_LIST: Liste à puces
BY: de
CANCEL: Annuler
CAN_BE_USED_FOR_AUTHOR_LINE_IN_FRONTEND_: Sera affiché comme auteur sur le site web.
CAPTION: Légende
CELL: Cellule
CENTER: Center
CHECK: Vérifier
CHOOSE_FILE: Sélectionner un fichier
CLASS: Classe
CLOSE_LIBRARY: Fermer la bibliothèque
CODE: Code
COG: cog
CONTENT: Contenu
COPYRIGHT: Copyright
CREATED_AT_(READONLY): Date de création (lecture seule)
CREATED_AT_(READ_ONLY): Date de création (lecture seule)
CREATE_NEW_USER: Créer un nouvel utilisateur
CREATE_USER: Créer un utilisateur
CROSS: croix
CUSTOM_CSS: CSS personnalisé
DEFINITION: Liste de définition
DEFINITION_LIST: Liste de définition
DELETE: Supprimer
DELETE_CLOSE: Supprimer/Fermer
DELETE_COLUMN: Supprimer cette colonne
DELETE_CONTENT_BLOCK: Supprimer un bloc
DELETE_PAGE: Supprimer la page
DELETE_ROW: Supprimer une rangée
DELETE_USER: Supprimer l'utilisateur
DESCRIPTION: Description
DISCARD: Annuler les modifications
DISCARD_CHANGES: Ne pas sauvegarder les modifications
DO_YOU_REALLY_WANT_TO_DELETE_THE_USER: Voulez-vous vraiment supprimer cet utilisateur ?
DO_YOU_REALLY_WANT_TO_DELETE_THIS_PAGE: Voulez-vous vraiment supprimer cette page ?
DO_YOU_WANT_TO_DISCARD_YOUR_CHANGES_AND_SET_THE_CONTENT_BACK_TO_THE_LIVE_VERSION: Voulez-vous annuler les modifications et revenir à la version précédente ?
DRAFT: comme brouillon
DRAG_A_PICTURE_OR_CLICK_TO_SELECT: faire glisser une image ou cliquer pour sélectionner
DUTCH__FLEMISH: Néerlandais, Flamand
EDIT: Éditer
EDITOR: editor
EDIT_USER: Modifier l'utilisateur
ENGLISH: Anglais
EXTERNAL_LINK: Lien externe
E_G_: ex.:
E_MAIL: Courriel
FAVICON: Favicon
FILE: Fichier
FILES: Fichiers
FIRST_NAME: Nom
FOLDER: Dossier
FORGOT_PASSWORD: Mot de passe oublié
FORMAT: Mise en forme
FRENCH: Français
GENERAL_PRESENTATION: Presentation générale
GERMAN: Allemand
GOOGLE_SITEMAP: Google Sitemap
HAS_EDIT_RIGHTS_FOR_THIS_ARTICLE_: Possède les droits d'édition sur cette article.
HEAD: Tête de colonne
HEADLINE: Titre
HEADLINE_ANCHORS: Ancres de titre
HERO_IMAGE: Bannière d'accueil (Hero image)
HIDE: Masquer
HIDE_PAGE_FROM_NAVIGATION: Masquer dans le menu de navigation du site
HOME: Accueil
HOMEPAGE: Page d'accueil
HORIZONTAL_LINE: Ligne horizontale
HR: Ligne horizontale
IF_NOT_FILLED__THE_DESCRIPTION_IS_EXTRACTED_FROM_CONTENT_: Si non renseignée ici, la description est extraite du contenu.
IMAGE: Image
IMAGES: Images
IMAGE_URL: URL de l'image
IMAGE_URL_(READ_ONLY): URL de l'image (lecture seule)
ITALIAN: Italien
ITALIC: Italique
LANGUAGE: Langue
LANGUAGE_ADMIN: Langue de l'interface d'administration
LANGUAGE_ATTR: Langue du site (website)
LAST_MODIFIED_LIVE_(READONLY): Dernière modification (lecture seule)
LAST_NAME: Prénom
LEFT: Gauche
LICENCE: Licence
LINK: Lien
LINK_TO_VIDEO: Lien vers une vidéo
LOGIN: Connexion
LOGO: Logo
LOGOUT: Déconnexion
MANUAL_DATE: Date manuelle
MARKDOWN: Markdown
MAXIMUM_SIZE_FOR_AN_IMAGE_IS_5_MB__HERO_IMAGES_ARE_NOT_SUPPORTED_BY_ALL_THEMES_: La taille maximale pour une image est 5 Mo. Les bannières d'accueil ne sont pas gérées dans tous les thèmes.
MEMBER: membre
MENU: Menu
META: Métadonnées
META_DESCRIPTION: Meta description
META_TITLE: Meta titre
MISSING_REQUIREMENTS: Prérequis non satisfaits
MOVE_VERTICAL: Déplacer verticalement
NAVIGATION_TITLE: Titre dans le menu de navigation
NEW_PASSWORD: Nouveau mot de passe
NONE: Aucune
NOTICE: Note
NOT_EDITABLE: Non modifiable
NO_DESCRIPTION: Aucune description
NO_PREVIEW: Pas de prévisualisation
NO_SETTINGS: Aucun paramètre
NUMBERED_LIST: Liste numérotée
OLIST: Liste ordonnée
ONLINE: Online
ONLY_THE_FOLLOWING_SPECIAL_CHARACTERS_ARE_ALLOWED: Seuls sont autorisés les caractères spéciaux suivants
OWNER_(USERNAME): propriétaire (nom d'utilisateur)
PARAGRAPH: Paragraphe
PASSWORD: Mot de passe
PLEASE_CONFIRM: Confirmer
PLEASE_CORRECT_THE_ERRORS_ABOVE: Veuillez corriger les erreurs ci-dessus
PLUGINS: Plugins
PLUGIN_STORE: Dépôt des plugins
POWER_OFF: Éteindre
PROFILE_IMAGE: Image du Profil
PUBLISH: Publier
QUOTE: Citation
QUOTES: Citations
RAW: Texte brut
RAW_CONTENT_EDITOR: Editeur de contenu brut
RAW_MARKDOWN_EDITOR: Editeur Markdown brut
RAW_MODE: Mode texte brut
RAW_USERDATA_(READONLY_FOR_ADMINS): Données utilisateur brutes (lecture seule pour les administrateurs)
READONLY: Lecture seule
REGISTERED_USERS_ONLY: réservé aux utilisateurs enregistrés
REMEMBER_TO_BOOKMARK_THIS_PAGE: Pensez à marquer cette page
REQUIRED: Requis
RIGHT: Droit
ROLE: Rôle
RUSSIAN: Russe
SAVE: Enregistrer
SAVED_SUCCESSFULLY: Enregistré avec succès
SAVE_ALL_SETTINGS: Sauvegarder tous les paramètres
SAVE_THEME: Enregistrer et activer ce thème
SELECT_FROM_MEDIALIB: Sélectionner dans la bibliothèque de médias
SETTINGS: Paramètres
SETTINGS_ARE_STORED: Les paramètres sont mis en mémoire
SETUP: Configuration
SHOW_ANCHORS_NEXT_TO_HEADLINES: Afficher les ancres à coté des titres
STANDARD_EDITOR_MODE: Mode éditeur standard
START: Démarrer
SYSTEM: Système
TABLE: Tableau
TABLE_OF_CONTENTS: Sommaire
TAKEN_FROM_YOUR_USER_ACCOUNT_IF_SET_: Si non renseigné ici, le nom est tiré du compte utilisateur courant.
TERM: Terme
TEXT_FILE: Fichier texte
THEMES: Thèmes
THEME_STORE: Dépôt des thèmes
THE_FORMAT_BUTTONS: Boutons de mise en forme
TITLE: Titre
TOC: Sommaire
TOP: En haut
TYPEMILL_DESCRIPTION: Le thème standard pour Typemill. Responsive, minimal et sans aucune dépendance. Il emploie les polices système Calibri et Helvetica. Pas de javascript utilisé.
ULIST: Liste simple
UNKNOWN: Inconnu
UPDATE_USER: Mettre à jour l'utilisateur
UPLOAD: Télécharger
UPLOAD_AN_IMAGE: Télécharger une image
UPLOAD_FILE: Télécharger un fichier
UPS__WRONG_PASSWORD_OR_USERNAME__PLEASE_TRY_AGAIN_: Oups, mot de passe ou nom d'utilisateur incorrect, veuillez réessayer.
USED_AS_FALLBACK_WHEN_NO_MANUAL_DATE_IS_SET_: Utilisée si aucune date manuelle n'est définie.
USER: Utilisateur
USERNAME: Nom de l'utilisateur
USERNAME_(READ_ONLY): Nom de l'utilisateur (lecture seule)
USERS: Utilisateurs
USE_2_TO_20_CHARACTERS: Vous devez saisir au moins 2 caractères et au maximum 20.
USE_2_TO_40_CHARACTERS: Vous devez saisir au moins 2 caractères et au maximum 40.
USE_A_VALID_LANGUAGE_ATTRIBUTE: Veuillez indiquer un attribut de langue valide
USE_A_VALID_YEAR: Entrez une année valide
VIDEO: Vidéo
VIEW_SITE: Voir le site
VISUAL: Visuel
VISUAL_CONTENT_EDITOR: Editeur visuel de contenu
VISUAL_EDITOR: Editeur visuel
VISUAL_MARKDOWN_EDITOR: Editeur visuel Markdown
VISUAL_MODE: Mode visuel
WAIT: Attendez
WEB: Site
WEBSITE_TITLE: Titre du site
WEBSITE_VISIBLE_FOR: Site visible pour
WRITING: Rédaction
YEAR: Année
YOU_CAN_OVERWRITE_THE_THEME_CSS_WITH_YOUR_OWN_CSS_HERE_: Vous pouvez surcharger ici le css du thème avec vos propres styles.
ADD_NEW_FEATURES_TO_YOUR_WEBSITE_WITH_PLUGINS_AND_CONFIGURE_THEM_: Ajoutez de nouvelles fonctionnalités à votre site grâce aux plugins et configurez-les.
BY_THE: par la
CHOOSE_A_THEME_FOR_YOUR_WEBSITE_AND_CONFIGURE_THE_THEME_DETAILS_: Choisissez un thème pour votre site et configurez les details du thème.
CODED_WITH: Codé avec
COMMUNITY: Communauté
CONFIGURE_YOUR_WEBSITE: Configurez votre site
DOCS: documentation
GET_HELP: Obtenir de l'aide
GIVE_YOUR_NEW_WEBSITE_A_NAME__ADD_THE_AUTHOR_AND_CHOOSE_A_COPYRIGHT_: Donnez un nom à votre nouveau site, ajouter un auteur et choisissez un copyright.
HURRA: Hourrah
IF_YOU_HAVE_ANY_QUESTIONS__PLEASE_READ_THE: Pour toutes questions, veuillez consulter la
NEXT_STEP: Prochaine étape
OR_OPEN_A_NEW_ISSUE_ON: ou ouvrir un nouveau bug sur
SETUP_WELCOME: Bienvenue dans l'installation
TRENDSCHAU_DIGITAL: Trendschau Digital
VISIT_THE_AUTHOR_PANEL_AND_SETUP_YOUR_NEW_WEBSITE__YOU_CAN_CONFIGURE_THE_SYSTEM__CHOOSE_THEMES_AND_ADD_PLUGINS_: Visitez le volet Auteur et configurez votre nouveau site. Vous pouvez paramétrer le système, choisir un thème et ajouter des plugins.
YOUR_ACCOUNT_HAS_BEEN_CREATED_AND_YOU_ARE_LOGGED_IN_NOW_: Votre compte a été créé et vous êtes maintenant connecté
ACCESS_CONTROL: Contrôle d'accès
ACTIVATE_INDIVIDUAL_RESTRICTIONS_FOR_PAGES_IN_THE_META_TAB_OF_EACH_PAGE_: Activer les restrictions par page dans l'onglet Métadonnées de chaque page.
ADD_ONE_OR_MORE_USERNAMES_SEPARATED_WITH_COMMA_: Ajouter un ou plusieurs noms séparés par une virgule.
CUT_RESTRICTED_CONTENT_AFTER_THE_FIRST_HR_ELEMENT_ON_A_PAGE_(PER_DEFAULT_CONTENT_WILL_BE_CUT_AFTER_TITLE)_: Couper le contenu restreint après le premier élément hr de la page (par défaut, le contenu sera coupé après le titre).
FOR_ACCESS_THE_USER_MUST_HAVE_THIS_MINIMUM_ROLE: Pour accéder l'utlisateur doit avoir au moins le rôle minimal.
LIMIT_THE_ACCESS_FOR_THE_WHOLE_WEBSITE_OR_FOR_EACH_PAGE_INDIVIDUALLY__IF_YOU_ACTIVATE_THE_WEBSITE_RESTRICTION_OR_THE_PAGE_RESTRICTIONS__THEN_SESSIONS_WILL_BE_USED_IN_FRONTEND_: Limitez l'accès pour la totalité du site ou pour chaque page séparément. Si vous activez la restriction globale ou par page, les sessions seront utilisées en frontend.
ONLY_THE_FOLLOWING_USERS_HAVE_ACCESS: Seuls les utilisateurs suivants ont accès
PAGE_RESTRICTIONS___ACTIVATE: Restrictions par page - Activer
PAGE_RESTRICTIONS___CUT_RESTRICTED_CONTENT: Restrictions par page - Retirer le contenu restreint
PAGE_RESTRICTIONS___NOTICE: Restrictions par page - Avis
PAGE_RESTRICTIONS___WRAP_NOTICE_INTO_A_BOX: Restrictions par page - Insérer la note dans une box
SELECT_THE_LOWEST_USERROLE__HIGHER_ROLES_WILL_HAVE_ACCESS_TOO_: Selectionner le rôle minimal. Les rôles plus élevés accèderont également.
SHOW_THE_WEBSITE_ONLY_TO_AUTHENTICATED_USERS_AND_REDIRECT_ALL_OTHER_USERS_TO_THE_LOGIN_PAGE_: Afficher le site uniquement pour les utilisateurs authentifiés et rediriger tous les autres vers la page de login.
USE_MARKDOWN: Utiliser le markdown
WEBSITE_RESTRICTION: Restriction sur le site
WRAP_THE_RESTRICTION_NOTICE_ABOVE_INTO_A_NOTICE_4_ELEMENT_(WHICH_CAN_BE_DESIGNED_AS_SPECIAL_BOX): Insérer l'avis de restriction ci-dessus dans un élément notice-4 (qui peut être conçu comme special box)
ACTIVATE_CACHE_FOR_TWIG_TEMPLATES: Activer le cache pour les templates Twig
ADD_MORE_URL_SCHEMES_FOR_EXTERNAL_LINKS_E_G__LIKE_DICT://_(COMMA_SEPARATED_LIST): "Ajouter d'autres schémas d'url pour les liens externes. ex: dict:// (liste séparée par des virgules)"
CLEAR_CACHE: Vider le cache
DELETE_ALL_CACHE_FILES: Vider tous les fichiers de cache
DEVELOPER: Développeur
DISABLE_HEADERS: Désactiver les entêtes
DISABLE_TYPEMILL_HEADERS_AND_SEND_YOUR_OWN: Désactiver les entêtes de Typemill et envoyer le vôtre
DISPLAY_APPLICATION_ERRORS: Afficher les erreurs de l'application
ERROR_REPORTING: Rapport d'erreur
IF_YOU_ADD_A_VALUE_FOR_THE_HEIGHT__THEN_THE_IMAGE_WILL_BE_CROPPED_: Si vous ajoutez une valeur pour la hauteur, l'image sera recadrée.
PROXY: Proxy
STANDARD_HEIGHT_FOR_IMAGES: Hauteur standard pour les images
STANDARD_WIDTH_FOR_IMAGES: Largeur standard pour les images
THE_FOLLOWING_OPTIONS_ARE_ONLY_FOR_DEVELOPERS: Les options suivantes sont destinés uniquement aux développeurs et administrateurs expérimentés. Ne les modifiez que si vous les comprenez vraiment. Par exemple, n'activez jamais un rapport d'erreur sur un site en production. Ne l'utilisez que pour une résolution de bug.
THIS_APPLIES_ONLY_FOR_FUTURE_IMAGES_IN_THE_CONTENT_AREA_: Ceci s'applique seulement aux futures images qui seront stockées dans la zone de contenu.
TRUSTED_IPS_FOR_PROXY_(COMMA_SEPARATED): IPs de confiance pour le proxy (séparateur virgule)
TWIG_CACHE: Cache Twig
USE_X_FORWARDED_HEADERS: Utiliser des entêtes X-Forwarded

View File

@@ -0,0 +1,246 @@
# Italian (Italiano) pls ignore autoupdates
# Author: Severo Iuliano (https://github.com/iusvar)
ACCOUNT: Utenza
ACTIVE: Attivo
ACTUAL_PASSWORD: Parola d'ordine corrente
ADD: aggiungi
ADD_CONTENT_BLOCK: aggiungi blocco contenuto
ADD_DEFINITION: aggiungi definizione
ADD_FILE: aggiungi file
ADD_FOLDER: aggiungi cartella
ADD_FOLDER_TO_BASE_LEVEL: aggiungi cartella al livello base
ADD_ITEM: aggiungi articolo
ADD_LEFT_COLUMN: aggiungi colonna a sinistra
ADD_RIGHT_COLUMN: aggiungi colonna a destra
ADD_ROW_ABOVE: aggiungi la riga sopra
ADD_ROW_BELOW: aggiungi la riga sotto
ADMINISTRATOR: administrator
ALL: All visitors
ALL_USERS: Tutti gli utenti
ALTERNATIVE_TEXT_FOR_THE_HERO_IMAGE: Testo alternativo per l'immagine hero
ALT_TEXT: Testo alternativo
AUTHOR: Autore
AUTHOR_DESCRIPTION_(MARKDOWN): Author-Description (Markdown)
BACK_TO_STARTPAGE: torna alla pagina iniziale
BOLD: grassetto
BOTTOM: Sotto
BROWSE: Sfoglia
BULLET_LIST: Elenco puntato
BY: di
CANCEL: Annulla
CAN_BE_USED_FOR_AUTHOR_LINE_IN_FRONTEND_: Can be used for author line in frontend.
CAPTION: Didascalia
CELL: cella
CENTER: Centro
CHECK: controllo
CHOOSE_FILE: Scegli il file
CLASS: Classe
CLOSE_LIBRARY: Chiudi libreria
CODE: Codice
COG: ingranaggio
CONTENT: Contenuto
COPYRIGHT: Diritti d'autore
CREATED_AT_(READONLY): Created at (read only)
CREATED_AT_(READ_ONLY): Created at (readonly)
CREATE_NEW_USER: Crea nuovo utente
CREATE_USER: Crea utente
CROSS: croce
CUSTOM_CSS: CSS personalizzato
DEFINITION: Elenco delle definizioni
DEFINITION_LIST: Elenco delle definizioni
DELETE: elimina
DELETE_CLOSE: elimina/chiudi
DELETE_COLUMN: elimina colonna
DELETE_CONTENT_BLOCK: elimina blocco contenuto
DELETE_PAGE: Elimina pagina
DELETE_ROW: elimina riga
DELETE_USER: Elimina utente
DESCRIPTION: descrizione
DISCARD: Scarta
DISCARD_CHANGES: Non salvare le modifiche
DO_YOU_REALLY_WANT_TO_DELETE_THE_USER: Vuoi veramente eliminare l'utente
DO_YOU_REALLY_WANT_TO_DELETE_THIS_PAGE: Vuoi veramente cancellare questa pagina?
DO_YOU_WANT_TO_DISCARD_YOUR_CHANGES_AND_SET_THE_CONTENT_BACK_TO_THE_LIVE_VERSION: Vuoi annullare le modifiche e ripristinare i contenuti alla versione precedente?
DRAFT: Bozza
DRAG_A_PICTURE_OR_CLICK_TO_SELECT: trascina un'immagine o fai clic per selezionare
DUTCH__FLEMISH: Olandese, Fiammingo
EDIT: modifica
EDITOR: editor
EDIT_USER: Modifica utente
ENGLISH: Inglese
EXTERNAL_LINK: collegamento esterno
E_G_: per es.:
E_MAIL: Posta elettronica
FAVICON: Favicon
FILE: Archivio
FILES: Archivi
FIRST_NAME: Nome
FOLDER: cartella
FORGOT_PASSWORD: Parola d'ordine dimenticata
FORMAT: Format
FRENCH: French
GENERAL_PRESENTATION: Presentazione generale
GERMAN: Tedesco
GOOGLE_SITEMAP: Sitemap di Google
HAS_EDIT_RIGHTS_FOR_THIS_ARTICLE_: Has edit rights for this article.
HEAD: Intestazione
HEADLINE: Titolo
HEADLINE_ANCHORS: Ancoraggi del titolo
HERO_IMAGE: Immagine Hero
HIDE: Nascondi
HIDE_PAGE_FROM_NAVIGATION: Nasconde la pagina dalla navigazione
HOME: home
HOMEPAGE: Homepage
HORIZONTAL_LINE: Linea orizzontale
HR: Linea orizzontale
IF_NOT_FILLED__THE_DESCRIPTION_IS_EXTRACTED_FROM_CONTENT_: Se non compilato, la descrizione viene estratta dal contenuto.
IMAGE: Immagine
IMAGES: Immagini
IMAGE_URL: Image URL
IMAGE_URL_(READ_ONLY): Image URL (read only)
ITALIAN: Italiano
ITALIC: corsivo
LANGUAGE: Lingua
LANGUAGE_ADMIN: Lingua (admin-ui)
LANGUAGE_ATTR: Attributo Lingua (sito)
LAST_MODIFIED_LIVE_(READONLY): Last modified live (readonly)
LAST_NAME: Cognome
LEFT: Sinistra
LICENCE: Licenza
LINK: Collegamento
LINK_TO_VIDEO: Collega al video
LOGIN: Accesso
LOGO: Logo
LOGOUT: Esci
MANUAL_DATE: Data manuale
MARKDOWN: Markdown
MAXIMUM_SIZE_FOR_AN_IMAGE_IS_5_MB__HERO_IMAGES_ARE_NOT_SUPPORTED_BY_ALL_THEMES_: La dimensione massima per un'immagine è di 5 MB. Le immagini hero non sono supportate da tutti i temi.
MEMBER: member
MENU: Menu
META: Metadati
META_DESCRIPTION: Descrizione
META_TITLE: Titolo
MISSING_REQUIREMENTS: Requisiti mancanti
MOVE_VERTICAL: spostare in verticale
NAVIGATION_TITLE: Titolo di navigazione
NEW_PASSWORD: Nuova parola d'ordine
NONE: Nessuna
NOTICE: Avviso
NOT_EDITABLE: non modificabile
NO_DESCRIPTION: Nessuna descrizione
NO_PREVIEW: Nessuna anteprima
NO_SETTINGS: Nessuna impostazione
NUMBERED_LIST: Elenco numerato
OLIST: Elenchi ordinati
ONLINE: in linea
ONLY_THE_FOLLOWING_SPECIAL_CHARACTERS_ARE_ALLOWED: Sono ammessi solo i seguenti caratteri speciali:
OWNER_(USERNAME): owner (username)
PARAGRAPH: Paragrafo
PASSWORD: Parola d'ordine
PLEASE_CONFIRM: Per favore conferma
PLEASE_CORRECT_THE_ERRORS_ABOVE: Si prega di correggere gli errori sopra
PLUGINS: Plugin
PLUGIN_STORE: Deposito plugin
POWER_OFF: spegni
PROFILE_IMAGE: Profile image
PUBLISH: Pubblica
QUOTE: Citazione
QUOTES: Citazione
RAW: grezzo
RAW_CONTENT_EDITOR: Editore contenuto grezzo
RAW_MARKDOWN_EDITOR: Editore grezzo Markdown
RAW_MODE: modo grezzo
READONLY: Sola lettura
REGISTERED_USERS_ONLY: Registered users only
REMEMBER_TO_BOOKMARK_THIS_PAGE: Ricorda di aggiungere questa pagina ai segnalibri
REQUIRED: Richiesto
RIGHT: Destra
ROLE: Ruolo
RUSSIAN: Russo
SAVE: Salva
SAVED_SUCCESSFULLY: Salvato con successo
SAVE_ALL_SETTINGS: Salva tutte le impostazioni
SAVE_THEME: Salva tema
SELECT_FROM_MEDIALIB: seleziona da medialib
SETTINGS: Impostazioni
SETTINGS_ARE_STORED: Le impostazioni sono memorizzate
SETUP: Configurazione
SHOW_ANCHORS_NEXT_TO_HEADLINES: Mostra le ancore accanto ai titoli
STANDARD_EDITOR_MODE: Modalità editore standard
START: Comincia
SYSTEM: Sistema
TABLE: Tabella
TABLE_OF_CONTENTS: Sommario
TAKEN_FROM_YOUR_USER_ACCOUNT_IF_SET_: Tratto dalla tua utenza, se impostata.
TERM: termine
TEXT_FILE: file di testo
THEMES: Temi
THEME_STORE: Deposito temi
THE_FORMAT_BUTTONS: pulsanti di formattazione
TITLE: Titolo
TOC: Sommario
TOP: Sopra
TYPEMILL_DESCRIPTION: Il tema standard per Typemill. Reattivo, minimo e senza dipendenze. Utilizza i caratteri di sistema Calibri ed Helvetica. Non viene utilizzato JavaScript.
ULIST: Elenchi non ordinati
UNKNOWN: Ignoto
UPDATE_USER: Aggiorna utente
UPLOAD: carica
UPLOAD_AN_IMAGE: carica un'immagine
UPLOAD_FILE: Carica un file
USED_AS_FALLBACK_WHEN_NO_MANUAL_DATE_IS_SET_: Utilizzato come ripiego quando non è impostata alcuna data manuale.
USER: Utente
USERNAME: Nome utente
USERNAME_(READ_ONLY): Username (read only)
USERS: Utenti
USE_2_TO_20_CHARACTERS: Usa da 2 a 20 caratteri.
USE_2_TO_40_CHARACTERS: Usa da 2 a 40 caratteri.
USE_A_VALID_LANGUAGE_ATTRIBUTE: Use a valid language attribute
USE_A_VALID_YEAR: Usa un anno valido
VIDEO: Video
VIEW_SITE: Mostra sito
VISUAL: visivo
VISUAL_CONTENT_EDITOR: Editore contenuto visivo
VISUAL_EDITOR: Editore visivo
VISUAL_MARKDOWN_EDITOR: Editore visivo Markdown
VISUAL_MODE: modo visivo
WAIT: aspetta
WEB: Sito
WEBSITE_TITLE: Titolo del sito
WEBSITE_VISIBLE_FOR: Website visible for
WRITING: Scrittura
YEAR: Anno
YOU_CAN_OVERWRITE_THE_THEME_CSS_WITH_YOUR_OWN_CSS_HERE_: È possibile sovrascrivere il css del tema con il proprio CSS qui.
ADD_NEW_FEATURES_TO_YOUR_WEBSITE_WITH_PLUGINS_AND_CONFIGURE_THEM_: Aggiungi nuove funzionalità al tuo sito mediante plugin e configurali.
BY_THE: dalla
CHOOSE_A_THEME_FOR_YOUR_WEBSITE_AND_CONFIGURE_THE_THEME_DETAILS_: Scegli un tema per il tuo sito e configura i dettagli del tema.
CODED_WITH: Codificato con
COMMUNITY: comunità
CONFIGURE_YOUR_WEBSITE: Configura il tuo sito
DOCS: documentazione
GET_HELP: Chiedi aiuto
GIVE_YOUR_NEW_WEBSITE_A_NAME__ADD_THE_AUTHOR_AND_CHOOSE_A_COPYRIGHT_: Dai un nome al tuo nuovo sito, aggiungi l'autore e scegli un diritto d'autore.
HURRA: Evviva
IF_YOU_HAVE_ANY_QUESTIONS__PLEASE_READ_THE: In caso di domande, si prega di leggere la
NEXT_STEP: Prossimo passo
OR_OPEN_A_NEW_ISSUE_ON: o aprire un nuovo problema su
SETUP_WELCOME: Benvenuto all'installazione
TRENDSCHAU_DIGITAL: Trendschau Digital
VISIT_THE_AUTHOR_PANEL_AND_SETUP_YOUR_NEW_WEBSITE__YOU_CAN_CONFIGURE_THE_SYSTEM__CHOOSE_THEMES_AND_ADD_PLUGINS_: Visita il pannello autore e configura il tuo nuovo sito. Puoi configurare il sistema, scegliere i temi e aggiungere plugin.
YOUR_ACCOUNT_HAS_BEEN_CREATED_AND_YOU_ARE_LOGGED_IN_NOW_: Il tuo account è stato creato e ora sei connesso.
ACTIVATE_CACHE_FOR_TWIG_TEMPLATES: Attiva la cache per i modelli Twig
CLEAR_CACHE: Cancella cache
DELETE_ALL_CACHE_FILES: Elimina tutti i file della cache
DEVELOPER: Sviluppatore
DISPLAY_APPLICATION_ERRORS: Visualizza errori dell'applicazione
ERROR_REPORTING: Segnalazione errori
IF_YOU_ADD_A_VALUE_FOR_THE_HEIGHT__THEN_THE_IMAGE_WILL_BE_CROPPED_: Se aggiungi un valore per l'altezza, l'immagine verrà ritagliata.
PROXY: Proxy
STANDARD_HEIGHT_FOR_IMAGES: Altezza standard per le immagini
STANDARD_WIDTH_FOR_IMAGES: Larghezza standard per le immagini
THE_FOLLOWING_OPTIONS_ARE_ONLY_FOR_DEVELOPERS: "Le seguenti opzioni sono solo per sviluppatori e amministratori esperti. Cambia le opzioni solo se le capisci davvero. Ad esempio: non attivare mai la segnalazione degli errori per un sito Web live, utilizzare questa opzione solo per la correzione dei bug."
THIS_APPLIES_ONLY_FOR_FUTURE_IMAGES_IN_THE_CONTENT_AREA_: Questo vale solo per le immagini future nell'area del contenuto.
TRUSTED_IPS_FOR_PROXY_(COMMA_SEPARATED): IP affidabili per proxy (separati da virgole)
TWIG_CACHE: Twig Cache
USE_X_FORWARDED_HEADERS: Usa intestazioni con X-Forwarded

View File

@@ -0,0 +1,268 @@
# Dutch (Nederlands)
# Author:
ACCOUNT: Account
ACTIVE: Actief
ACTUAL_PASSWORD: Actueel wachtwoord
ADD: toevoegen
ADD_CONTENT_BLOCK: inhoudblok toevoegen
ADD_DEFINITION: definitie toevoegen
ADD_FILE: Bestand toevoegen
ADD_FOLDER: Map toevoegen
ADD_FOLDER_TO_BASE_LEVEL: map toevoegen op basisniveau
ADD_ITEM: Item toevoegen
ADD_LEFT_COLUMN: linkerkolom toevoegen
ADD_RIGHT_COLUMN: rechter kolom toevoegen
ADD_ROW_ABOVE: rij boven toevoegen
ADD_ROW_BELOW: rij hieronder toevoegen
ADMINISTRATOR: administrator
ALL: All visitors
ALL_USERS: Alle gebruikers
ALTERNATIVE_TEXT_FOR_THE_HERO_IMAGE: Alternative Text for the hero image
ALT_TEXT: Alt-tekst
AUTHOR: Auteur
AUTHOR_DESCRIPTION_(MARKDOWN): Author-Description (Markdown)
BACK_TO_STARTPAGE: terug naar startpagina
BOLD: vetgedrukt
BOTTOM: bodem
BROWSE: BROWSE
BULLET_LIST: Lijst met opsommingstekens
BY: door
CANCEL: annuleren
CAN_BE_USED_FOR_AUTHOR_LINE_IN_FRONTEND_: Can be used for author line in frontend.
CAPTION: Bijschrift
CELL: cel
CENTER: Midden
CHECK: check
CHOOSE_FILE: Choose file
CLASS: Class
CLOSE_LIBRARY: Close Library
CODE: Code
COG: tandwiel
CONTENT: Inhoud
COPYRIGHT: Auteursrecht
CREATED_AT_(READONLY): Created at (read only)
CREATED_AT_(READ_ONLY): Created at (readonly)
CREATE_NEW_USER: Nieuwe gebruiker maken
CREATE_USER: Gebruiker maken
CROSS: kruis
CUSTOM_CSS: Custom CSS
DEFINITION: Definitielijst
DEFINITION_LIST: Definitielijst
DELETE: verwijderen
DELETE_CLOSE: verwijderen / sluiten
DELETE_COLUMN: kolom verwijderen
DELETE_CONTENT_BLOCK: content-block verwijderen
DELETE_PAGE: pagina verwijderen
DELETE_ROW: rij verwijderen
DELETE_USER: Gebruiker verwijderen
DESCRIPTION: omschrijving
DISCARD: weggooien
DISCARD_CHANGES: Wijzigingen negeren
DO_YOU_REALLY_WANT_TO_DELETE_THE_USER: Wilt u de gebruiker echt verwijderen
DO_YOU_REALLY_WANT_TO_DELETE_THIS_PAGE: Wilt u deze pagina echt verwijderen?
DO_YOU_WANT_TO_DISCARD_YOUR_CHANGES_AND_SET_THE_CONTENT_BACK_TO_THE_LIVE_VERSION: Wilt u uw wijzigingen annuleren en de inhoud terugzetten naar de live versie?
DRAFT: Ontwerp
DRAG_A_PICTURE_OR_CLICK_TO_SELECT: sleep een foto of klik om te selecteren
DUTCH__FLEMISH: Dutch, Flemish
EDIT: bewerken
EDITOR: editor
EDIT_USER: Gebruiker bewerken
ENGLISH: English
EXTERNAL_LINK: externe link
E_G_: bijv.
E_MAIL: e-mail
FAVICON: Favicon
FILE: File
FILES: Files
FIRST_NAME: Voornaam
FOLDER: map
FORGOT_PASSWORD: Wachtwoord vergeten
FORMAT: Formaat
FRENCH: French
GENERAL_PRESENTATION: General Presentation
GERMAN: German
GOOGLE_SITEMAP: Google Sitemap
HAS_EDIT_RIGHTS_FOR_THIS_ARTICLE_: Has edit rights for this article.
HEAD: Kop
HEADLINE: Kop
HEADLINE_ANCHORS: Headline Anchors
HERO_IMAGE: Hero Image
HIDE: Hide
HIDE_PAGE_FROM_NAVIGATION: Hide page from navigation
HOME: home
HOMEPAGE: Homepage
HORIZONTAL_LINE: Horizontale lijn
HR: hr
IF_NOT_FILLED__THE_DESCRIPTION_IS_EXTRACTED_FROM_CONTENT_: Indien niet ingevuld, wordt de beschrijving uit de inhoud gehaald.
IMAGE: Afbeelding
IMAGES: Images
IMAGE_URL: Image URL
IMAGE_URL_(READ_ONLY): Image URL (read only)
ITALIAN: Italian
ITALIC: cursief
LANGUAGE: Taal
LANGUAGE_ADMIN: Taal (admin-ui)
LANGUAGE_ATTR: Taal Attribute (website)
LAST_MODIFIED_LIVE_(READONLY): Last modified live (readonly)
LAST_NAME: achternaam
LEFT: Links
LICENCE: Licentie
LINK: Link
LINK_TO_VIDEO: Link naar video
LOGIN: Inloggen
LOGO: Logo
LOGOUT: Uitloggen
MANUAL_DATE: Handmatige datum
MARKDOWN: Markdown
MAXIMUM_SIZE_FOR_AN_IMAGE_IS_5_MB__HERO_IMAGES_ARE_NOT_SUPPORTED_BY_ALL_THEMES_: Maximum size for an image is 5 MB. Hero images are not supported by all themes.
MEMBER: Lid
MENU: Menu
META: Meta
META_DESCRIPTION: Metabeschrijving
META_TITLE: Metatitel
MISSING_REQUIREMENTS: ontbrekende vereisten
MOVE_VERTICAL: verplaats verticaal
NAVIGATION_TITLE: Navigation Title
NEW_PASSWORD: Nieuw wachtwoord
NONE: Geen
NOTICE: Opmerking
NOT_EDITABLE: niet bewerkbaar
NO_DESCRIPTION: Geen beschrijving
NO_PREVIEW: Geen voorbeeld
NO_SETTINGS: Geen instellingen
NUMBERED_LIST: Genummerde lijst
OLIST: olist
ONLINE: online
ONLY_THE_FOLLOWING_SPECIAL_CHARACTERS_ARE_ALLOWED: alleen de volgende speciale tekens zijn toegestaan:
OWNER_(USERNAME): owner (username)
PARAGRAPH: Paragraaf
PASSWORD: Wachtwoord
PLEASE_CONFIRM: bevestig alstublieft
PLEASE_CORRECT_THE_ERRORS_ABOVE: Corrigeer bovenstaande fouten
PLUGINS: plug-ins
PLUGIN_STORE: Plugin Store
POWER_OFF: power-off
PROFILE_IMAGE: Profiel afbeelding
PUBLISH: Publiceren
QUOTE: Citeer
QUOTES: Citeren
RAW: rauw
RAW_CONTENT_EDITOR: Ruwe Content Editor
RAW_MARKDOWN_EDITOR: Ruwe Markdown Editor
RAW_MODE: onbewerkte modus
RAW_USERDATA_(READONLY_FOR_ADMINS): Ruwe Gebruikersdata (alleen lezen voor admins)
READONLY: Alleen lezen
REGISTERED_USERS_ONLY: Alleen geregistreerde gebruikers
REMEMBER_TO_BOOKMARK_THIS_PAGE: vergeet deze pagina niet te bookmarken
REQUIRED: verplicht
RIGHT: Rechts
ROLE: rol
RUSSIAN: Russisch
SAVE: Opslaan
SAVED_SUCCESSFULLY: Succesvol opgeslagen
SAVE_ALL_SETTINGS: Sla alle instellingen op
SAVE_THEME: Thema opslaan
SELECT_FROM_MEDIALIB: Selecteer uit medialib
SETTINGS: Instellingen
SETTINGS_ARE_STORED: Instellingen zijn opgeslagen
SETUP: Instellen
SHOW_ANCHORS_NEXT_TO_HEADLINES: Ankers naast koppen weergeven
STANDARD_EDITOR_MODE: Standaardeditormodus
START: Start
SYSTEM: Systeem
TABLE: Tabel
TABLE_OF_CONTENTS: Inhoudsopgave
TAKEN_FROM_YOUR_USER_ACCOUNT_IF_SET_: Genomen uit uw gebruikersaccount indien ingesteld.
TERM: term
TEXT_FILE: tekstbestand
THEMES: Themes
THEME_STORE: Theme Store
THE_FORMAT_BUTTONS: De opmaakknoppen
TITLE: Titel
TOC: Toc
TOP: Top
TYPEMILL_DESCRIPTION: Het standaardthema voor typemill. Responsief, minimaal en zonder afhankelijkheden. Het gebruikt de systeemlettertypen Calibri en Helvetica. Er is geen JavaScript gebruikt.
ULIST: ulist
UNKNOWN: onbekend
UPDATE_USER: Gebruiker bijwerken
UPLOAD: Upload
UPLOAD_AN_IMAGE: Upload een afbeelding
UPLOAD_FILE: Upload een bestand
UPS__WRONG_PASSWORD_OR_USERNAME__PLEASE_TRY_AGAIN_: Oops, verkeerd wachtwoord of gebruikersnaam, probeer het opnieuw.
USED_AS_FALLBACK_WHEN_NO_MANUAL_DATE_IS_SET_: Gebruikt als fallback als er geen handmatige datum is ingesteld.
USER: Gebruiker
USERNAME: Gebruikersnaam
USERNAME_(READ_ONLY): Gebruikersnaam (alleen lezen)
USERS: Gebruikers
USE_2_TO_20_CHARACTERS: Gebruik 2 tot 20 tekens.
USE_2_TO_40_CHARACTERS: Gebruik 2 tot 40 tekens.
USE_A_VALID_LANGUAGE_ATTRIBUTE: Gebruik een geldig taalkenmerk
USE_A_VALID_YEAR: Gebruik een geldig jaar
VIDEO: Video
VIEW_SITE: Site bekijken
VISUAL: visual
VISUAL_CONTENT_EDITOR: Visual Content Editor
VISUAL_EDITOR: visuele editor
VISUAL_MARKDOWN_EDITOR: Visual Markdown Editor
VISUAL_MODE: visuele modus
WAIT: wacht
WEB: Web
WEBSITE_TITLE: Titel van de website
WEBSITE_VISIBLE_FOR: Website visible for
WRITING: Schrijven
YEAR: Jaar
YOU_CAN_OVERWRITE_THE_THEME_CSS_WITH_YOUR_OWN_CSS_HERE_: Je kunt de thema-css hier overschrijven met je eigen css.
ADD_NEW_FEATURES_TO_YOUR_WEBSITE_WITH_PLUGINS_AND_CONFIGURE_THEM_: Voeg nieuwe functies toe aan uw website met plug-ins en configureer ze.
BY_THE: door de
CHOOSE_A_THEME_FOR_YOUR_WEBSITE_AND_CONFIGURE_THE_THEME_DETAILS_: Kies een thema voor uw website en configureer de themadetails.
CODED_WITH: Geprogrammeerd met
COMMUNITY: gemeenschap
CONFIGURE_YOUR_WEBSITE: Configureer uw website
DOCS: Documentatie
GET_HELP: Krijg hulp
GIVE_YOUR_NEW_WEBSITE_A_NAME__ADD_THE_AUTHOR_AND_CHOOSE_A_COPYRIGHT_: Geef je nieuwe website een naam, voeg de auteur toe en kies een copyright.
HURRA: Hoera
IF_YOU_HAVE_ANY_QUESTIONS__PLEASE_READ_THE: Als je vragen hebt, lees dan de
NEXT_STEP: Volgende stap
OR_OPEN_A_NEW_ISSUE_ON: of open een nieuw issue op
SETUP_WELCOME: Installatie Welkom
TRENDSCHAU_DIGITAL: Trendschau Digital
VISIT_THE_AUTHOR_PANEL_AND_SETUP_YOUR_NEW_WEBSITE__YOU_CAN_CONFIGURE_THE_SYSTEM__CHOOSE_THEMES_AND_ADD_PLUGINS_: Ga naar het auteurspaneel en stel uw nieuwe website in. U kunt het systeem configureren, thema's kiezen en plug-ins toevoegen.
YOUR_ACCOUNT_HAS_BEEN_CREATED_AND_YOU_ARE_LOGGED_IN_NOW_: Uw account is aangemaakt en u bent nu ingelogd.
ACCESS_CONTROL: Toegangscontrole
ACTIVATE_INDIVIDUAL_RESTRICTIONS_FOR_PAGES_IN_THE_META_TAB_OF_EACH_PAGE_: Activeer individuele beperkingen voor pagina's in de meta-tab van elke pagina.
ADD_ONE_OR_MORE_USERNAMES_SEPARATED_WITH_COMMA_: Voeg een of meer gebruikersnamen toe, gescheiden door komma's.
CUT_RESTRICTED_CONTENT_AFTER_THE_FIRST_HR_ELEMENT_ON_A_PAGE_(PER_DEFAULT_CONTENT_WILL_BE_CUT_AFTER_TITLE)_: Beperkte inhoud knippen na het eerste hr-element op een pagina (standaard wordt inhoud na titel geknipt).
FOR_ACCESS_THE_USER_MUST_HAVE_THIS_MINIMUM_ROLE: Voor toegang moet de gebruiker deze minimale rol hebben
LIMIT_THE_ACCESS_FOR_THE_WHOLE_WEBSITE_OR_FOR_EACH_PAGE_INDIVIDUALLY__IF_YOU_ACTIVATE_THE_WEBSITE_RESTRICTION_OR_THE_PAGE_RESTRICTIONS__THEN_SESSIONS_WILL_BE_USED_IN_FRONTEND_: Beperk de toegang voor de hele website of voor elke pagina afzonderlijk. Als u de websitebeperking of de paginabeperkingen activeert, worden sessies in de frontend gebruikt.
ONLY_THE_FOLLOWING_USERS_HAVE_ACCESS: Alleen de volgende gebruikers hebben toegang
PAGE_RESTRICTIONS___ACTIVATE: Paginabeperkingen - Activeren
PAGE_RESTRICTIONS___CUT_RESTRICTED_CONTENT: Paginabeperkingen - Beperkte inhoud knippen
PAGE_RESTRICTIONS___NOTICE: Paginabeperkingen - Kennisgeving
PAGE_RESTRICTIONS___WRAP_NOTICE_INTO_A_BOX: Paginabeperkingen - Verpak kennisgeving in een doos
SELECT_THE_LOWEST_USERROLE__HIGHER_ROLES_WILL_HAVE_ACCESS_TOO_: Selecteer de laagste gebruikersrol. Hogere rollen hebben ook toegang.
SHOW_THE_WEBSITE_ONLY_TO_AUTHENTICATED_USERS_AND_REDIRECT_ALL_OTHER_USERS_TO_THE_LOGIN_PAGE_: Toon de website alleen aan geverifieerde gebruikers en stuur alle andere gebruikers door naar de inlogpagina.
USE_MARKDOWN: gebruik markdown
WEBSITE_RESTRICTION: Websitebeperking
WRAP_THE_RESTRICTION_NOTICE_ABOVE_INTO_A_NOTICE_4_ELEMENT_(WHICH_CAN_BE_DESIGNED_AS_SPECIAL_BOX): Wikkel de beperkingskennisgeving hierboven in een bericht-4-element (dat kan worden ontworpen als een speciaal vak)
ACTIVATE_CACHE_FOR_TWIG_TEMPLATES: Cache activeren voor Twig-sjablonen
ADD_MORE_URL_SCHEMES_FOR_EXTERNAL_LINKS_E_G__LIKE_DICT://_(COMMA_SEPARATED_LIST): Voeg meer url-schema's toe voor externe links, b.v. zoals dict:// (door komma's gescheiden lijst)
CLEAR_CACHE: Cache wissen
DELETE_ALL_CACHE_FILES: Alle cachebestanden verwijderen
DEVELOPER: Ontwikkelaar
DISABLE_HEADERS: Headers uitschakelen
DISABLE_TYPEMILL_HEADERS_AND_SEND_YOUR_OWN: Schakel Typemill-headers uit en verzend uw eigen
DISPLAY_APPLICATION_ERRORS: Applicatiefouten weergeven
ERROR_REPORTING: Foutmelding
IF_YOU_ADD_A_VALUE_FOR_THE_HEIGHT__THEN_THE_IMAGE_WILL_BE_CROPPED_: Als u een waarde voor de hoogte toevoegt, wordt de afbeelding bijgesneden.
PROXY: Proxy
STANDARD_HEIGHT_FOR_IMAGES: Standaard hoogte voor afbeeldingen
STANDARD_WIDTH_FOR_IMAGES: Standaardbreedte voor afbeeldingen
THE_FOLLOWING_OPTIONS_ARE_ONLY_FOR_DEVELOPERS: De volgende opties zijn alleen voor ontwikkelaars en ervaren beheerders. Wijzig de opties alleen als u ze echt begrijpt. Bijvoorbeeld: Activeer nooit de foutrapportage voor een live website, gebruik deze optie alleen voor het oplossen van fouten.
THIS_APPLIES_ONLY_FOR_FUTURE_IMAGES_IN_THE_CONTENT_AREA_: Dit geldt alleen voor toekomstige afbeeldingen in het inhoudsgebied.
TRUSTED_IPS_FOR_PROXY_(COMMA_SEPARATED): Vertrouwde IP's voor proxy (gescheiden door komma's)
TWIG_CACHE: Twig Cache
USE_X_FORWARDED_HEADERS: Gebruik X-Forwarded Headers

View File

@@ -0,0 +1,268 @@
# Russian (Русский)
# Author: Paul (https://paul.bid) paulbid@protonmail.com
ACCOUNT: Аккаунт
ACTIVE: Активный
ACTUAL_PASSWORD: Текущий пароль
ADD: Добавить
ADD_CONTENT_BLOCK: Добавить блок
ADD_DEFINITION: добавить определение
ADD_FILE: добавить запись
ADD_FOLDER: добавить раздел
ADD_FOLDER_TO_BASE_LEVEL: добавить раздел на корневой уровень
ADD_ITEM: Добавить
ADD_LEFT_COLUMN: добавить колонку слева
ADD_RIGHT_COLUMN: добавить колонку справа
ADD_ROW_ABOVE: добавить строку выше
ADD_ROW_BELOW: добавить строку ниже
ADMINISTRATOR: Администратор
ALL: Всех посетителей
ALL_USERS: Все пользователи
ALTERNATIVE_TEXT_FOR_THE_HERO_IMAGE: Альтернативный текст-описание главного изображения для записи
ALT_TEXT: Альтернативный текст
AUTHOR: Автор
AUTHOR_DESCRIPTION_(MARKDOWN): Об авторе (Markdown)
BACK_TO_STARTPAGE: Назад на главную страницу
BOLD: Полужирный
BOTTOM: Внизу
BROWSE: ОБЗОР
BULLET_LIST: Маркированный список
BY: от
CANCEL: Отмена
CAN_BE_USED_FOR_AUTHOR_LINE_IN_FRONTEND_: Может использоваться в качестве описания автора на странице с записью.
CAPTION: Название
CELL: ячейка
CENTER: По центру
CHECK: проверить
CHOOSE_FILE: Выберите файл
CLASS: Класс
CLOSE_LIBRARY: Закрыть библиотеку
CODE: Код
COG: cog
CONTENT: Контент
COPYRIGHT: Копирайт
CREATED_AT_(READONLY): Создано (нельзя изменить)
CREATED_AT_(READ_ONLY): Создано (нельзя изменить)
CREATE_NEW_USER: Создание нового пользователя
CREATE_USER: Создать пользователя
CROSS: Зачёркнутый
CUSTOM_CSS: Дополнительный CSS
DEFINITION: Список определений
DEFINITION_LIST: Список определений
DELETE: Удалить
DELETE_CLOSE: Удалить/закрыть
DELETE_COLUMN: Удалить колонку
DELETE_CONTENT_BLOCK: Удалить блок
DELETE_PAGE: Удалить страницу
DELETE_ROW: Удалить строку
DELETE_USER: Удалить пользователя
DESCRIPTION: описание
DISCARD: Сбросить
DISCARD_CHANGES: Сбросить изменения
DO_YOU_REALLY_WANT_TO_DELETE_THE_USER: Вы действительно хотите удалить пользователя
DO_YOU_REALLY_WANT_TO_DELETE_THIS_PAGE: Вы действительно хотите удалить эту страницу?
DO_YOU_WANT_TO_DISCARD_YOUR_CHANGES_AND_SET_THE_CONTENT_BACK_TO_THE_LIVE_VERSION: Вы хотите сбросить изменения и вернуться к предыдущей версии?
DRAFT: Черновик
DRAG_A_PICTURE_OR_CLICK_TO_SELECT: загрузить изображение
DUTCH__FLEMISH: Dutch, Flemish (Голландский, Фламандский)
EDIT: редактировать
EDITOR: Редактор
EDIT_USER: Редактирование профиля
ENGLISH: English (Английский)
EXTERNAL_LINK: внешняя ссылка
E_G_: например
E_MAIL: Email
FAVICON: Иконка сайта (Favicon)
FILE: Файл
FILES: Файлы
FIRST_NAME: Имя
FOLDER: раздел
FORGOT_PASSWORD: Забыл пароль
FORMAT: Формат
FRENCH: French (Французский)
GENERAL_PRESENTATION: Графическая атрибутика
GERMAN: German (Немецкий)
GOOGLE_SITEMAP: Google Sitemap
HAS_EDIT_RIGHTS_FOR_THIS_ARTICLE_: Имеет права на редактирование этой записи.
HEAD: Заголовок
HEADLINE: Заголовок
HEADLINE_ANCHORS: Якорные ссылки в заголовках
HERO_IMAGE: Главное изображение для записи
HIDE: Скрыть
HIDE_PAGE_FROM_NAVIGATION: Скрыть страницу из меню навигации
HOME: главная
HOMEPAGE: Главная
HORIZONTAL_LINE: Горизонтальная линия
HR: Горизонтальная линия
IF_NOT_FILLED__THE_DESCRIPTION_IS_EXTRACTED_FROM_CONTENT_: Если не заполнено, описание генерируется из содержимого.
IMAGE: Изображение
IMAGES: Изображения
IMAGE_URL: URL изображения
IMAGE_URL_(READ_ONLY): URL изображения (нельзя изменить)
ITALIAN: Italian (Итальянский)
ITALIC: Наклонный
LANGUAGE: Язык
LANGUAGE_ADMIN: Язык (для панели администратора)
LANGUAGE_ATTR: Атрибут языка (для сайта)
LAST_MODIFIED_LIVE_(READONLY): Последнее изменение было (нельзя изменить)
LAST_NAME: Фамилия
LEFT: Слева
LICENCE: Лицензия
LINK: Ссылка
LINK_TO_VIDEO: Ссылка на видео
LOGIN: Войти
LOGO: Логотип
LOGOUT: Выйти
MANUAL_DATE: Выбор даты вручную
MARKDOWN: Markdown
MAXIMUM_SIZE_FOR_AN_IMAGE_IS_5_MB__HERO_IMAGES_ARE_NOT_SUPPORTED_BY_ALL_THEMES_: Максимальный размер изображения - 5 MB. Главные изображения для записей поддерживаются не всеми темами.
MEMBER: Зарегистрированный пользователь
MENU: Меню
META: Метаданные
META_DESCRIPTION: Мета-описание
META_TITLE: Мета-заголовок
MISSING_REQUIREMENTS: Отсутствующие требования
MOVE_VERTICAL: двигаться по вертикали
NAVIGATION_TITLE: Название страницы в меню навигации
NEW_PASSWORD: Новый пароль
NONE: Нет
NOTICE: Заметка
NOT_EDITABLE: нельзя отредактировать
NO_DESCRIPTION: Нет описания
NO_PREVIEW: Нет предпросмотра
NO_SETTINGS: Нет настроек
NUMBERED_LIST: Упорядоченный список
OLIST: Упорядоченный список
ONLINE: онлайн
ONLY_THE_FOLLOWING_SPECIAL_CHARACTERS_ARE_ALLOWED: Разрешены только следующие специальные символы -
OWNER_(USERNAME): Владелец (имя пользователя)
PARAGRAPH: Параграф
PASSWORD: Пароль
PLEASE_CONFIRM: Пожалуйста подтвердите
PLEASE_CORRECT_THE_ERRORS_ABOVE: Пожалуйста, исправьте ошибки выше
PLUGINS: Плагины
PLUGIN_STORE: Магазин Плагинов
POWER_OFF: выключить
PROFILE_IMAGE: Изображение профиля (аватар)
PUBLISH: Опубликовать
QUOTE: Цитата
QUOTES: Цитирует
RAW: исходный код
RAW_CONTENT_EDITOR: Редактор исходного кода
RAW_MARKDOWN_EDITOR: Редактор кода Markdown
RAW_MODE: Режим исходного кода
RAW_USERDATA_(READONLY_FOR_ADMINS): Пользовательские данные (только для администраторов и только просмотр)
READONLY: Нельзя изменить
REGISTERED_USERS_ONLY: Только зарегистрированных пользователей
REMEMBER_TO_BOOKMARK_THIS_PAGE: Не забудьте добавить в закладки эту страницу
REQUIRED: Обязательно
RIGHT: Справа
ROLE: Роль
RUSSIAN: Russian (Русский)
SAVE: Сохранить
SAVED_SUCCESSFULLY: Успешно сохранено
SAVE_ALL_SETTINGS: Сохранить все настройки
SAVE_THEME: Сохранить настройки Темы
SELECT_FROM_MEDIALIB: Выбрать из библиотеки
SETTINGS: Настройки
SETTINGS_ARE_STORED: Настройки сохранены
SETUP: Установка
SHOW_ANCHORS_NEXT_TO_HEADLINES: Показывать якорные ссылки рядом с заголовками
STANDARD_EDITOR_MODE: Стандартный режим редактора
START: Начать
SYSTEM: Настройки
TABLE: Таблица
TABLE_OF_CONTENTS: Оглавление
TAKEN_FROM_YOUR_USER_ACCOUNT_IF_SET_: Взято из вашего профиля, если в профиле заполнено
TERM: термин
TEXT_FILE: текстовый файл
THEMES: Темы
THEME_STORE: Магазин Тем
THE_FORMAT_BUTTONS: Кнопки для форматирования
TITLE: Название
TOC: Оглавление
TOP: Наверху
TYPEMILL_DESCRIPTION: Стандартная тема для Typemill. Отзывчивая, минималистичная и без каких-либо зависимостей. Используются системные шрифты Calibri и Helvetica. А вот JavaScript не используется.
ULIST: Маркированный список
UNKNOWN: Неизвестен
UPDATE_USER: Обновить данные профиля
UPLOAD: загрузить
UPLOAD_AN_IMAGE: Загрузить изображение
UPLOAD_FILE: Загрузить файл
UPS__WRONG_PASSWORD_OR_USERNAME__PLEASE_TRY_AGAIN_: Упс... неправильный пароль или имя пользователя, попробуйте войти ещё раз.
USED_AS_FALLBACK_WHEN_NO_MANUAL_DATE_IS_SET_: Используется как запасной вариант, когда дата не установлена в ручную.
USER: Пользователь
USERNAME: Имя пользователя
USERNAME_(READ_ONLY): Имя пользователя (не изменяется)
USERS: Пользователи
USE_2_TO_20_CHARACTERS: Изпользуйте от 2 до 20 символов.
USE_2_TO_40_CHARACTERS: Используйте от 2 до 40 символов.
USE_A_VALID_LANGUAGE_ATTRIBUTE: Используйте допустимый языковой атрибут
USE_A_VALID_YEAR: Используйте правильный формат для указания года
VIDEO: Видео
VIEW_SITE: На сайт
VISUAL: визуально
VISUAL_CONTENT_EDITOR: Визуальный редактор контента
VISUAL_EDITOR: Визуальный редактор
VISUAL_MARKDOWN_EDITOR: Визуальный редактор Markdown
VISUAL_MODE: визульный режим
WAIT: подождите
WEB: Сайт
WEBSITE_TITLE: Название сайта
WEBSITE_VISIBLE_FOR: Сайт виден для
WRITING: Настройки редактора
YEAR: Год
YOU_CAN_OVERWRITE_THE_THEME_CSS_WITH_YOUR_OWN_CSS_HERE_: Вы можете изменить CSS стиль Темы на свой собственный CSS здесь.
ADD_NEW_FEATURES_TO_YOUR_WEBSITE_WITH_PLUGINS_AND_CONFIGURE_THEM_: Добавляйте новые функции на свой сайт с помощью плагинов и настраивайте их.
BY_THE: от
CHOOSE_A_THEME_FOR_YOUR_WEBSITE_AND_CONFIGURE_THE_THEME_DETAILS_: Выберите тему для своего сайта и настройте детали темы.
CODED_WITH: Разработано с
COMMUNITY: сообщество
CONFIGURE_YOUR_WEBSITE: Настройте свой сайт
DOCS: документация
GET_HELP: Помощь
GIVE_YOUR_NEW_WEBSITE_A_NAME__ADD_THE_AUTHOR_AND_CHOOSE_A_COPYRIGHT_: Дайте вашему новому сайту имя, добавьте автора и выберите тип авторского права (лицензии).
HURRA: Урашечки
IF_YOU_HAVE_ANY_QUESTIONS__PLEASE_READ_THE: Если у вас есть вопросы, пожалуйста прочтите
NEXT_STEP: Следующий шаг
OR_OPEN_A_NEW_ISSUE_ON: или откройте новое обсуждение проблемы на
SETUP_WELCOME: Первоначальные настройки
TRENDSCHAU_DIGITAL: Trendschau Digital
VISIT_THE_AUTHOR_PANEL_AND_SETUP_YOUR_NEW_WEBSITE__YOU_CAN_CONFIGURE_THE_SYSTEM__CHOOSE_THEMES_AND_ADD_PLUGINS_: Посетите панель администратора и настройте свой новый сайт. Вы можете настроить систему, выбрать темы и добавить плагины.
YOUR_ACCOUNT_HAS_BEEN_CREATED_AND_YOU_ARE_LOGGED_IN_NOW_: Ваша учётная запись создана, и вы вошли в систему.
ACCESS_CONTROL: Настройки доступа
ACTIVATE_INDIVIDUAL_RESTRICTIONS_FOR_PAGES_IN_THE_META_TAB_OF_EACH_PAGE_: Активировать индивидуальные ограничения для страниц во вкладке метаданные у каждой страницы.
ADD_ONE_OR_MORE_USERNAMES_SEPARATED_WITH_COMMA_: Добавьте одно или несколько имен пользователей через запятую.
CUT_RESTRICTED_CONTENT_AFTER_THE_FIRST_HR_ELEMENT_ON_A_PAGE_(PER_DEFAULT_CONTENT_WILL_BE_CUT_AFTER_TITLE)_: Скрыть контент после первого hr-элемента на странице (по умолчанию контент скрывается после заголовка).
FOR_ACCESS_THE_USER_MUST_HAVE_THIS_MINIMUM_ROLE: Для доступа к этой странице пользователь должен иметь как минимум выбранную роль
LIMIT_THE_ACCESS_FOR_THE_WHOLE_WEBSITE_OR_FOR_EACH_PAGE_INDIVIDUALLY__IF_YOU_ACTIVATE_THE_WEBSITE_RESTRICTION_OR_THE_PAGE_RESTRICTIONS__THEN_SESSIONS_WILL_BE_USED_IN_FRONTEND_: Ограничьте доступ для всего сайта или для каждой страницы в отдельности. Если вы активируете ограничение сайта или ограничения страниц, то сеансы пользователей (сессии) будут храниться на стороне пользователей (во фронтенд части).
ONLY_THE_FOLLOWING_USERS_HAVE_ACCESS: Только следующие пользователи имеют доступ к этой странице
PAGE_RESTRICTIONS___ACTIVATE: Настройки страниц - Активация ограничения доступа
PAGE_RESTRICTIONS___CUT_RESTRICTED_CONTENT: Настройки страниц - Скрытие ограниченного контента
PAGE_RESTRICTIONS___NOTICE: Настройки страниц - Примечание об ограничении доступа
PAGE_RESTRICTIONS___WRAP_NOTICE_INTO_A_BOX: Настройки страниц - Оборачивание примечания об ограничении доступа в рамку
SELECT_THE_LOWEST_USERROLE__HIGHER_ROLES_WILL_HAVE_ACCESS_TOO_: Выберите роль пользователя. Более важные роли также будут иметь доступ к странице.
SHOW_THE_WEBSITE_ONLY_TO_AUTHENTICATED_USERS_AND_REDIRECT_ALL_OTHER_USERS_TO_THE_LOGIN_PAGE_: Показывать сайт только аутентифицированным пользователям и перенаправлять всех остальных пользователей на страницу входа.
USE_MARKDOWN: поддерживается Markdown
WEBSITE_RESTRICTION: Ограничения доступа к сайту
WRAP_THE_RESTRICTION_NOTICE_ABOVE_INTO_A_NOTICE_4_ELEMENT_(WHICH_CAN_BE_DESIGNED_AS_SPECIAL_BOX): Обернуть указанное выше примечание об ограничении доступа в элемент notice-4 (который может быть выполнен в виде специальной рамки).
ACTIVATE_CACHE_FOR_TWIG_TEMPLATES: Активировать кэш для шаблонов Twig
ADD_MORE_URL_SCHEMES_FOR_EXTERNAL_LINKS_E_G__LIKE_DICT://_(COMMA_SEPARATED_LIST): Добавьте дополнительные схемы URL для внешних ссылок, например как dict:// (список, разделенный запятыми)
CLEAR_CACHE: Очистить кэш
DELETE_ALL_CACHE_FILES: Удалить все файлы кэша
DEVELOPER: Настройки разработчика
DISABLE_HEADERS: Отключение заголовков
DISABLE_TYPEMILL_HEADERS_AND_SEND_YOUR_OWN: Отключить заголовки Typemill и отправлять свои заголовки
DISPLAY_APPLICATION_ERRORS: Отображение ошибок
ERROR_REPORTING: Отчёты об ошибках
IF_YOU_ADD_A_VALUE_FOR_THE_HEIGHT__THEN_THE_IMAGE_WILL_BE_CROPPED_: Если вы добавите значение высоты, изображение будет обрезано.
PROXY: Прокси
STANDARD_HEIGHT_FOR_IMAGES: Стандартная высота для изображений
STANDARD_WIDTH_FOR_IMAGES: Стандартная ширина для изображений
THE_FOLLOWING_OPTIONS_ARE_ONLY_FOR_DEVELOPERS: Следующие параметры предназначены только для разработчиков и опытных администраторов. Изменяйте параметры только в том случае, если вы действительно их понимаете. Например: никогда не активируйте отчет об ошибках для действующего сайта, используйте эту опцию только для исправления ошибок.
THIS_APPLIES_ONLY_FOR_FUTURE_IMAGES_IN_THE_CONTENT_AREA_: Это относится только к будущим изображениям в области содержимого.
TRUSTED_IPS_FOR_PROXY_(COMMA_SEPARATED): Список надежных IP-адресов для прокси (список, разделенный запятыми)
TWIG_CACHE: Twig кэш
USE_X_FORWARDED_HEADERS: Использовать X-Forwarded Заголовки

View File

@@ -1,5 +1,29 @@
<?php
use Typemill\Controllers\ControllerSystemApi;
use Typemill\Middleware\RestrictApiAccess;
# https://stackoverflow.blog/2021/10/06/best-practices-for-authentication-and-authorization-for-rest-apis/
# INTERNAL API
# on login generate token
# tmpApiKey: store token in userfile
# tmpApiDate: store date in userfile
# send username and token to frontend
# AUTHORIZATION: apikey username.tmpapikey
# validy equals session length from settings
# PUBLIC API
# ApiKey:
# AUTHORIZATION: apikey username.apikey
$app->get('/api/v1/settings', ControllerSystemApi::class . ':getSettings')->setName('api.settings.get')->add(new RestrictApiAccess());
$app->get('/api/v1/systemnavi', ControllerSystemApi::class . ':getSystemnavi')->setName('api.systemnavi.get')->add(new RestrictApiAccess());
$app->get('/api/v1/mainnavi', ControllerSystemApi::class . ':getMainnavi')->setName('api.mainnavi.get')->add(new RestrictApiAccess());
/*
use Typemill\Controllers\ControllerAuthorArticleApi;
use Typemill\Controllers\ControllerAuthorBlockApi;

View File

@@ -2,15 +2,26 @@
use Typemill\Controllers\ControllerWebFrontend;
use Typemill\Controllers\ControllerWebLogin;
use Typemill\Middleware\RedirectIfUnauthenticated;
use Typemill\Controllers\ControllerSystem;
use Typemill\Middleware\RedirectIfAuthenticated;
use Typemill\Middleware\RedirectIfUnauthenticated;
use Slim\Views\TwigMiddleware;
$app->get('/tm/login', ControllerWebLogin::class . ':show')->setName('auth.show')->add(new RedirectIfAuthenticated($routeParser, $settings));
$app->post('/tm/login', ControllerWebLogin::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($routeParser, $settings));
$app->get('/tm/system', ControllerSystem::class . ':showSettings')->setName('settings.show')->add(new RedirectIfUnauthenticated($routeParser));
$app->get('/tm/themes', ControllerSystem::class . ':showThemes')->setName('themes.show');
$app->get('/tm/plugins', ControllerSystem::class . ':showPlugins')->setName('plugins.show');
$app->get('/tm/account', ControllerSystem::class . ':showAccount')->setName('user.account');
$app->get('/tm/user/new', ControllerSystem::class . ':newUser')->setName('user.new');
$app->get('/tm/users', ControllerSystem::class . ':listUser')->setName('user.list');
$app->get('/tm/content/visual[/{params:.*}]', ControllerAuthorEditor::class . ':showBlox')->setName('content.visual');
$app->get('/tm/login', ControllerWebLogin::class . ':show')->setName('auth.show')
->add(new RedirectIfAuthenticated($container->get('routeParser'), $container->get('settings')))
->add(TwigMiddleware::createFromContainer($app));
#$app->post('/tm/login', ControllerWebLogin::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($container['router'], $container['settings']));
$app->get('/[{params:.*}]', ControllerWebFrontend::class . ':index')->setName('home');
/*

View File

@@ -12,6 +12,7 @@ contentFolder: 'content'
authorFolder: '/system/typemill/author'
adminSegment: 'tm'
editor: 'visual'
storage: '\Typemill\Models\Storage'
formats:
- 'markdown'
- 'headline'

View File

@@ -0,0 +1,25 @@
'content':
'title': 'Content'
'routename': 'content.visual'
'aclresource': 'content'
'aclprivilege': 'view'
'system':
'title': 'System'
'routename': 'settings.show'
'aclresource': 'system'
'aclprivilege': 'view'
'account':
'title': 'Account'
'routename': 'user.account'
'aclresource': 'user'
'aclprivilege': 'view'
'frontend':
'title': 'Frontend'
'routename': 'home'
'aclresource': 'user'
'aclprivilege': 'view'
'logout':
'title': 'Logout'
'routename': 'auth.login'
'aclresource': 'user'
'aclprivilege': 'view'

View File

@@ -0,0 +1,30 @@
'system':
'title': 'System'
'routename': 'settings.show'
'icon': 'icon-wrench'
'aclresource': 'system'
'aclprivilege': 'view'
'themes':
'title': 'Themes'
'routename': 'themes.show'
'icon': 'icon-paint-brush'
'aclresource': 'system'
'aclprivilege': 'view'
'plugins':
'title': 'Plugins'
'routename': 'plugins.show'
'icon': 'icon-plug'
'aclresource': 'system'
'aclprivilege': 'view'
'account':
'title': 'Account'
'routename': 'user.account'
'icon': 'icon-user'
'aclresource': 'user'
'aclprivilege': 'view'
'users':
'title': 'Users'
'routename': 'user.list'
'icon': 'icon-group'
'aclresource': 'userlist'
'aclprivilege': 'view'

View File

@@ -3,28 +3,33 @@
# included from /public/index.php
use DI\Container;
#use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Exception\HttpNotFoundException;
use Slim\Middleware\ErrorMiddleware;
use Slim\Psr7\Response as Response;
use Slim\Factory\AppFactory;
use Slim\Csrf\Guard;
use Slim\Views\Twig;
use Slim\Views\TwigMiddleware;
use Slim\Psr7\Factory\UriFactory;
use Slim\Flash\Messages;
use Twig\Extension\DebugExtension;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Typemill\Static\Settings;
use Typemill\Static\Plugins;
use Typemill\Static\Translations;
use Typemill\Static\Permissions;
use Typemill\Static\Session;
use Typemill\Events\OnSettingsLoaded;
use Typemill\Events\OnPluginsLoaded;
use Typemill\Events\OnSessionSegmentsLoaded;
use Typemill\Events\OnRolesPermissionsLoaded;
use Typemill\Events\OnResourcesLoaded;
use Typemill\Middleware\JsonBodyParser;
use Typemill\Middleware\CreateSession;
use Typemill\Middleware\TwigView;
use Typemill\Middleware\CsrfProtection;
use Typemill\Middleware\CsrfProtectionToMiddleware;
use Typemill\Middleware\FlashMessages;
#use Typemill\Middleware\ValidationErrors;
use Typemill\Extensions\TwigCsrfExtension;
use Typemill\Extensions\TwigUrlExtension;
use Typemill\Extensions\TwigUserExtension;
use Typemill\Models\StorageWrapper;
require __DIR__ . '/../vendor/autoload.php';
# require __DIR__ . '/../vendor/autoload.php';
$timer = [];
$timer['start'] = microtime(true);
@@ -37,13 +42,11 @@ ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
/****************************
* LOAD SETTINGS *
****************************/
$settings = Typemill\Static\Settings::loadSettings($rootpath);
$settings = Settings::loadSettings();
/****************************
* HANDLE DISPLAY ERRORS *
@@ -55,6 +58,18 @@ if(isset($settings['displayErrorDetails']) && $settings['displayErrorDetails'])
ini_set('display_startup_errors', 1);
}
/****************************
* ADD PATH-INFOS FOR LATER *
****************************/
# ADD THEM TO THE SETTINGS AND YOU HAVE THEM EVERYWHERE??
$uriFactory = new UriFactory();
$uri = $uriFactory->createFromGlobals($_SERVER);
$settings['fullpath'] = $uri->getPath();
$settings['basepath'] = preg_replace('/(.*)\/.*/', '$1', $_SERVER['SCRIPT_NAME']);
$settings['routepath'] = str_replace($settings['basepath'], '', $settings['fullpath']);
$timer['settings'] = microtime(true);
/****************************
@@ -71,43 +86,29 @@ $container = $app->getContainer();
$responseFactory = $app->getResponseFactory();
$routeParser = $app->getRouteCollector()->getRouteParser();
# add route parser to conatiner to use named routes in controller
# add route parser to container to use named routes in controller
$container->set('routeParser', $routeParser);
$timer['container'] = microtime(true);
/****************************
* BASE URL AND ROOT PATH *
****************************/
$uriFactory = new \Slim\Psr7\Factory\UriFactory();
$uri = $uriFactory->createFromGlobals($_SERVER);
$uripath = $uri->getPath();
$basepath = preg_replace('/(.*)\/.*/', '$1', $_SERVER['SCRIPT_NAME']);
$routepath = str_replace($basepath, '', $uripath);
# in slim 4 you alsways have to set application basepath
$app->setBasePath($basepath);
$app->setBasePath($settings['basepath']);
$container->set('basePath', $basepath);
$container->set('rootPath', $rootpath);
$container->set('uriPath', $uripath);
$container->set('routePath', $routepath);
$timer['container'] = microtime(true);
/****************************
* CREATE EVENT DISPATCHER *
****************************/
$dispatcher = new \Symfony\Component\EventDispatcher\EventDispatcher();
$dispatcher = new EventDispatcher();
/****************************
* LOAD & UPDATE PLUGINS *
****************************/
$plugins = Typemill\Static\Plugins::loadPlugins($rootpath);
$pluginSettings = $routes = $middleware = [];
$plugins = Plugins::loadPlugins();
$routes = [];
$middleware = [];
# if there are less plugins in the scan than in the settings, then a plugin has been removed
if(count($plugins) < count($settings['plugins']))
@@ -133,8 +134,8 @@ foreach($plugins as $plugin)
# if the plugin is activated, add routes/middleware and add plugin as event subscriber
if($settings['plugins'][$pluginName]['active'])
{
$routes = Typemill\Static\Plugins::getNewRoutes($className, $routes);
$middleware = Typemill\Static\Plugins::getNewMiddleware($className, $middleware);
$routes = Plugins::getNewRoutes($className, $routes);
$middleware = Plugins::getNewMiddleware($className, $middleware);
$dispatcher->addSubscriber(new $className($container));
}
@@ -144,7 +145,7 @@ foreach($plugins as $plugin)
if(isset($updateSettings))
{
# update stored settings file
Typemill\settings::updateSettings($settings);
Settings::updateSettings($settings);
}
# add final settings to the container
@@ -163,20 +164,16 @@ $timer['plugins'] = microtime(true);
* LOAD ROLES & PERMISSIONS *
****************************/
# load roles and permissions
$rolesAndPermissions = Typemill\Static\Permissions::loadRolesAndPermissions($settings['defaultSettingsPath']);
# dispatch roles so plugins can enhance them
# load roles and permissions and dispatch to plugins
$rolesAndPermissions = Permissions::loadRolesAndPermissions($settings['defaultSettingsPath']);
$rolesAndPermissions = $dispatcher->dispatch(new OnRolesPermissionsLoaded($rolesAndPermissions), 'onRolesPermissionsLoaded')->getData();
# load resources
$resources = Typemill\Static\Permissions::loadResources($settings['defaultSettingsPath']);
# dispatch roles so plugins can enhance them
# load resources and dispatch to plugins
$resources = Permissions::loadResources($settings['defaultSettingsPath']);
$resources = $dispatcher->dispatch(new OnResourcesLoaded($resources), 'onResourcesLoaded')->getData();
# create acl-object
$acl = Typemill\Static\Permissions::createAcl($rolesAndPermissions, $resources);
$acl = Permissions::createAcl($rolesAndPermissions, $resources);
# add acl to container
$container->set('acl', function() use ($acl){ return $acl; });
@@ -192,11 +189,11 @@ $timer['permissions'] = microtime(true);
if( ( isset($settings['access']) && $settings['access'] ) || ( isset($settings['pageaccess']) && $settings['pageaccess'] ) )
{
# activate session for all routes
$session_segments = [$routepath];
$session_segments = [$settings['routepath']];
}
else
{
$session_segments = ['setup', 'tm/', 'api/'];
$session_segments = ['setup', 'tm/'];
# let plugins add own segments for session, eg. to enable csrf for forms
$client_segments = $dispatcher->dispatch(new OnSessionSegmentsLoaded([]), 'onSessionSegmentsLoaded')->getData();
@@ -204,7 +201,7 @@ else
}
# start session
# Typemill\Static\Session::startSessionForSegments($session_segments, $routepath);
Session::startSessionForSegments($session_segments, $settings['routepath']);
$timer['session segments'] = microtime(true);
@@ -212,72 +209,90 @@ $timer['session segments'] = microtime(true);
* OTHER CONTAINER ITEMS *
****************************/
# Register Middleware On Container
if(isset($_SESSION)){
$container->set('csrf', function () use ($responseFactory){ return new Guard($responseFactory); });
}
# dispatcher to container
$container->set('dispatcher', function() use ($dispatcher){ return $dispatcher; });
# asset function for plugins
$container->set('assets', function() use ($basepath){ return new \Typemill\Assets($basepath); });
$container->set('assets', function() use ($settings){ return new \Typemill\Assets($settings['basepath']); });
$timer['other container'] = microtime(true);
# Register Middleware On Container
$csrf = false;
if(isset($_SESSION))
{
# add flash messsages
$container->set('flash', function(){ return new Messages(); });
# Register Middleware On Container
$csrf = new Guard($responseFactory);
$container->set('csrf', function () use ($csrf){ return $csrf; });
# Add Validation Errors Middleware
# $app->add(new ValidationErrors($container->get('view')));
}
/****************************
* TWIG TO CONTAINER *
****************************/
$container->set('view', function() use ($settings, $csrf, $uri) {
$twig = Twig::create(
[
# path to templates
$settings['rootPath'] . $settings['authorFolder'],
$settings['rootPath'] . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $settings['theme'],
],
[
# settings
'cache' => ( isset($settings['twigcache']) && $settings['twigcache'] ) ? $settings['rootPath'] . '/cache/twig' : false,
'debug' => isset($settings['displayErrorDetails'])
]
);
$twig->getEnvironment()->addGlobal('errors', NULL);
$twig->getEnvironment()->addGlobal('flash', NULL);
# add extensions
$twig->addExtension(new DebugExtension());
$twig->addExtension(new TwigUserExtension());
$twig->addExtension(new TwigUrlExtension($uri, $settings['basepath']));
# $twig->addExtension(new \Nquire\Extensions\TwigUserExtension());
if($csrf)
{
$twig->addExtension(new TwigCsrfExtension($csrf));
}
# translations
$translations = Translations::loadTranslations($settings);
# $twig->getEnvironment()->addGlobal('translations', $translations);
$twig->addExtension(new Typemill\Extensions\TwigLanguageExtension( $translations ));
return $twig;
});
/****************************
* MIDDLEWARE *
****************************/
# Add Validation Errors Middleware
# $app->add(new ValidationErrors($container->get('view')));
# Add Flash Messages Middleware
# $app->add(new FlashMessages($container->get('view')));
# Add Twig-View Middleware
# $app->add(TwigMiddleware::createFromContainer($app));
# if session add flash messages
$app->add(new FlashMessages($container));
/*
if(isset($_SESSION))
{
echo '<br>add csrf';
# Register Middleware To Be Executed On All Routes
# Add Validation Errors Middleware
#$app->add(new ValidationErrors($container->get('view')));
# Add Flash Messages Middleware
$app->add(new FlashMessages($container->get('view')));
# Add csrf middleware globally
$app->add('csrf');
}
*/
# $container->set('csrf', null);
# $app->add('csrf');
# $app->add(new CsrfProtectionToMiddleware($container));
$app->add(function($request, $handler) use ($container){
$response = $handler->handle($request);
$existingContent = (string) $response->getBody();
$response = new Response();
$response->getBody()->write('BEFORE' . $existingContent);
return $response;
});
# if session add csrf protection
$app->add(new CsrfProtection($container, $responseFactory));
# add session
$app->add(new CreateSession($session_segments, ltrim($routepath, '/') ));
# check if user : apikey
# if yes
# validate it as normal password
# do not create sessions
# set authentication to true somehow
# Add Twig-View Middleware
$app->add(TwigMiddleware::createFromContainer($app));
# add JsonBodyParser Middleware
$app->add(new JsonBodyParser());
@@ -317,15 +332,16 @@ require __DIR__ . '/routes/web.php';
$timer['routes'] = microtime(true);
/************************
* RUN APP *
* RUN APP *
************************/
$app->run();
$timer['run'] = microtime(true);
Typemill\Static\Helpers::printTimer($timer);
# Typemill\Static\Helpers::printTimer($timer);
die();
die('After app run');
@@ -367,8 +383,8 @@ foreach($session_segments as $segment)
$test = substr( $trimmedRoute, 0, strlen($segment) );
echo '<br>' . $test . ' = ' . $segment;
continue;
# echo '<br>' . $test . ' = ' . $segment;
# continue;
if(substr( $uri->getPath(), 0, strlen($segment) ) === ltrim($segment, '/'))
{
@@ -448,7 +464,7 @@ $container->set('view', function() use ($container) {
return $twig;
});
/****************************
* SET ROUTE PARSER TO USE NAMED ROUTES IN CONTROLLER *
****************************/