1
0
mirror of https://github.com/typemill/typemill.git synced 2025-10-17 07:36:24 +02:00

Version 1.1.6 User Role, Fieldsets and Refactoring

This commit is contained in:
Sebastian
2018-05-22 23:03:27 +02:00
parent aefb9709cb
commit e346625e32
32 changed files with 1073 additions and 657 deletions

View File

@@ -7,6 +7,7 @@ use Slim\Http\Request;
use Slim\Http\Response;
use Typemill\Models\Validation;
use Typemill\Models\User;
use Typemill\Models\WriteYaml;
class AuthController extends Controller
{
@@ -34,9 +35,37 @@ class AuthController extends Controller
public function show(Request $request, Response $response, $args)
{
$this->c->view->render($response, '/auth/login.twig');
}
$data = array();
/* check previous login attemps */
$yaml = new WriteYaml();
$logins = $yaml->getYaml('settings/users', '.logins');
$userIP = $this->getUserIP();
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
if($userLogins)
{
/* get the latest */
$lastLogin = intval($logins[$userIP][$userLogins-1]);
/* if last login is longer than 60 seconds ago, clear it. */
if(time() - $lastLogin > 60)
{
unset($logins[$userIP]);
$yaml->updateYaml('settings/users', '.logins', $logins);
}
/* Did the user made three login attemps that failed? */
elseif($userLogins >= 3)
{
$timeleft = 60 - (time() - $lastLogin);
$data['messages'] = array('time' => $timeleft, 'error' => array( 'Too many bad logins. Please wait.'));
}
}
$this->c->view->render($response, '/auth/login.twig', $data);
}
/**
* signin an existing user
*
@@ -47,6 +76,36 @@ class AuthController extends Controller
public function login(Request $request, Response $response)
{
/* log user attemps to authenticate */
$yaml = new WriteYaml();
$logins = $yaml->getYaml('settings/users', '.logins');
$userIP = $this->getUserIP();
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
/* if there have been user logins before. You have to do this again, because user does not always refresh the login page and old login attemps are stored. */
if($userLogins)
{
/* get the latest */
$lastLogin = intval($logins[$userIP][$userLogins-1]);
/* if last login is longer than 60 seconds ago, clear it and add this attempt */
if(time() - $lastLogin > 60)
{
unset($logins[$userIP]);
$yaml->updateYaml('settings/users', '.logins', $logins);
}
/* Did the user made three login attemps that failed? */
elseif($userLogins >= 2)
{
$logins[$userIP][] = time();
$yaml->updateYaml('settings/users', '.logins', $logins);
return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
}
/* authentication */
$params = $request->getParams();
$validation = new Validation();
@@ -58,14 +117,26 @@ class AuthController extends Controller
if($userdata && password_verify($params['password'], $userdata['password']))
{
$user->login($userdata['username']);
/* clear the user login attemps */
if($userLogins)
{
unset($logins[$userIP]);
$yaml->updateYaml('settings/users', '.logins', $logins);
}
return $response->withRedirect($this->c->router->pathFor('settings.show'));
}
}
/* if authentication failed, add attempt to log file */
$logins[$userIP][] = time();
$yaml->updateYaml('settings/users', '.logins', $logins);
$this->c->flash->addMessage('error', 'Ups, credentials were wrong, please try again.');
$this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.');
return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
/**
* log out a user
*
@@ -82,5 +153,27 @@ class AuthController extends Controller
}
return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
}
private function getUserIP()
{
$client = @$_SERVER['HTTP_CLIENT_IP'];
$forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
$remote = $_SERVER['REMOTE_ADDR'];
if(filter_var($client, FILTER_VALIDATE_IP))
{
$ip = $client;
}
elseif(filter_var($forward, FILTER_VALIDATE_IP))
{
$ip = $forward;
}
else
{
$ip = $remote;
}
return $ip;
}
}

View File

@@ -20,6 +20,11 @@ abstract class Controller
$data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
unset($_SESSION['old']);
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
return $this->c->view->render($response, $route, $data);
}

View File

@@ -90,7 +90,7 @@ class SettingsController extends Controller
if(isset($themeSettings['forms']['fields']))
{
$fields = $this->getFields($userSettings, 'themes', $themeName, $themeSettings);
/* overwrite original theme form definitions with enhanced form objects */
$themedata[$themeName]['forms']['fields'] = $fields;
}
@@ -177,57 +177,73 @@ class SettingsController extends Controller
/* then iterate through the fields */
foreach($objectSettings['forms']['fields'] as $fieldName => $fieldConfigs)
{
/* and create a new field object with the field name and the field configurations. */
$field = new Field($fieldName, $fieldConfigs);
/* you have to prefil the value for the field with default settings, user settings or old user-input from form */
$userValue = false;
/* first, add the default values from the original plugin or theme settings. Ignore checkboxes, otherwiese they might be always checked */
if(isset($objectSettings['settings'][$fieldName]))
if($fieldConfigs['type'] == 'fieldset')
{
$userValue = $objectSettings['settings'][$fieldName];
}
/* now overwrite them with the local stored user settings */
if(isset($userSettings[$objectType][$objectName][$fieldName]))
{
$userValue = $userSettings[$objectType][$objectName][$fieldName];
}
/* overwrite it with old input in form, if exists */
if(isset($_SESSION['old'][$objectName][$fieldName]))
{
$userValue = $_SESSION['old'][$objectName][$fieldName];
}
/* now we have set the uservalue for the field. Prepopulate the field object with it now */
if($field->getType() == "textarea")
{
if($userValue)
{
$field->setContent($userValue);
}
}
elseif($field->getType() == "checkbox")
{
/* needs special treatment, because field does not exist in settings if unchecked by user */
if(isset($userSettings[$objectType][$objectName][$fieldName]))
{
$field->setAttribute('checked', 'checked');
}
else
{
$field->unsetAttribute('checked');
}
/* Create an array for the subsettings of the fieldset with the same structure and data as the original settings array */
$subSettings = $objectSettings;
$subSettings['forms'] = $fieldConfigs;
$fieldset = array();
$fieldset['type'] = 'fieldset';
$fieldset['legend'] = $fieldConfigs['legend'];
$fieldset['fields'] = $this->getFields($userSettings, $objectType, $objectName, $subSettings);
$fields[] = $fieldset;
}
else
{
$field->setAttributeValue('value', $userValue);
}
/* and create a new field object with the field name and the field configurations. */
$field = new Field($fieldName, $fieldConfigs);
/* you have to prefil the value for the field with default settings, user settings or old user-input from form */
$userValue = false;
/* add the field to the field-List with the plugin-name as key */
$fields[] = $field;
/* first, add the default values from the original plugin or theme settings. Ignore checkboxes, otherwiese they might be always checked */
if(isset($objectSettings['settings'][$fieldName]))
{
$userValue = $objectSettings['settings'][$fieldName];
}
/* now overwrite them with the local stored user settings */
if(isset($userSettings[$objectType][$objectName][$fieldName]))
{
$userValue = $userSettings[$objectType][$objectName][$fieldName];
}
/* overwrite it with old input in form, if exists */
if(isset($_SESSION['old'][$objectName][$fieldName]))
{
$userValue = $_SESSION['old'][$objectName][$fieldName];
}
/* now we have set the uservalue for the field. Prepopulate the field object with it now */
if($field->getType() == "textarea")
{
if($userValue)
{
$field->setContent($userValue);
}
}
elseif($field->getType() == "checkbox")
{
/* needs special treatment, because field does not exist in settings if unchecked by user */
if(isset($userSettings[$objectType][$objectName][$fieldName]))
{
$field->setAttribute('checked', 'checked');
}
else
{
$field->unsetAttribute('checked');
}
}
else
{
$field->setAttributeValue('value', $userValue);
}
/* add the field to the field-List with the plugin-name as key */
$fields[] = $field;
}
}
return $fields;
@@ -331,13 +347,31 @@ class SettingsController extends Controller
/* fetch the original settings from the folder (plugin or theme) to get the field definitions */
$originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName);
if($originalSettings)
if(isset($originalSettings['forms']['fields']))
{
/* flaten the multi-dimensional array with fieldsets to a one-dimensional array */
$originalFields = array();
foreach($originalSettings['forms']['fields'] as $fieldName => $fieldValue)
{
if(isset($fieldValue['fields']))
{
foreach($fieldValue['fields'] as $subFieldName => $subFieldValue)
{
$originalFields[$subFieldName] = $subFieldValue;
}
}
else
{
$originalFields[$fieldName] = $fieldValue;
}
}
/* take the user input data and iterate over all fields and values */
foreach($userInput as $fieldName => $fieldValue)
{
/* get the corresponding field definition from original plugin settings */
$fieldDefinition = isset($originalSettings['forms']['fields'][$fieldName]) ? $originalSettings['forms']['fields'][$fieldName] : false;
$fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false;
if($fieldDefinition)
{
/* validate user input for this field */
@@ -345,18 +379,23 @@ class SettingsController extends Controller
}
if(!$fieldDefinition && $fieldName != 'active')
{
$_SESSION['errors'][$objectName][$fieldName] = 'This field is not defined!';
$_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!');
}
}
}
}
/***********************
** USER MANAGEMENT **
***********************/
public function showUser($request, $response, $args)
{
if($_SESSION['role'] == 'editor' && $_SESSION['user'] !== $args['username'])
{
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] ));
}
$validate = new Validation();
if($validate->username($args['username']))
@@ -428,12 +467,25 @@ class SettingsController extends Controller
public function updateUser($request, $response, $args)
{
if($request->isPost())
{
{
$params = $request->getParams();
$user = new User();
$userroles = $user->getUserroles();
$validate = new Validation();
/* non admins have different update rights */
if($_SESSION['role'] !== 'administrator')
{
/* if an editor tries to update other userdata than its own */
if($_SESSION['user'] !== $params['username'])
{
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] ));
}
/* non admins cannot change his userrole */
$params['userrole'] = $_SESSION['role'];
}
if($validate->existingUser($params, $userroles))
{
$userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole']);
@@ -442,14 +494,14 @@ class SettingsController extends Controller
{
$user->updateUser($userdata);
$this->c->flash->addMessage('info', 'Saved all changes');
return $response->withRedirect($this->c->router->pathFor('user.list'));
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']]));
}
elseif($validate->newPassword($params))
{
$userdata['password'] = $params['newpassword'];
$user->updateUser($userdata);
$this->c->flash->addMessage('info', 'Saved all changes');
return $response->withRedirect($this->c->router->pathFor('user.list'));
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']]));
}
}
@@ -465,6 +517,16 @@ class SettingsController extends Controller
$params = $request->getParams();
$validate = new Validation();
$user = new User();
/* non admins have different update rights */
if($_SESSION['role'] !== 'administrator')
{
/* if an editor tries to delete other user than its own */
if($_SESSION['user'] !== $params['username'])
{
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] ));
}
}
if($validate->username($params['username']))
{

View File

@@ -0,0 +1,34 @@
<?php
namespace Typemill\Extensions;
class TwigUserExtension extends \Twig_Extension
{
public function getFunctions()
{
return [
new \Twig_SimpleFunction('is_role', array($this, 'isRole' )),
new \Twig_SimpleFunction('get_username', array($this, 'getUsername' ))
];
}
public function isRole($role)
{
if(isset($_SESSION['role']) && $_SESSION['role'] == $role)
{
return true;
}
return false;
}
public function getUsername()
{
if(isset($_SESSION['user']))
{
return $_SESSION['user'];
}
return false;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Typemill\Middleware;
use Slim\Interfaces\RouterInterface;
use Slim\Http\Request;
use Slim\Http\Response;
class RedirectIfNoAdmin
{
protected $router;
public function __construct(RouterInterface $router, $flash)
{
$this->router = $router;
}
public function __invoke(Request $request, Response $response, $next)
{
if(!isset($_SESSION['login']) || !isset($_SESSION['role']))
{
$response = $response->withRedirect($this->router->pathFor('auth.show'));
}
if($_SESSION['role'] != 'administrator')
{
$response = $response->withRedirect($this->router->pathFor('content.show'));
}
return $next($request, $response);
}
}

View File

@@ -21,7 +21,7 @@ class RedirectIfUnauthenticated
{
$response = $response->withRedirect($this->router->pathFor('auth.show'));
}
return $next($request, $response);
}
}

View File

@@ -13,12 +13,14 @@ class User extends WriteYaml
/* get all plugins folder */
$users = array_diff(scandir($userDir), array('..', '.'));
$cleanUser = array();
foreach($users as $key => $user)
{
if($user == '.logins'){ continue; }
$cleanUser[] = str_replace('.yaml', '', $user);
}
return $cleanUser;
}
@@ -75,8 +77,13 @@ class User extends WriteYaml
public function login($username)
{
$_SESSION['user'] = $username;
$_SESSION['login'] = true;
$user = $this->getUser($username);
if($user)
{
$_SESSION['user'] = $user['username'];
$_SESSION['role'] = $user['userrole'];
$_SESSION['login'] = true;
}
}
public function generatePassword($password)

