diff --git a/.gitignore b/.gitignore index c59d41c..3687314 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,11 @@ -cache/structure.txt -cache/sitemap.xml -cache/lastSitemap.txt -cache/stats.txt +cache settings/settings.yaml settings/users plugins/admin plugins/demo plugins/disqus +plugins/download plugins/finalwords -plugins/share plugins/version system/vendor tests diff --git a/cache/lastCache.txt b/cache/lastCache.txt index 3603ce6..059d42c 100644 --- a/cache/lastCache.txt +++ b/cache/lastCache.txt @@ -1 +1 @@ -1526030510 \ No newline at end of file +1527021720 \ No newline at end of file diff --git a/content/1_getting-started/02-settings.md b/content/1_getting-started/02-settings.md index ee460aa..9f6bc2f 100644 --- a/content/1_getting-started/02-settings.md +++ b/content/1_getting-started/02-settings.md @@ -1,6 +1,6 @@ # Settings -As of Version 1.1.3 you can edit all settings in the new authoring panel of TYPEMILL. Just login to your typemill installation and go to settings. There you can edit: +As of Version 1.1.3 you can edit all settings in the new authoring panel of TYPEMILL. Just visit the url `yourwebsite.com/tm/login` and go to settings after the login. There you can edit: * The system (basic settings). * Themes (choose themes and configure it). @@ -17,4 +17,5 @@ There are some settings that are not available via the author panel. Most of the displayErrorDetails: true ```` -Don't forget to set it back to `false` before you deploy the website live. It is not secure to show the world your internal errors and many hosters will turn off all public error reports by default. \ No newline at end of file +Don't forget to set it back to `false` before you deploy the website live. It is not secure to show the world your internal errors and many hosters will turn off all public error reports by default. + diff --git a/content/2_for-writers/03-author-panel.md b/content/2_for-writers/03-author-panel.md index d86d650..9aab467 100644 --- a/content/2_for-writers/03-author-panel.md +++ b/content/2_for-writers/03-author-panel.md @@ -3,7 +3,7 @@ After you have setup your TYPEMILL website, you can login to the author panel. The login url is ```` -https://yourwebsite.net/tm-author/login +https://yourwebsite.net/tm/login ```` You can also use the url `https://yourwebsite.net/setup` that redirects to the login screen. @@ -15,4 +15,5 @@ In the **settings area** of the author panel you can: * Activate and configure **plugins**. * Manage **users**. -the **content area** of the author-panel is not ready yet, but it is on it's way and will be published in near future. Then you can create and manage all the content of your website online. For time being you have to create your content offline e.g. with a markdown editor and upload your content files with a FTP software. \ No newline at end of file +the **content area** of the author-panel is not ready yet, but it is on it's way and will be published in near future. Then you can create and manage all the content of your website online. For time being you have to create your content offline e.g. with a markdown editor and upload your content files with a FTP software. + diff --git a/content/2_for-writers/60-lost-password.md b/content/2_for-writers/60-lost-password.md new file mode 100644 index 0000000..2b66e01 --- /dev/null +++ b/content/2_for-writers/60-lost-password.md @@ -0,0 +1,21 @@ +# Lost Your Password? + +TYPEMILL does not provide a password recovery, but there are two ways to create a new password. + +## Is there another admin? + +If there is another user with admin rights, then contact him. He can delete your user, create a new one and tell you the new password. Change the password immediately after login. + +## No other admin? + +If you are the only admin user, then please follow these steps: + +* Connect to your website (e.g. via FTP). +* Go to the folder `/settings` and backup the file `settings.yaml`. +* Then delete the file `settings.yaml` on your server. +* Go to `yoursite.com/setup`. +* Fill out the form. This will create a new admin user and a fresh settings-file. +* Upload your old settings-file, so your old settings are active again. +* If not done before e.g. via FTP, delete the old admin-user in the user management now. + +It might look a bit uncomfortable but it makes sure, that you are the owner of the website. \ No newline at end of file diff --git a/content/3_for-theme-developers/03-theme-meta.md b/content/3_for-theme-developers/03-theme-meta.md index c29c1a0..229e0f9 100644 --- a/content/3_for-theme-developers/03-theme-meta.md +++ b/content/3_for-theme-developers/03-theme-meta.md @@ -66,4 +66,35 @@ forms: TYPEMILL will use these definitions and generate input fields for the author panel on the fly, so that the user can edit the values and customize the theme. If you have defined settings with the same name as the field name (e.g. `chapter`), then the input field in the author panel will automatically be prefilled with your settings from the YAML-file. -If you read the YAML-definition for input fields carefully, then you will notice that the definitions are pretty similar HTML: You simply define types and attributes like input-type, labels and placeholders. Nearly all valid field-types and field attributes are supported. You can find a detailed list in the [documentation for plugins](/plugin-developers/documentation/field-overview). \ No newline at end of file +If you have a lot of fields, you can even group some fields together in a fieldset like this: + +```` +forms: + fields: + + chapter: + type: text + label: chapter + placeholder: Add Name for Chapter + required: true + + MyFirstfieldset: + type: fieldset + legend: Last Modified + fields: + + modified: + type: checkbox + label: Activate Last Modified + description: Show last modified date at the end of each page? + + modifiedText: + type: text + label: Last Modified Text + placeholder: Last Updated + +```` + +The fields `modified` and `modifiedText` will then be grouped in a fieldset with the legend `Last Modified`. + +If you read the YAML-definition for input fields carefully, then you will notice that the definitions are pretty similar to HTML: You simply define types and attributes like input-type, labels and placeholders. Nearly all valid field-types and field attributes are supported. You can find a detailed list in the [documentation for plugins](/plugin-developers/documentation/field-overview). \ No newline at end of file diff --git a/content/4_for-plugin-developers/02-documentation/06-field-overview.md b/content/4_for-plugin-developers/02-documentation/06-field-overview.md index 81c61a7..af235f7 100644 --- a/content/4_for-plugin-developers/02-documentation/06-field-overview.md +++ b/content/4_for-plugin-developers/02-documentation/06-field-overview.md @@ -47,6 +47,7 @@ TYPEMILL accepts the following field type definitions: * textarea * tel * url +* fieldset A simple field definition looks like this: @@ -174,6 +175,38 @@ SimpleCheckbox: description: Please check me ```` +## Using Fieldsets + +If you have a lot of fields, you can group them togeter with a fieldset like this: + +```` +forms: + fields: + + chapter: + type: text + label: chapter + placeholder: Add Name for Chapter + required: true + + MyFirstfieldset: + type: fieldset + legend: Last Modified + fields: + + modified: + type: checkbox + label: Activate Last Modified + description: Show last modified date at the end of each page? + + modifiedText: + type: text + label: Last Modified Text + placeholder: Last Updated +```` + +The fields `modified` and `modifiedText` will then be grouped in a fieldset with the legend `Last Modified`. + ## Example for a complete yaml configuration To sum it up, this is a complete example of a yaml configuration file for a plugin with the meta-description, a default value and a field definition for user input: @@ -201,3 +234,4 @@ forms: block: Block classic: Classic ```` + diff --git a/system/Controllers/AuthController.php b/system/Controllers/AuthController.php index f07bbaa..1c332fb 100644 --- a/system/Controllers/AuthController.php +++ b/system/Controllers/AuthController.php @@ -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; + } } \ No newline at end of file diff --git a/system/Controllers/Controller.php b/system/Controllers/Controller.php index fbc152f..dc901f6 100644 --- a/system/Controllers/Controller.php +++ b/system/Controllers/Controller.php @@ -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); } diff --git a/system/Controllers/SettingsController.php b/system/Controllers/SettingsController.php index a74a7f2..cb14db7 100644 --- a/system/Controllers/SettingsController.php +++ b/system/Controllers/SettingsController.php @@ -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'])) { diff --git a/system/Extensions/TwigUserExtension.php b/system/Extensions/TwigUserExtension.php new file mode 100644 index 0000000..18bdb46 --- /dev/null +++ b/system/Extensions/TwigUserExtension.php @@ -0,0 +1,34 @@ +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); + } +} \ No newline at end of file diff --git a/system/Middleware/RedirectIfUnauthenticated.php b/system/Middleware/RedirectIfUnauthenticated.php index 07b0c1d..23f2c33 100644 --- a/system/Middleware/RedirectIfUnauthenticated.php +++ b/system/Middleware/RedirectIfUnauthenticated.php @@ -21,7 +21,7 @@ class RedirectIfUnauthenticated { $response = $response->withRedirect($this->router->pathFor('auth.show')); } - + return $next($request, $response); } } \ No newline at end of file diff --git a/system/Models/User.php b/system/Models/User.php index 5cb413f..7a9b5e2 100644 --- a/system/Models/User.php +++ b/system/Models/User.php @@ -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) diff --git a/system/Routes/Web.php b/system/Routes/Web.php index c8122bb..1365489 100644 --- a/system/Routes/Web.php +++ b/system/Routes/Web.php @@ -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) { diff --git a/system/Settings.php b/system/Settings.php index b7ffb5e..8f2c75c 100644 --- a/system/Settings.php +++ b/system/Settings.php @@ -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 ]; diff --git a/system/author/auth/login.twig b/system/author/auth/login.twig index 05687ce..7e9febc 100644 --- a/system/author/auth/login.twig +++ b/system/author/auth/login.twig @@ -22,11 +22,18 @@ {{ errors.password | first }} {% endif %} - + + +