View File

@@ -7,6 +7,7 @@ use Typemill\Controllers\SettingsController;
use Typemill\Controllers\ContentController;
use Typemill\Middleware\RedirectIfUnauthenticated;
use Typemill\Middleware\RedirectIfAuthenticated;
use Typemill\Middleware\RedirectIfNoAdmin;
if($settings['settings']['setup'])
{
@@ -26,23 +27,26 @@ else
$app->get('/setup/welcome', AuthController::class . ':redirect')->setName('setup.welcome');
}
$app->get('/tm-author', AuthController::class . ':redirect');
$app->get('/tm-author/login', AuthController::class . ':show')->setName('auth.show')->add(new RedirectIfAuthenticated($container['router']));
$app->post('/tm-author/login', AuthController::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($container['router']));
$app->get('/tm-author/logout', AuthController::class . ':logout')->setName('auth.logout')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm-author/settings', SettingsController::class . ':showSettings')->setName('settings.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->post('/tm-author/settings', SettingsController::class . ':saveSettings')->setName('settings.save')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm-author/themes', SettingsController::class . ':showThemes')->setName('themes.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->post('/tm-author/themes', SettingsController::class . ':saveThemes')->setName('themes.save')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm-author/plugins', SettingsController::class . ':showPlugins')->setName('plugins.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->post('/tm-author/plugins', SettingsController::class . ':savePlugins')->setName('plugins.save')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm-author/user/new', SettingsController::class . ':newUser')->setName('user.new')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->post('/tm-author/user/create', SettingsController::class . ':createUser')->setName('user.create')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->post('/tm-author/user/update', SettingsController::class . ':updateUser')->setName('user.update')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->post('/tm-author/user/delete', SettingsController::class . ':deleteUser')->setName('user.delete')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm-author/user/{username}', SettingsController::class . ':showUser')->setName('user.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm-author/user', SettingsController::class . ':listUser')->setName('user.list')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm-author/content', ContentController::class . ':showContent')->setName('content.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm', AuthController::class . ':redirect');
$app->get('/tm/login', AuthController::class . ':show')->setName('auth.show')->add(new RedirectIfAuthenticated($container['router']));
$app->post('/tm/login', AuthController::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($container['router']));
$app->get('/tm/logout', AuthController::class . ':logout')->setName('auth.logout')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm/content', ContentController::class . ':showContent')->setName('content.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm/settings', SettingsController::class . ':showSettings')->setName('settings.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
$app->post('/tm/settings', SettingsController::class . ':saveSettings')->setName('settings.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
$app->get('/tm/themes', SettingsController::class . ':showThemes')->setName('themes.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
$app->post('/tm/themes', SettingsController::class . ':saveThemes')->setName('themes.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
$app->get('/tm/plugins', SettingsController::class . ':showPlugins')->setName('plugins.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
$app->post('/tm/plugins', SettingsController::class . ':savePlugins')->setName('plugins.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
$app->get('/tm/user/new', SettingsController::class . ':newUser')->setName('user.new')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
$app->post('/tm/user/create', SettingsController::class . ':createUser')->setName('user.create')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
$app->post('/tm/user/update', SettingsController::class . ':updateUser')->setName('user.update')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->post('/tm/user/delete', SettingsController::class . ':deleteUser')->setName('user.delete')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm/user/{username}', SettingsController::class . ':showUser')->setName('user.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash']));
$app->get('/tm/user', SettingsController::class . ':listUser')->setName('user.list')->add(new RedirectIfNoAdmin($container['router'], $container['flash']));
foreach($routes as $pluginRoute)
{

View File

@@ -41,7 +41,9 @@ class Settings
'userPath' => $rootPath . 'settings' . DIRECTORY_SEPARATOR . 'users',
'authorPath' => __DIR__ . DIRECTORY_SEPARATOR . 'author' . DIRECTORY_SEPARATOR,
'contentFolder' => 'content',
'version' => '1.1.5',
'cache' => true,
'cachePath' => $rootPath . 'cache',
'version' => '1.1.4',
'setup' => true,
'welcome' => true
];

View File

@@ -22,11 +22,18 @@
<span class="error">{{ errors.password | first }}</span>
{% endif %}
</div>
</fieldset>
</fieldset>
<div class="loginarea" id="loginarea">
<input type="submit" value="Login" id="loginbutton" class="loginbutton" />
{{ csrf_field() | raw }}
{% if messages.time %}
<div id="counter" class="counter">wait <span id="wait">{{ messages.time }}</span> sec</div>
<div class="forgotpw"><a href="https://typemill.net/writers/forgot-password" target="_blank">Forgot password?</a></div>
{% endif %}
</div>
<input type="submit" value="Login" />
{{ csrf_field() | raw }}
</form>
</div>

File diff suppressed because it is too large Load Diff

32
system/author/js/auth.js Normal file
View File

@@ -0,0 +1,32 @@
/*************************************
** LOGIN TIMER **
*************************************/
var wait = document.getElementById('wait');
if(wait)
{
var loginbtn = document.getElementById("loginbutton");
var seconds = parseInt(wait.innerHTML);
loginbtn.disabled = true;
loginbtn.value = '';
var counter = setInterval(function () {
seconds = seconds - 1;
wait.innerHTML = seconds;
if (seconds == 0) {
loginbtn.disabled = false;
loginbtn.value = 'Login';
var countdown = document.getElementById("counter");
// var flash = document.getElementById("flash-message");
countdown.parentNode.removeChild(countdown);
// flash.parentNode.removeChild(flash);
clearInterval(counter);
}
}, 1000);
}

View File

@@ -248,7 +248,7 @@
});
}
}
/*************************************
** COLOR PICKER **
*************************************/

View File

@@ -27,5 +27,6 @@
{% block content %}{% endblock %}
</div>
<script src="{{ base_url }}/system/author/js/auth.js"></script>
</body>
</html>

View File

@@ -1,17 +1,19 @@
<nav id="sidebar-menu" class="sidebar-menu">
<div id="mobile-menu" class="menu-action">Menu <span class="button-arrow"></span></div>
<h3>Settings</h3>
<ul class="menu-list">
<li class="menu-item"><a href="{{ path_for('settings.show') }}"{{ (route == 'settings.show') ? 'class="active"' : '' }}>System</a></li>
<li class="menu-item"><a href="{{ path_for('themes.show') }}"{{ (route == 'themes.show') ? 'class="active"' : '' }}>Themes</a></li>
<li class="menu-item"><a href="{{ path_for('plugins.show') }}"{{ (route == 'plugins.show') ? 'class="active"' : '' }}>Plugins</a></li>
</ul>
<h3>Users</h3>
<ul class="menu-list">
<li class="menu-item"><a href="{{ path_for('user.list') }}"{{ (route == 'user.list') ? 'class="active"' : '' }}>All users</a></li>
<li class="menu-item"><a href="{{ path_for('user.new') }}"{{ (route == 'user.new') ? 'class="active"' : '' }}>Create user</a></li>
{% for user in users %}
<li class="menu-item"><a href="{{ path_for('user.show', {'username' : user }) }}"{{ (username == user) ? 'class="active"' : '' }}>{{ user }}</a></li>
{% endfor %}
</ul>
{% if is_role('administrator') %}
<div id="mobile-menu" class="menu-action">Menu <span class="button-arrow"></span></div>
<h3>Settings</h3>
<ul class="menu-list">
<li class="menu-item"><a href="{{ path_for('settings.show') }}"{{ (route == 'settings.show') ? 'class="active"' : '' }}>System</a></li>
<li class="menu-item"><a href="{{ path_for('themes.show') }}"{{ (route == 'themes.show') ? 'class="active"' : '' }}>Themes</a></li>
<li class="menu-item"><a href="{{ path_for('plugins.show') }}"{{ (route == 'plugins.show') ? 'class="active"' : '' }}>Plugins</a></li>
</ul>
<h3>Users</h3>
<ul class="menu-list">
<li class="menu-item"><a href="{{ path_for('user.list') }}"{{ (route == 'user.list') ? 'class="active"' : '' }}>All users</a></li>
<li class="menu-item"><a href="{{ path_for('user.new') }}"{{ (route == 'user.new') ? 'class="active"' : '' }}>Create user</a></li>
{% for user in users %}
<li class="menu-item"><a href="{{ path_for('user.show', {'username' : user }) }}"{{ (username == user) ? 'class="active"' : '' }}>{{ user }}</a></li>
{% endfor %}
</ul>
{% endif %}
</nav>

View File

@@ -1,6 +1,6 @@
{% if flash.getMessage('info') %}
<div class="alert alert-info">
<div class="alert alert-info" id="flash-message">
{{ flash.getMessage('info') | first }}
</div>
@@ -8,7 +8,7 @@
{% if messages.info %}
<div class="alert alert-error">
<div class="alert alert-error" id="flash-message">
{{ messages.info | first }}
</div>
@@ -16,7 +16,7 @@
{% if flash.getMessage('error') %}
<div class="alert alert-error">
<div class="alert alert-error" id="flash-message">
{{ flash.getMessage('error') | first }}
</div>
@@ -24,7 +24,7 @@
{% if messages.error %}
<div class="alert alert-error">
<div class="alert alert-error" id="flash-message">
{{ messages.error | first }}
</div>

View File

@@ -4,8 +4,12 @@
</div>
<ul class="navi-items">
<li><a href="{{ path_for('content.show') }}"{{ navigation ? 'class="active"' : '' }}><i class="icon-doc-text"></i><span class="nav-label"> Content</span></a></li><li>
<a href="{{ path_for('settings.show') }}"{{ users ? 'class="active"' : '' }}><i class="icon-cog"></i><span class="nav-label"> Settings</span></a></li><li>
<a href="{{ base_url }}"><i class="icon-link-ext"></i><span class="nav-label"> Website</span></a></li><li>
{% if is_role('administrator') %}
<a href="{{ path_for('settings.show') }}"{{ users ? 'class="active"' : '' }}><i class="icon-cog"></i><span class="nav-label"> Settings</span></a></li><li>
{% else %}
<a href="{{ path_for('user.show', {'username' : get_username() }) }}"{{ users ? 'class="active"' : '' }}><i class="icon-cog"></i><span class="nav-label"> Account</span></a></li><li>
{% endif %}
<a href="{{ base_url }}"><i class="icon-link-ext"></i><span class="nav-label"> View Site</span></a></li><li>
<a href="{{ path_for('auth.logout') }}"><i class="icon-off"></i><span class="nav-label"> Logout</span></a></li>
</ul>
</nav>

View File

@@ -28,15 +28,17 @@
</div>
</header>
<div id="{{ pluginName }}" class="fc-plugin-version update-banner">{{ plugin.version ? plugin.version : 'Unknown' }}</div>
<p>{{ plugin.description ? plugin.description : 'No description' }}</p>
<ul class="cardInfo">
<li>{{ plugin.version ? plugin.version : 'Unknown' }}</li><li>
{{ plugin.licence ? plugin.licence : 'Unkown' }}</li><li>
by {{ plugin.author ? plugin.author : 'Unknown' }}</li>{% if plugin.homepage %}<li>
<a href="{{ plugin.homepage}}" target="blank">Web</a></li>{% endif %}
</ul>
<div class="cardDescPlugin">
<p>{{ plugin.description ? plugin.description : 'No description' }}</p>
<ul class="cardInfo">
<li>{{ plugin.version ? plugin.version : 'Unknown' }}</li><li>
{{ plugin.licence ? plugin.licence : 'Unkown' }}</li><li>
by {{ plugin.author ? plugin.author : 'Unknown' }}</li>{% if plugin.homepage %}<li>
<a href="{{ plugin.homepage}}" target="blank">Web</a></li>{% endif %}
</ul>
</div>
<div class="cardInner cardFields{{ errors[pluginName] ? ' open' : '' }}">
{% for field in plugin.forms.fields %}

View File

@@ -17,6 +17,7 @@
<form method="POST" action="{{ path_for('themes.save') }}">
<fieldset class="card{{ errors[themeName] ? ' errors' : '' }}">
<div class="cardInner cardHead">
{% if theme.img %}
@@ -24,39 +25,54 @@
{% else %}
<div class="no-preview">No Preview</div>
{% endif %}
<div id="{{ themeName }}" class="fc-theme-version update-banner">{{ theme.version ? theme.version : 'Unknown' }}</div>
<div class="cardContent">
<h2>{{ themeName }}</h2>
<p>{{ theme.description }}</p>
<ul class="cardInfo">
<li>{{ theme.version ? theme.version : 'Unknown' }}</li><li>
{{ theme.licence ? theme.licence : 'Unkown' }}</li><li>
by {{ theme.author ? theme.author : 'Unknown' }}</li>{% if theme.homepage %}<li>
<a href="{{ theme.homepage}}" target="blank">Web</a></li>{% endif %}
</ul>
<div id="{{ themeName }}" class="fc-theme-version update-banner">{{ theme.version ? theme.version : 'Unknown' }}</div>
<div class="cardDescription">
<h2>{{ themeName }}</h2>
<p>{{ theme.description }}</p>
<ul class="cardInfo">
<li>{{ theme.version ? theme.version : 'Unknown' }}</li><li>
{{ theme.licence ? theme.licence : 'Unkown' }}</li><li>
by {{ theme.author ? theme.author : 'Unknown' }}</li>{% if theme.homepage %}<li>
<a href="{{ theme.homepage}}" target="blank">Web</a></li>{% endif %}
</ul>
</div>
</div>
<div class="cardInner cardFields{{ errors[themeName] ? ' open' : '' }}">
{% for field in theme.forms.fields %}
{% include '/partials/forms.twig' with {'itemName' : themeName, 'object' : 'themes' } %}
{% if field.type == 'fieldset' %}
<fieldset class="subfield">
<legend>{{ field.legend }}</legend>
{% for field in field.fields %}
{% include '/partials/forms.twig' with {'itemName' : themeName, 'object' : 'themes' } %}
{% endfor %}
</fieldset>
{% else %}
{% include '/partials/forms.twig' with {'itemName' : themeName, 'object' : 'themes' } %}
{% endif %}
{% endfor %}
</div>
<input type="hidden" name="theme" value="{{ themeName }}">
<div class="medium">
<button type="button" class="theme-button fc-settings{{ (settings.theme == themeName) ? ' active' : '' }}{{ theme.forms.fields|length > 0 ? ' has-settings' : ' no-settings'}}">{{ theme.forms.fields|length > 0 ? 'Settings <span class="button-arrow"></span>' : 'No Settings'}}</button>
</div>
<div class="medium">
<input type="submit" value="Save Theme" />
</div>
<div class="button-box">
<input type="hidden" name="theme" value="{{ themeName }}">
<div class="medium">
<button type="button" class="theme-button fc-settings{{ (settings.theme == themeName) ? ' active' : '' }}{{ theme.forms.fields|length > 0 ? ' has-settings' : ' no-settings'}}">{{ theme.forms.fields|length > 0 ? 'Settings <span class="button-arrow"></span>' : 'No Settings'}}</button>
</div>
<div class="medium">
<input type="submit" value="Save Theme" />
</div>
</div>
</div>
</fieldset>

View File

@@ -9,7 +9,7 @@
<section id="user" class="settings">
<header>
<header class="headline">
<h1>Edit User</h1>
</header>
@@ -32,17 +32,19 @@
{% endif %}
</div>
<div class="large{{ errors.userrole ? ' errors' : '' }}">
<label for="userrole">Role <abbr title="required">*</abbr></label>
<select name="userrole" required>
{% for role in userrole %}
<option value="{{ role }}"{% if (role == old.userrole or role == userdata.userrole) %} selected{% endif %}>{{ role }}</option>
{% endfor %}
</select>
{% if errors.userrole %}
<span class="error">{{ errors.userrole | first }}</span>
{% endif %}
</div>
{% if is_role('administrator') %}
<div class="large{{ errors.userrole ? ' errors' : '' }}">
<label for="userrole">Role <abbr title="required">*</abbr></label>
<select name="userrole" required>
{% for role in userrole %}
<option value="{{ role }}"{% if (role == old.userrole or role == userdata.userrole) %} selected{% endif %}>{{ role }}</option>
{% endfor %}
</select>
{% if errors.userrole %}
<span class="error">{{ errors.userrole | first }}</span>
{% endif %}
</div>
{% endif %}
<div class="large{{ errors.password ? ' errors' : '' }}">
<label for="password">Actual Password</label>

View File

@@ -7,7 +7,7 @@
<section id="users" class="settings">
<header>
<header class="headline">
<h1>All Users</h1>
</header>

View File

@@ -9,7 +9,7 @@
<section id="new-user" class="settings">
<header>
<header class="headline">
<h1>Create New User</h1>
</header>

View File

@@ -7,6 +7,8 @@ use Typemill\Events\OnPluginsLoaded;
* START SESSION *
************************/
ini_set( 'session.cookie_httponly', 1 );
session_name('typemill_session');
session_start();
/****************************
@@ -133,7 +135,7 @@ $container['view'] = function ($container)
$path = array($container->get('settings')['themePath'], $container->get('settings')['authorPath']);
$view = new \Slim\Views\Twig( $path, [
'cache' => false,
'cache' => $container->get('settings')['cache'] ? $container->get('settings')['cachePath'] : false,
'autoescape' => false,
'debug' => true
]);
@@ -143,6 +145,7 @@ $container['view'] = function ($container)
$view->addExtension(new Slim\Views\TwigExtension($container['router'], $basePath));
$view->addExtension(new Twig_Extension_Debug());
$view->addExtension(new Typemill\Extensions\TwigCsrfExtension($container['csrf']));
$view->addExtension(new Typemill\Extensions\TwigUserExtension());
/* use {{ base_url() }} in twig templates */
$view['base_url'] = $container['request']->getUri()->getBaseUrl();