Procházet zdrojové kódy

Version 1.1.3. Admin Dashboard

Sebastian před 7 roky
rodič
revize
f8dc7093bf
65 změnil soubory, kde provedl 3224 přidání a 1033 odebrání
  1. 2 0
      .gitignore
  2. 4 1
      .htaccess
  3. 1 1
      cache/lastCache.txt
  4. 11 8
      plugins/analytics/analytics.php
  5. 86 0
      system/Controllers/AuthController.php
  6. 23 0
      system/Controllers/ContentController.php
  7. 2 1
      system/Controllers/Controller.php
  8. 2 2
      system/Controllers/PageController.php
  9. 731 0
      system/Controllers/SettingsController.php
  10. 38 410
      system/Controllers/SetupController.php
  11. 5 7
      system/Extensions/ParsedownExtension.php
  12. 27 0
      system/Middleware/RedirectIfAuthenticated.php
  13. 27 0
      system/Middleware/RedirectIfUnauthenticated.php
  14. 86 0
      system/Models/User.php
  15. 61 88
      system/Models/Validation.php
  16. 30 13
      system/Models/Write.php
  17. 6 2
      system/Models/WriteYaml.php
  18. 2 2
      system/Routes/Api.php
  19. 31 4
      system/Routes/Web.php
  20. 3 2
      system/Settings.php
  21. 34 0
      system/author/auth/login.twig
  22. 41 0
      system/author/auth/setup.twig
  23. 49 0
      system/author/auth/welcome.twig
  24. 23 0
      system/author/content/content.twig
  25. 12 0
      system/author/css/fontello/LICENSE.txt
  26. 75 0
      system/author/css/fontello/README.txt
  27. 46 0
      system/author/css/fontello/config.json
  28. 85 0
      system/author/css/fontello/css/animation.css
  29. 7 0
      system/author/css/fontello/css/fontello-codes.css
  30. 10 0
      system/author/css/fontello/css/fontello-embedded.css
  31. 7 0
      system/author/css/fontello/css/fontello-ie7-codes.css
  32. 18 0
      system/author/css/fontello/css/fontello-ie7.css
  33. 63 0
      system/author/css/fontello/css/fontello.css
  34. 313 0
      system/author/css/fontello/demo.html
  35. binární
      system/author/css/fontello/font/fontello.eot
  36. 22 0
      system/author/css/fontello/font/fontello.svg
  37. binární
      system/author/css/fontello/font/fontello.ttf
  38. binární
      system/author/css/fontello/font/fontello.woff
  39. binární
      system/author/css/fontello/font/fontello.woff2
  40. 502 56
      system/author/css/style.css
  41. binární
      system/author/img/apple-touch-icon-144x144.png
  42. binární
      system/author/img/apple-touch-icon-152x152.png
  43. binární
      system/author/img/favicon-16x16.png
  44. binární
      system/author/img/favicon-32x32.png
  45. binární
      system/author/img/favicon.ico
  46. binární
      system/author/img/mstile-144x144.png
  47. 40 168
      system/author/js/author.js
  48. 0 21
      system/author/layout.twig
  49. 39 0
      system/author/layouts/layout.twig
  50. 31 0
      system/author/layouts/layoutAuth.twig
  51. 33 0
      system/author/layouts/layoutContent.twig
  52. 17 0
      system/author/partials/aside.twig
  53. 5 0
      system/author/partials/asideContent.twig
  54. 31 0
      system/author/partials/flash.twig
  55. 11 0
      system/author/partials/navi.twig
  56. 124 0
      system/author/settings/plugins.twig
  57. 75 0
      system/author/settings/system.twig
  58. 137 0
      system/author/settings/themes.twig
  59. 89 0
      system/author/settings/user.twig
  60. 36 0
      system/author/settings/userlist.twig
  61. 63 0
      system/author/settings/usernew.twig
  62. 0 218
      system/author/setup.twig
  63. 0 18
      system/author/welcome.twig
  64. 2 5
      themes/typemill/css/style.css
  65. 6 6
      themes/typemill/typemill.yaml

+ 2 - 0
.gitignore

@@ -3,6 +3,7 @@ cache/sitemap.xml
 cache/lastSitemap.txt
 cache/stats.txt
 settings/settings.yaml
+settings/users
 plugins/admin
 plugins/demo
 plugins/disqus
@@ -11,5 +12,6 @@ plugins/share
 plugins/version
 system/vendor
 tests
+themes/monograf
 zips
 zip.php

+ 4 - 1
.htaccess

@@ -22,11 +22,14 @@ RewriteRule ^(.*/)?\.git+ - [F,L]
 # RewriteCond %{HTTPS} off
 # RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
 
-
 # Use this to redirect www to non-wwww on apache servers
 # RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
 # RewriteRule ^(.*)$ http://%1/$1 [R=301,L]
 
+# Use this to redirect slash/ to no slash urls on apache servers
+# RewriteCond %{REQUEST_FILENAME} !-d
+# RewriteRule ^(.*)/$ /$1 [R=301,L]
+
 # Removes index.php
 RewriteCond %{THE_REQUEST} ^GET.*index\.php [NC]
 RewriteRule (.*?)index\.php/*(.*) /$1$2 [R=301,NE,L]

+ 1 - 1
cache/lastCache.txt

@@ -1 +1 @@
-1521057612
+1524067221

+ 11 - 8
plugins/analytics/analytics.php

@@ -30,15 +30,18 @@ class Analytics extends Plugin
 
 		$analyticSettings = $this->settings['settings']['plugins']['analytics'];
 	
-		/* fetch the template, render it with twig and add javascript with settings */
-		if($analyticSettings['tool'] == 'piwik')
+		if(isset($analyticsSettings['tool']))
 		{
-			$this->addInlineJS($twig->fetch('/piwikanalytics.twig', $this->settings));
-		}
-		elseif($analyticSettings['tool'] == 'google')
-		{
-			$this->addJS('https://www.googletagmanager.com/gtag/js?id=' . $analyticSettings['google_id']);
-			$this->addInlineJS($twig->fetch('/googleanalytics.twig', $analyticSettings));
+			/* fetch the template, render it with twig and add javascript with settings */
+			if($analyticSettings['tool'] == 'piwik')
+			{
+				$this->addInlineJS($twig->fetch('/piwikanalytics.twig', $this->settings));
+			}
+			elseif($analyticSettings['tool'] == 'google')
+			{
+				$this->addJS('https://www.googletagmanager.com/gtag/js?id=' . $analyticSettings['google_id']);
+				$this->addInlineJS($twig->fetch('/googleanalytics.twig', $analyticSettings));
+			}			
 		}
 	}
 }

+ 86 - 0
system/Controllers/AuthController.php

@@ -0,0 +1,86 @@
+<?php
+
+namespace Typemill\Controllers;
+
+use Slim\Views\Twig;
+use Slim\Http\Request;
+use Slim\Http\Response;
+use Typemill\Models\Validation;
+use Typemill\Models\User;
+
+class AuthController extends Controller
+{
+	
+	public function redirect(Request $request, Response $response)
+	{
+		if(isset($_SESSION['login']))
+		{
+			return $response->withRedirect($this->c->router->pathFor('settings.show'));
+		}
+		else
+		{
+			return $response->withRedirect($this->c->router->pathFor('auth.show'));			
+		}
+	}
+	
+	/**
+	* show login form
+	* 
+	* @param obj $request the slim request object.
+	* @param obj $response the slim response object.
+	* @param array $args with arguments past to the slim router
+	* @return obj $response and string route.
+	*/
+	
+	public function show(Request $request, Response $response, $args)
+	{
+		$this->c->view->render($response, '/auth/login.twig');
+	}
+
+	/**
+	* 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)
+	{
+		$params	 		= $request->getParams();
+		$validation		= new Validation();
+		
+		if($validation->signin($params))
+		{
+			$user = new User();
+			$userdata = $user->getUser($params['username']);
+			
+			if($userdata && password_verify($params['password'], $userdata['password']))
+			{
+				$user->login($userdata['username']);
+				return $response->withRedirect($this->c->router->pathFor('settings.show'));
+			}
+		}
+		
+		$this->c->flash->addMessage('error', 'Ups, credentials were wrong, please try again.');
+		return $response->withRedirect($this->c->router->pathFor('auth.show'));
+	}
+		
+	/**
+	* log out a user
+	* 
+	* @param obj $request the slim request object
+	* @param obj $response the slim response object
+	* @return obje $response with redirect to route
+	*/
+	
+	public function logout(Request $request, Response $response)
+	{
+		if(isset($_SESSION))
+		{
+			session_destroy();
+		}
+		
+		return $response->withRedirect($this->c->router->pathFor('auth.show'));
+	}	
+}

+ 23 - 0
system/Controllers/ContentController.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Typemill\Controllers;
+
+use Slim\Views\Twig;
+use Slim\Http\Request;
+use Slim\Http\Response;
+
+class ContentController extends Controller
+{		
+	/**
+	* Show Content
+	* 
+	* @param obj $request the slim request object
+	* @param obj $response the slim response object
+	* @return obje $response with redirect to route
+	*/
+	
+	public function showContent(Request $request, Response $response)
+	{
+		$this->render($response, 'content/content.twig', array('navigation' => true));
+	}
+}

+ 2 - 1
system/Controllers/Controller.php

@@ -18,7 +18,8 @@ abstract class Controller
 	protected function render($response, $route, $data)
 	{
 		$data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
-		
+
+		unset($_SESSION['old']);
 		return $this->c->view->render($response, $route, $data);
 	}
 	

+ 2 - 2
system/Controllers/PageController.php

@@ -127,7 +127,7 @@ class PageController extends Controller
 		}
 		
 		$contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData();
-		
+				
 		/* initialize parsedown */
 		$parsedown 		= new ParsedownExtension();
 
@@ -234,7 +234,7 @@ class PageController extends Controller
 	{
 		foreach($contentBlocks as $block)
 		{
-			if($block['element']['name'] == 'p' && substr($block['element']['text'], 0, 2) == '![' )
+			if(isset($block['element']['name']) && $block['element']['name'] == 'p' && substr($block['element']['text'], 0, 2) == '![' )
 			{
 				return $block['element']['text'];
 			}

+ 731 - 0
system/Controllers/SettingsController.php

@@ -0,0 +1,731 @@
+<?php
+
+namespace Typemill\Controllers;
+
+use \Symfony\Component\Yaml\Yaml;
+use Typemill\Models\Field;
+use Typemill\Models\Validation;
+use Typemill\Models\User;
+
+class SettingsController extends Controller
+{
+	
+	/*********************
+	**	BASIC SETTINGS	**
+	*********************/
+	
+	public function showSettings($request, $response, $args)
+	{
+		$user		= new User();		
+		$settings 	= $this->c->get('settings');
+		$copyright	= $this->getCopyright();
+		$languages	= $this->getLanguages();
+		$locale		= explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']);
+		$locale		= $locale[0];
+		$users		= $user->getUsers();
+		$route 		= $request->getAttribute('route');
+		
+		$this->render($response, 'settings/system.twig', array('settings' => $settings, 'copyright' => $copyright, 'languages' => $languages, 'locale' => $locale, 'users' => $users, 'route' => $route->getName() ));
+	}
+	
+	public function saveSettings($request, $response, $args)
+	{
+		if($request->isPost())
+		{
+			$settings 		= \Typemill\Settings::getUserSettings();
+			$params 		= $request->getParams();
+			$newSettings	= isset($params['settings']) ? $params['settings'] : false;
+			$validate		= new Validation();
+		
+			if($newSettings)
+			{
+				$copyright 					= $this->getCopyright();
+				$newSettings['startpage'] 	= isset($newSettings['startpage']) ? true : false;
+				
+				$validate->settings($newSettings, $copyright, 'settings');
+			}
+			
+			if(isset($_SESSION['errors']))
+			{
+				$this->c->flash->addMessage('error', 'Please correct the errors');
+				return $response->withRedirect($this->c->router->pathFor('settings.show'));
+			}
+			
+			/* store updated settings */
+			\Typemill\Settings::updateSettings(array_merge($settings, $newSettings));
+			
+			$this->c->flash->addMessage('info', 'Settings are stored');
+			return $response->withRedirect($this->c->router->pathFor('settings.show'));
+		}
+	}
+
+	/*********************
+	**	THEME SETTINGS	**
+	*********************/
+	
+	public function showThemes($request, $response, $args)
+	{
+		$settings 	= $this->c->get('settings');
+		$themes 	= $this->getThemes();
+		$themedata	= array();
+			
+		foreach($themes as $themeName)
+		{	
+			/* if theme is active, list it first */
+			if($settings['theme'] == $themeName)
+			{
+				$themedata = array_merge(array($themeName => null), $themedata);
+			}
+			else
+			{
+				$themedata[$themeName] = null;
+			}
+			
+			$themeSettings = \Typemill\Settings::getThemeSettings($themeName);
+			if($themeSettings)
+			{
+				/* store them as default theme data with author, year, default settings and field-definitions */
+				$themedata[$themeName] = $themeSettings;
+			}
+			
+			if(isset($themeSettings['forms']))
+			{
+				$fields = array();
+				
+				/* then iterate through the fields */
+				foreach($themeSettings['forms']['fields'] as $fieldName => $fieldConfigs)
+				{
+					/* and create a new field object with the field name and the field configurations. */
+					$field = new Field($fieldName, $fieldConfigs);
+
+					$userValue = false;
+
+					/* add value from stored usersettings */
+					if($settings['theme'] == $themeName && isset($settings['themesettings'][$fieldName]))
+					{
+						$userValue = $settings['themesettings'][$fieldName];
+					}
+					/* alternatively add value from original theme settings */
+					elseif(isset($themeSettings['settings'][$fieldName]))
+					{
+						$userValue = $themeSettings['settings'][$fieldName];
+					}
+					/* overwrite it with old input in form, if exists */
+					if(isset($_SESSION['old'][$themeName][$fieldName]))
+					{
+						$userValue = $_SESSION['old'][$themeName][$fieldName];
+					}
+										
+					if($field->getType() == "textarea")
+					{
+						if($userValue)
+						{
+							$field->setContent($userValue);
+						}
+					}
+					elseIf($field->getType() != "checkbox")
+					{
+						$field->setAttributeValue('value', $userValue);	
+					}
+
+					/* add the field to the field-List with the plugin-name as key */
+					$fields[] = $field;					
+				}
+								
+				/* overwrite original theme form definitions with enhanced form objects */
+				$themedata[$themeName]['forms']['fields'] = $fields;				
+			}
+			
+			/* add the preview image */
+			$img = getcwd() . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeName . DIRECTORY_SEPARATOR . $themeName . '.jpg';
+			$img = file_exists($img) ? $img : false;
+				
+			$themedata[$themeName]['img'] = $img;
+		}
+		
+		/* add the users for navigation */
+		$user		= new User();
+		$users		= $user->getUsers();
+		$route 		= $request->getAttribute('route');
+	
+		$this->render($response, 'settings/themes.twig', array('settings' => $settings, 'themes' => $themedata, 'users' => $users, 'route' => $route->getName() ));
+	}
+	
+	public function saveThemes($request, $response, $args)
+	{
+		if($request->isPost())
+		{
+			$settings 		= \Typemill\Settings::getUserSettings();
+			$params 		= $request->getParams();
+			$theme			= isset($params['theme']) ? $params['theme'] : false;
+			$themeSettings	= isset($params[$theme]) ? $params[$theme] : false;
+			$validate		= new Validation();
+			
+			/* set theme name and delete theme settings from user settings for the case, that the new theme has no settings */
+
+			$settings['theme'] = $theme;
+			unset($settings['themesettings']);
+						
+			if($themeSettings)
+			{
+				// load theme definitions by theme name
+				$themeOriginalSettings = \Typemill\Settings::getThemeSettings($theme);
+
+				// validate input with field definitions				
+				if($themeOriginalSettings)
+				{
+					foreach($themeSettings as $fieldName => $fieldValue)
+					{
+						$fieldDefinition = isset($themeOriginalSettings['forms']['fields'][$fieldName]) ? $themeOriginalSettings['forms']['fields'][$fieldName] : false;
+						if($fieldDefinition)
+						{
+							/* validate user input for this field */
+							$validate->pluginField($fieldName, $fieldValue, $theme, $fieldDefinition);
+						}
+						else
+						{
+							$_SESSION['errors'][$themeName][$fieldName] = 'This field is not defined for the theme!';
+						}
+					}
+				}
+				$settings['themesettings'] 	= $themeSettings;
+			}
+						
+			/* check for errors and redirect to path, if errors found */
+			if(isset($_SESSION['errors']))
+			{
+				$this->c->flash->addMessage('error', 'Please correct the errors');
+				return $response->withRedirect($this->c->router->pathFor('themes.show'));
+			}
+
+			/* store updated settings */
+			\Typemill\Settings::updateSettings($settings);
+			
+			$this->c->flash->addMessage('info', 'Settings are stored');
+			return $response->withRedirect($this->c->router->pathFor('themes.show'));
+		}
+	}
+
+
+	/*********************
+	**	Plugin SETTINGS	**
+	*********************/
+	
+	public function showPlugins($request, $response, $args)
+	{
+		$settings 	= $this->c->get('settings');
+		$plugins	= array();
+		$fields 	= array();
+
+		/* iterate through the plugins in the stored user settings */
+		foreach($settings['plugins'] as $pluginName => $pluginUserSettings)
+		{
+			/* add plugin to plugin Data, if active, set it first */
+			/* if theme is active, list it first */
+			if($settings['plugins'][$pluginName]['active'] == true)
+			{
+				$plugins = array_merge(array($pluginName => null), $plugins);
+			}
+			else
+			{
+				$plugins[$pluginName] = Null;
+			}
+						
+			/* Check if the user has deleted a plugin. Then delete it in the settings and store the updated settings. */
+			if(!is_dir($settings['rootPath'] . 'plugins' . DIRECTORY_SEPARATOR . $pluginName))
+			{
+				/* remove the plugin settings and store updated settings */
+				\Typemill\Settings::removePluginSettings($pluginName);
+				continue;
+			}
+
+			/* load the original plugin definitions from the plugin folder (author, version and stuff) */
+			$pluginOriginalSettings = \Typemill\Settings::getPluginSettings($pluginName);
+			if($pluginOriginalSettings)
+			{
+				/* store them as default plugin data with plugin author, plugin year, default settings and field-definitions */
+				$plugins[$pluginName] = $pluginOriginalSettings;
+			}
+
+			/* overwrite the original plugin settings with the stored user settings, if they exist */
+			if($pluginUserSettings)
+			{
+				$plugins[$pluginName]['settings'] = $pluginUserSettings;
+			}
+			
+			/* check, if the plugin has been disabled in the form-session-data */
+			/* TODO: Works only, if there is at least one plugin with settings */
+			if(isset($_SESSION['old']) && !isset($_SESSION['old'][$pluginName]['active']))
+			{
+				$plugins[$pluginName]['settings']['active'] = false;
+			}
+			
+			/* if the plugin defines forms and fields, so that the user can edit the plugin settings in the frontend */
+			if(isset($pluginOriginalSettings['forms']))
+			{
+				$fields = array();
+				
+				/* then iterate through the fields */
+				foreach($pluginOriginalSettings['forms']['fields'] as $fieldName => $fieldConfigs)
+				{
+					/* and create a new field object with the field name and the field configurations. */
+					$field = new Field($fieldName, $fieldConfigs);
+					
+					/* now you have the configurations of the field. Time to set the values */
+					
+					/* At first, get the value for the field from the stored user settings */
+					// $userValue = isset($pluginUserSettings[$fieldName]) ? $pluginUserSettings[$fieldName] : NULL;
+					$userValue = isset($plugins[$pluginName]['settings'][$fieldName]) ? $plugins[$pluginName]['settings'][$fieldName] : NULL;
+					
+					/* Then overwrite the value, if there are old input values for the field in the session */
+					$userValue = isset($_SESSION['old'][$pluginName][$fieldName]) ? $_SESSION['old'][$pluginName][$fieldName] : $userValue;
+					
+					if($field->getType() == "textarea")
+					{
+						if($userValue)
+						{
+							$field->setContent($userValue);
+						}
+					}
+					elseIf($field->getType() != "checkbox")
+					{
+						$field->setAttributeValue('value', $userValue);	
+					}
+
+					/* add the field to the field-List with the plugin-name as key */
+					$fields[] = $field;
+				}
+				/* overwrite original plugin form definitions with enhanced form objects */
+				$plugins[$pluginName]['forms']['fields'] = $fields;
+			}
+		}
+		
+		$user 	= new User();
+		$users 	= $user->getUsers();
+		$route 	= $request->getAttribute('route');
+		
+		$this->render($response, 'settings/plugins.twig', array('settings' => $settings, 'plugins' => $plugins, 'users' => $users, 'route' => $route->getName() ));
+	}
+
+	public function savePlugins($request, $response, $args)
+	{
+		if($request->isPost())
+		{
+			$settings 		= \Typemill\Settings::getUserSettings();
+			$pluginSettings	= array();
+			$params 		= $request->getParams();
+			$validate		= new Validation();
+					
+			/* use the stored user settings and iterate over all original plugin settings, so we do not forget any... */
+			foreach($settings['plugins'] as $pluginName => $pluginUserSettings)
+			{
+				/* if there are no input-data for this plugin, then use the stored plugin settings */
+				if(!isset($params[$pluginName]))
+				{
+					$pluginSettings[$pluginName] = $pluginUserSettings;
+				}
+				else
+				{
+					/* now fetch the original plugin settings from the plugin folder to get the field definitions */
+					$pluginOriginalSettings = \Typemill\Settings::getPluginSettings($pluginName);
+					
+					if($pluginOriginalSettings)
+					{
+						/* take the user input data and iterate over all fields and values */
+						foreach($params[$pluginName] as $fieldName => $fieldValue)
+						{
+							/* get the corresponding field definition from original plugin settings */
+							$fieldDefinition = isset($pluginOriginalSettings['forms']['fields'][$fieldName]) ? $pluginOriginalSettings['forms']['fields'][$fieldName] : false;
+							if($fieldDefinition)
+							{
+								/* validate user input for this field */
+								$validate->pluginField($fieldName, $fieldValue, $pluginName, $fieldDefinition);
+							}
+							if(!$fieldDefinition && $fieldName != 'active')
+							{
+								$_SESSION['errors'][$pluginName][$fieldName] = 'This field is not defined in the plugin!';
+							}
+						}
+					}
+					
+					/* use the input data */
+					$pluginSettings[$pluginName] = $params[$pluginName];
+				}
+				
+				/* deactivate the plugin, if there is no active flag */
+				if(!isset($params[$pluginName]['active']))
+				{
+					$pluginSettings[$pluginName]['active'] = false;
+				}
+			}
+						
+			if(isset($_SESSION['errors']))
+			{
+				$this->c->flash->addMessage('error', 'Please correct the errors below');
+			}
+			else
+			{
+				/* if everything is valid, add plugin settings to base settings again */
+				$settings['plugins'] = $pluginSettings;
+				
+				/* store updated settings */
+				\Typemill\Settings::updateSettings($settings);
+
+				$this->c->flash->addMessage('info', 'Settings are stored');
+			}
+			
+			return $response->withRedirect($this->c->router->pathFor('plugins.show'));
+		}
+	}
+
+	
+	/***********************
+	**   USER MANAGEMENT  **
+	***********************/
+	
+	public function showUser($request, $response, $args)
+	{
+		$validate 	= new Validation();
+		
+		if($validate->username($args['username']))
+		{
+			$user 		= new User();
+			$users		= $user->getUsers();
+			$userrole	= $user->getUserroles();
+			$userdata 	= $user->getUser($args['username']);
+			
+			if($userdata)
+			{				
+				return $this->render($response, 'settings/user.twig', array('users' => $users, 'userdata' => $userdata, 'userrole' => $userrole, 'username' => $args['username'] ));
+			}
+		}
+		
+		$this->c->flash->addMessage('error', 'User does not exists');
+		return $response->withRedirect($this->c->router->pathFor('user.list'));
+	}
+
+	public function listUser($request, $response)
+	{
+		$user		= new User();
+		$users		= $user->getUsers();
+		$userdata 	= array();
+		$route 		= $request->getAttribute('route');
+		
+		foreach($users as $username)
+		{
+			$userdata[] = $user->getUser($username);
+		}
+		
+		$this->render($response, 'settings/userlist.twig', array('users' => $users, 'userdata' => $userdata, 'route' => $route->getName() ));		
+	}
+	
+	public function newUser($request, $response, $args)
+	{
+		$user 		= new User();
+		$users		= $user->getUsers();
+		$userrole	= $user->getUserroles();
+		$route 		= $request->getAttribute('route');
+
+		$this->render($response, 'settings/usernew.twig', array('users' => $users, 'userrole' => $userrole, 'route' => $route->getName() ));
+	}
+		
+	public function createUser($request, $response, $args)
+	{
+		if($request->isPost())
+		{
+			$params 		= $request->getParams();
+			$user 			= new User();
+			$userroles		= $user->getUserroles();
+			$validate		= new Validation();
+
+			if($validate->newUser($params, $userroles))
+			{
+				$userdata	= array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']);
+				
+				$user->createUser($userdata);
+
+				$this->c->flash->addMessage('info', 'Welcome, there is a new user!');
+				return $response->withRedirect($this->c->router->pathFor('user.list'));
+			}
+			
+			$this->c->flash->addMessage('error', 'Please correct your input');
+			return $response->withRedirect($this->c->router->pathFor('user.new'));
+		}
+	}
+	
+	public function updateUser($request, $response, $args)
+	{
+		if($request->isPost())
+		{
+			$params 		= $request->getParams();
+			$user 			= new User();
+			$userroles		= $user->getUserroles();
+			$validate		= new Validation();
+
+			if($validate->existingUser($params, $userroles))
+			{
+				$userdata	= array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole']);
+				
+				if(empty($params['password']) AND empty($params['newpassword']))
+				{
+					$user->updateUser($userdata);
+					$this->c->flash->addMessage('info', 'Saved all changes');
+					return $response->withRedirect($this->c->router->pathFor('user.list'));
+				}
+				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'));
+				}
+			}
+			
+			$this->c->flash->addMessage('error', 'Please correct your input');
+			return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']]));
+		}
+	}
+	
+	public function deleteUser($request, $response, $args)
+	{
+		if($request->isPost())
+		{
+			$params 		= $request->getParams();
+			$validate		= new Validation();
+			$user			= new User();
+			
+			if($validate->username($params['username']))
+			{
+				$user->deleteUser($params['username']);
+				
+				$this->c->flash->addMessage('info', 'Say goodbye, the user is gone!');
+				return $response->withRedirect($this->c->router->pathFor('user.list'));			
+			}
+			
+			$this->c->flash->addMessage('error', 'Ups, we did not find that user');
+			return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']]));			
+		}
+	}
+
+	private function getThemes()
+	{
+		$themeFolder 	= $this->c->get('settings')['rootPath'] . $this->c->get('settings')['themeFolder'];
+		$themeFolderC 	= scandir($themeFolder);
+		$themes 		= array();
+		foreach ($themeFolderC as $key => $theme)
+		{
+			if (!in_array($theme, array(".","..")))
+			{
+				if (is_dir($themeFolder . DIRECTORY_SEPARATOR . $theme))
+				{
+					$themes[] = $theme;
+				}
+			}
+		}
+		return $themes;
+	}
+		
+	private function getCopyright()
+	{
+		return array(
+			"©",
+			"CC-BY",
+			"CC-BY-NC",
+			"CC-BY-NC-ND",
+			"CC-BY-NC-SA",
+			"CC-BY-ND",
+			"CC-BY-SA",
+			"None"
+		);
+	}
+	
+	private function getLanguages()
+	{
+		return array(
+			'ab' => 'Abkhazian',
+			'aa' => 'Afar',
+			'af' => 'Afrikaans',
+			'ak' => 'Akan',
+			'sq' => 'Albanian',
+			'am' => 'Amharic',
+			'ar' => 'Arabic',
+			'an' => 'Aragonese',
+			'hy' => 'Armenian',
+			'as' => 'Assamese',
+			'av' => 'Avaric',
+			'ae' => 'Avestan',
+			'ay' => 'Aymara',
+			'az' => 'Azerbaijani',
+			'bm' => 'Bambara',
+			'ba' => 'Bashkir',
+			'eu' => 'Basque',
+			'be' => 'Belarusian',
+			'bn' => 'Bengali',
+			'bh' => 'Bihari languages',
+			'bi' => 'Bislama',
+			'bs' => 'Bosnian',
+			'br' => 'Breton',
+			'bg' => 'Bulgarian',
+			'my' => 'Burmese',
+			'ca' => 'Catalan, Valencian',
+			'km' => 'Central Khmer',
+			'ch' => 'Chamorro',
+			'ce' => 'Chechen',
+			'ny' => 'Chichewa, Chewa, Nyanja',
+			'zh' => 'Chinese',
+			'cu' => 'Church Slavonic, Old Bulgarian, Old Church Slavonic',
+			'cv' => 'Chuvash',
+			'kw' => 'Cornish',
+			'co' => 'Corsican',
+			'cr' => 'Cree',
+			'hr' => 'Croatian',
+			'cs' => 'Czech',
+			'da' => 'Danish',
+			'dv' => 'Divehi, Dhivehi, Maldivian',
+			'nl' => 'Dutch, Flemish',
+			'dz' => 'Dzongkha',
+			'en' => 'English',
+			'eo' => 'Esperanto',
+			'et' => 'Estonian',
+			'ee' => 'Ewe',
+			'fo' => 'Faroese',
+			'fj' => 'Fijian',
+			'fi' => 'Finnish',
+			'fr' => 'French',
+			'ff' => 'Fulah',
+			'gd' => 'Gaelic, Scottish Gaelic',
+			'gl' => 'Galician',
+			'lg' => 'Ganda',
+			'ka' => 'Georgian',
+			'de' => 'German',
+			'ki' => 'Gikuyu, Kikuyu',
+			'el' => 'Greek (Modern)',
+			'kl' => 'Greenlandic, Kalaallisut',
+			'gn' => 'Guarani',
+			'gu' => 'Gujarati',
+			'ht' => 'Haitian, Haitian Creole',
+			'ha' => 'Hausa',
+			'he' => 'Hebrew',
+			'hz' => 'Herero',
+			'hi' => 'Hindi',
+			'ho' => 'Hiri Motu',
+			'hu' => 'Hungarian',
+			'is' => 'Icelandic',
+			'io' => 'Ido',
+			'ig' => 'Igbo',
+			'id' => 'Indonesian',
+			'ia' => 'Interlingua (International Auxiliary Language Association)',
+			'ie' => 'Interlingue',
+			'iu' => 'Inuktitut',
+			'ik' => 'Inupiaq',
+			'ga' => 'Irish',
+			'it' => 'Italian',
+			'ja' => 'Japanese',
+			'jv' => 'Javanese',
+			'kn' => 'Kannada',
+			'kr' => 'Kanuri',
+			'ks' => 'Kashmiri',
+			'kk' => 'Kazakh',
+			'rw' => 'Kinyarwanda',
+			'kv' => 'Komi',
+			'kg' => 'Kongo',
+			'ko' => 'Korean',
+			'kj' => 'Kwanyama, Kuanyama',
+			'ku' => 'Kurdish',
+			'ky' => 'Kyrgyz',
+			'lo' => 'Lao',
+			'la' => 'Latin',
+			'lv' => 'Latvian',
+			'lb' => 'Letzeburgesch, Luxembourgish',
+			'li' => 'Limburgish, Limburgan, Limburger',
+			'ln' => 'Lingala',
+			'lt' => 'Lithuanian',
+			'lu' => 'Luba-Katanga',
+			'mk' => 'Macedonian',
+			'mg' => 'Malagasy',
+			'ms' => 'Malay',
+			'ml' => 'Malayalam',
+			'mt' => 'Maltese',
+			'gv' => 'Manx',
+			'mi' => 'Maori',
+			'mr' => 'Marathi',
+			'mh' => 'Marshallese',
+			'ro' => 'Moldovan, Moldavian, Romanian',
+			'mn' => 'Mongolian',
+			'na' => 'Nauru',
+			'nv' => 'Navajo, Navaho',
+			'nd' => 'Northern Ndebele',
+			'ng' => 'Ndonga',
+			'ne' => 'Nepali',
+			'se' => 'Northern Sami',
+			'no' => 'Norwegian',
+			'nb' => 'Norwegian Bokmål',
+			'nn' => 'Norwegian Nynorsk',
+			'ii' => 'Nuosu, Sichuan Yi',
+			'oc' => 'Occitan (post 1500)',
+			'oj' => 'Ojibwa',
+			'or' => 'Oriya',
+			'om' => 'Oromo',
+			'os' => 'Ossetian, Ossetic',
+			'pi' => 'Pali',
+			'pa' => 'Panjabi, Punjabi',
+			'ps' => 'Pashto, Pushto',
+			'fa' => 'Persian',
+			'pl' => 'Polish',
+			'pt' => 'Portuguese',
+			'qu' => 'Quechua',
+			'rm' => 'Romansh',
+			'rn' => 'Rundi',
+			'ru' => 'Russian',
+			'sm' => 'Samoan',
+			'sg' => 'Sango',
+			'sa' => 'Sanskrit',
+			'sc' => 'Sardinian',
+			'sr' => 'Serbian',
+			'sn' => 'Shona',
+			'sd' => 'Sindhi',
+			'si' => 'Sinhala, Sinhalese',
+			'sk' => 'Slovak',
+			'sl' => 'Slovenian',
+			'so' => 'Somali',
+			'st' => 'Sotho, Southern',
+			'nr' => 'South Ndebele',
+			'es' => 'Spanish, Castilian',
+			'su' => 'Sundanese',
+			'sw' => 'Swahili',
+			'ss' => 'Swati',
+			'sv' => 'Swedish',
+			'tl' => 'Tagalog',
+			'ty' => 'Tahitian',
+			'tg' => 'Tajik',
+			'ta' => 'Tamil',
+			'tt' => 'Tatar',
+			'te' => 'Telugu',
+			'th' => 'Thai',
+			'bo' => 'Tibetan',
+			'ti' => 'Tigrinya',
+			'to' => 'Tonga (Tonga Islands)',
+			'ts' => 'Tsonga',
+			'tn' => 'Tswana',
+			'tr' => 'Turkish',
+			'tk' => 'Turkmen',
+			'tw' => 'Twi',
+			'ug' => 'Uighur, Uyghur',
+			'uk' => 'Ukrainian',
+			'ur' => 'Urdu',
+			'uz' => 'Uzbek',
+			've' => 'Venda',
+			'vi' => 'Vietnamese',
+			'vo' => 'Volap_k',
+			'wa' => 'Walloon',
+			'cy' => 'Welsh',
+			'fy' => 'Western Frisian',
+			'wo' => 'Wolof',
+			'xh' => 'Xhosa',
+			'yi' => 'Yiddish',
+			'yo' => 'Yoruba',
+			'za' => 'Zhuang, Chuang',
+			'zu' => 'Zulu'
+		);
+	}
+}

+ 38 - 410
system/Controllers/SetupController.php

@@ -3,441 +3,69 @@
 namespace Typemill\Controllers;
 
 use \Symfony\Component\Yaml\Yaml;
-use Typemill\Models\Field;
 use Typemill\Models\Validation;
+use Typemill\Models\User;
+use Typemill\Models\Write;
 
 class SetupController extends Controller
 {
-	public function setup($request, $response, $args)
+	public function show($request, $response, $args)
 	{
-		$settings 	= $this->c->get('settings');		
-		$themes 	= $this->getThemes();
-		$copyright	= $this->getCopyright();
-		$languages	= $this->getLanguages();
-		$locale		= explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']);
-		$locale		= $locale[0];
+		/* make some checks befor you install */
+		$checkFolder = new Write();
 		
-		$plugins	= array();
-		$fields 	= array();
+		$systemcheck = array();
 		
-		/* iterate through the plugins in the stored user settings */
-		foreach($settings['plugins'] as $pluginName => $pluginUserSettings)
-		{
-			/* add plugin to plugin Data */
-			$plugins[$pluginName] = Null;
-			
-			/* Check if the user has deleted a plugin. Then delete it in the settings and store the updated settings. */
-			if(!is_dir($settings['rootPath'] . 'plugins' . DIRECTORY_SEPARATOR . $pluginName))
-			{
-				/* remove the plugin settings and store updated settings */
-				\Typemill\Settings::removePluginSettings($pluginName);
-				continue;
-			}
+		try{ $checkFolder->checkPath('settings'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
+		try{ $checkFolder->checkPath('settings/users'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
+		try{ $checkFolder->checkPath('content'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
+		try{ $checkFolder->checkPath('cache'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
 
-			/* load the original plugin definitions from the plugin folder (author, version and stuff) */
-			$pluginOriginalSettings = \Typemill\Settings::getPluginSettings($pluginName);
-			if($pluginOriginalSettings)
-			{
-				/* store them as default plugin data with plugin author, plugin year, default settings and field-definitions */
-				$plugins[$pluginName] = $pluginOriginalSettings;
-			}
-						
-			/* overwrite the original plugin settings with the stored user settings, if they exist */
-			if($pluginUserSettings)
-			{
-				$plugins[$pluginName]['settings'] = $pluginUserSettings;
-			}
-			
-			/* check, if the plugin has been disabled in the form-session-data */
-			/* TODO: Works only, if there is at least one plugin with settings */
-			if(isset($_SESSION['old']) && !isset($_SESSION['old'][$pluginName]['active']))
-			{
-				$plugins[$pluginName]['settings']['active'] = false;
-			}
-			
-			/* if the plugin defines forms and fields, so that the user can edit the plugin settings in the frontend */
-			if(isset($pluginOriginalSettings['forms']))
-			{
-				$fields = array();
-				
-				/* then iterate through the fields */
-				foreach($pluginOriginalSettings['forms']['fields'] as $fieldName => $fieldConfigs)
-				{
-					/* and create a new field object with the field name and the field configurations. */
-					$field = new Field($fieldName, $fieldConfigs);
-					
-					/* now you have the configurations of the field. Time to set the values */
-					
-					/* At first, get the value for the field from the stored user settings */
-					// $userValue = isset($pluginUserSettings[$fieldName]) ? $pluginUserSettings[$fieldName] : NULL;
-					$userValue = isset($plugins[$pluginName]['settings'][$fieldName]) ? $plugins[$pluginName]['settings'][$fieldName] : NULL;
-					
-					/* Then overwrite the value, if there are old input values for the field in the session */
-					$userValue = isset($_SESSION['old'][$pluginName][$fieldName]) ? $_SESSION['old'][$pluginName][$fieldName] : $userValue;
-					
-					if($field->getType() == "textarea")
-					{
-						if($userValue)
-						{
-							$field->setContent($userValue);
-						}
-					}
-					elseIf($field->getType() != "checkbox")
-					{
-						$field->setAttributeValue('value', $userValue);	
-					}
+		$systemcheck = empty($systemcheck) ? false : $systemcheck;
 
-					/* add the field to the field-List with the plugin-name as key */
-					$fields[] = $field;
-				}
-				/* overwrite original plugin form definitions with enhanced form objects */
-				$plugins[$pluginName]['forms']['fields'] = $fields;
-			}
-		}
-		$this->c->view->render($response, '/setup.twig', array('settings' => $settings, 'themes' => $themes,'copyright' => $copyright,'plugins' => $plugins, 'languages' => $languages, 'locale' => $locale));
+		return $this->render($response, 'auth/setup.twig', array( 'messages' => $systemcheck ));
 	}
 
-	public function save($request, $response, $args)
+	public function create($request, $response, $args)
 	{
 		if($request->isPost())
 		{
-			$settings 		= $this->c->get('settings');
-			$pluginSettings	= array();
 			$params 		= $request->getParams();
 			$validate		= new Validation();
-		
-			/* extract the settings for the basic application and validate them */
-			$appSettings	= isset($params['settings']) ? $params['settings'] : false;
-			if($appSettings)
-			{
-				$copyright 					= $this->getCopyright();
-				$themes 					= $this->getThemes();
-				$appSettings['startpage'] 	= isset($appSettings['startpage']) ? true : false;
-				
-				$validate->settings($appSettings, $themes, $copyright, 'settings');
-			}
-			
-			$themeSettings	= isset($params['themesettings']) ? $params['themesettings'] : false;
-			if($themeSettings)
-			{
-				// load theme definitions by theme name
-				$themeOriginalSettings = \Typemill\Settings::getThemeSettings($appSettings['theme']);
+			$user			= new User();
 
-				// validate input with field definitions				
-				if($themeOriginalSettings)
-				{
-					foreach($themeSettings as $fieldName => $fieldValue)
-					{
-						$fieldDefinition = isset($themeOriginalSettings['forms']['fields'][$fieldName]) ? $themeOriginalSettings['forms']['fields'][$fieldName] : false;
-						if($fieldDefinition)
-						{
-							/* validate user input for this field */
-							$validate->pluginField($fieldName, $fieldValue, $appSettings['theme'], $fieldDefinition);
-						}						
-					}
-				}
-				$appSettings['themesettings'] = $themeSettings;
-			}
+			/* set user as admin */
+			$params['userrole'] = 'administrator';
+			
+			/* get userroles for validation */
+			$userroles		= $user->getUserroles();
 			
-			/* use the stored user settings and iterate over all original plugin settings, so we do not forget any... */
-			foreach($settings['plugins'] as $pluginName => $pluginUserSettings)
+			/* validate user */
+			if($validate->newUser($params, $userroles))
 			{
-				/* if there are no input-data for this plugin, then use the stored plugin settings */
-				if(!isset($params[$pluginName]))
-				{
-					$pluginSettings[$pluginName] = $pluginUserSettings;
-				}
-				else
-				{
-					/* now fetch the original plugin settings from the plugin folder to get the field definitions */
-					$pluginOriginalSettings = \Typemill\Settings::getPluginSettings($pluginName);
-					
-					if($pluginOriginalSettings)
-					{
-						/* take the user input data and iterate over all fields and values */
-						foreach($params[$pluginName] as $fieldName => $fieldValue)
-						{
-							/* get the corresponding field definition from original plugin settings */
-							$fieldDefinition = isset($pluginOriginalSettings['forms']['fields'][$fieldName]) ? $pluginOriginalSettings['forms']['fields'][$fieldName] : false;
-							if($fieldDefinition)
-							{
-								/* validate user input for this field */
-								$validate->pluginField($fieldName, $fieldValue, $pluginName, $fieldDefinition);
-							}
-						}
-					}
-					
-					/* use the input data */
-					$pluginSettings[$pluginName] = $params[$pluginName];
-				}
+				$userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']);
 				
-				/* deactivate the plugin, if there is no active flag */					
-				if(!isset($params[$pluginName]['active']))
+				/* create initial user */
+				$username = $user->createUser($userdata);
+				
+				if($username)
 				{
-					$pluginSettings[$pluginName]['active'] = false;
-				}
-			}
-			
-			if(!is_writable($this->c->get('settings')['settingsPath']))
-			{
-				$_SESSION['errors']['folder'] = 'Your settings-folder is not writable';
-			}
+					/* login user */
+					$user->login($username);
 
-			if(isset($_SESSION['errors']))
-			{
-				return $response->withRedirect($this->c->router->pathFor('setup'));
-			}
-			
-			/* if everything is valid, add plugin settings to base settings again */
-			$appSettings['plugins'] = $pluginSettings;
-						
-			/* store updated settings */
-			\Typemill\Settings::updateSettings($appSettings);
-			
-			unset($_SESSION['old']);
-			
-			$this->c->view->render($response, '/welcome.twig', $appSettings);
-		}
-	}
-		
-	private function getCopyright()
-	{
-		return array(
-			"©",
-			"CC-BY",
-			"CC-BY-NC",
-			"CC-BY-NC-ND",
-			"CC-BY-NC-SA",
-			"CC-BY-ND",
-			"CC-BY-SA",
-			"None"
-		);
-	}
-	
-	private function getLanguages()
-	{
-		return array(
-			'ab' => 'Abkhazian',
-			'aa' => 'Afar',
-			'af' => 'Afrikaans',
-			'ak' => 'Akan',
-			'sq' => 'Albanian',
-			'am' => 'Amharic',
-			'ar' => 'Arabic',
-			'an' => 'Aragonese',
-			'hy' => 'Armenian',
-			'as' => 'Assamese',
-			'av' => 'Avaric',
-			'ae' => 'Avestan',
-			'ay' => 'Aymara',
-			'az' => 'Azerbaijani',
-			'bm' => 'Bambara',
-			'ba' => 'Bashkir',
-			'eu' => 'Basque',
-			'be' => 'Belarusian',
-			'bn' => 'Bengali',
-			'bh' => 'Bihari languages',
-			'bi' => 'Bislama',
-			'bs' => 'Bosnian',
-			'br' => 'Breton',
-			'bg' => 'Bulgarian',
-			'my' => 'Burmese',
-			'ca' => 'Catalan, Valencian',
-			'km' => 'Central Khmer',
-			'ch' => 'Chamorro',
-			'ce' => 'Chechen',
-			'ny' => 'Chichewa, Chewa, Nyanja',
-			'zh' => 'Chinese',
-			'cu' => 'Church Slavonic, Old Bulgarian, Old Church Slavonic',
-			'cv' => 'Chuvash',
-			'kw' => 'Cornish',
-			'co' => 'Corsican',
-			'cr' => 'Cree',
-			'hr' => 'Croatian',
-			'cs' => 'Czech',
-			'da' => 'Danish',
-			'dv' => 'Divehi, Dhivehi, Maldivian',
-			'nl' => 'Dutch, Flemish',
-			'dz' => 'Dzongkha',
-			'en' => 'English',
-			'eo' => 'Esperanto',
-			'et' => 'Estonian',
-			'ee' => 'Ewe',
-			'fo' => 'Faroese',
-			'fj' => 'Fijian',
-			'fi' => 'Finnish',
-			'fr' => 'French',
-			'ff' => 'Fulah',
-			'gd' => 'Gaelic, Scottish Gaelic',
-			'gl' => 'Galician',
-			'lg' => 'Ganda',
-			'ka' => 'Georgian',
-			'de' => 'German',
-			'ki' => 'Gikuyu, Kikuyu',
-			'el' => 'Greek (Modern)',
-			'kl' => 'Greenlandic, Kalaallisut',
-			'gn' => 'Guarani',
-			'gu' => 'Gujarati',
-			'ht' => 'Haitian, Haitian Creole',
-			'ha' => 'Hausa',
-			'he' => 'Hebrew',
-			'hz' => 'Herero',
-			'hi' => 'Hindi',
-			'ho' => 'Hiri Motu',
-			'hu' => 'Hungarian',
-			'is' => 'Icelandic',
-			'io' => 'Ido',
-			'ig' => 'Igbo',
-			'id' => 'Indonesian',
-			'ia' => 'Interlingua (International Auxiliary Language Association)',
-			'ie' => 'Interlingue',
-			'iu' => 'Inuktitut',
-			'ik' => 'Inupiaq',
-			'ga' => 'Irish',
-			'it' => 'Italian',
-			'ja' => 'Japanese',
-			'jv' => 'Javanese',
-			'kn' => 'Kannada',
-			'kr' => 'Kanuri',
-			'ks' => 'Kashmiri',
-			'kk' => 'Kazakh',
-			'rw' => 'Kinyarwanda',
-			'kv' => 'Komi',
-			'kg' => 'Kongo',
-			'ko' => 'Korean',
-			'kj' => 'Kwanyama, Kuanyama',
-			'ku' => 'Kurdish',
-			'ky' => 'Kyrgyz',
-			'lo' => 'Lao',
-			'la' => 'Latin',
-			'lv' => 'Latvian',
-			'lb' => 'Letzeburgesch, Luxembourgish',
-			'li' => 'Limburgish, Limburgan, Limburger',
-			'ln' => 'Lingala',
-			'lt' => 'Lithuanian',
-			'lu' => 'Luba-Katanga',
-			'mk' => 'Macedonian',
-			'mg' => 'Malagasy',
-			'ms' => 'Malay',
-			'ml' => 'Malayalam',
-			'mt' => 'Maltese',
-			'gv' => 'Manx',
-			'mi' => 'Maori',
-			'mr' => 'Marathi',
-			'mh' => 'Marshallese',
-			'ro' => 'Moldovan, Moldavian, Romanian',
-			'mn' => 'Mongolian',
-			'na' => 'Nauru',
-			'nv' => 'Navajo, Navaho',
-			'nd' => 'Northern Ndebele',
-			'ng' => 'Ndonga',
-			'ne' => 'Nepali',
-			'se' => 'Northern Sami',
-			'no' => 'Norwegian',
-			'nb' => 'Norwegian Bokmål',
-			'nn' => 'Norwegian Nynorsk',
-			'ii' => 'Nuosu, Sichuan Yi',
-			'oc' => 'Occitan (post 1500)',
-			'oj' => 'Ojibwa',
-			'or' => 'Oriya',
-			'om' => 'Oromo',
-			'os' => 'Ossetian, Ossetic',
-			'pi' => 'Pali',
-			'pa' => 'Panjabi, Punjabi',
-			'ps' => 'Pashto, Pushto',
-			'fa' => 'Persian',
-			'pl' => 'Polish',
-			'pt' => 'Portuguese',
-			'qu' => 'Quechua',
-			'rm' => 'Romansh',
-			'rn' => 'Rundi',
-			'ru' => 'Russian',
-			'sm' => 'Samoan',
-			'sg' => 'Sango',
-			'sa' => 'Sanskrit',
-			'sc' => 'Sardinian',
-			'sr' => 'Serbian',
-			'sn' => 'Shona',
-			'sd' => 'Sindhi',
-			'si' => 'Sinhala, Sinhalese',
-			'sk' => 'Slovak',
-			'sl' => 'Slovenian',
-			'so' => 'Somali',
-			'st' => 'Sotho, Southern',
-			'nr' => 'South Ndebele',
-			'es' => 'Spanish, Castilian',
-			'su' => 'Sundanese',
-			'sw' => 'Swahili',
-			'ss' => 'Swati',
-			'sv' => 'Swedish',
-			'tl' => 'Tagalog',
-			'ty' => 'Tahitian',
-			'tg' => 'Tajik',
-			'ta' => 'Tamil',
-			'tt' => 'Tatar',
-			'te' => 'Telugu',
-			'th' => 'Thai',
-			'bo' => 'Tibetan',
-			'ti' => 'Tigrinya',
-			'to' => 'Tonga (Tonga Islands)',
-			'ts' => 'Tsonga',
-			'tn' => 'Tswana',
-			'tr' => 'Turkish',
-			'tk' => 'Turkmen',
-			'tw' => 'Twi',
-			'ug' => 'Uighur, Uyghur',
-			'uk' => 'Ukrainian',
-			'ur' => 'Urdu',
-			'uz' => 'Uzbek',
-			've' => 'Venda',
-			'vi' => 'Vietnamese',
-			'vo' => 'Volap_k',
-			'wa' => 'Walloon',
-			'cy' => 'Welsh',
-			'fy' => 'Western Frisian',
-			'wo' => 'Wolof',
-			'xh' => 'Xhosa',
-			'yi' => 'Yiddish',
-			'yo' => 'Yoruba',
-			'za' => 'Zhuang, Chuang',
-			'zu' => 'Zulu'
-		);
-	}
-	
-	private function getThemes()
-	{
-		$themeFolder 	= $this->c->get('settings')['rootPath'] . $this->c->get('settings')['themeFolder'];
-		$themeFolderC 	= scandir($themeFolder);
-		$themes 		= array();
-		foreach ($themeFolderC as $key => $theme)
-		{
-			if (!in_array($theme, array(".","..")))
-			{
-				if (is_dir($themeFolder . DIRECTORY_SEPARATOR . $theme))
-				{
-					$themes[] = $theme;
+					/* store updated settings */
+					$settings = $this->c->get('settings');
+					$settings->replace(['setup' => false]);
+									
+					/* store updated settings */
+					\Typemill\Settings::updateSettings(array('setup' => false));
+
+					return $this->render($response, 'auth/welcome.twig', array());
 				}
 			}
-		}
-		return $themes;
-	}
-
-	public function themes($request, $response)
-	{
-		/* Extract the parameters from get-call */
-		$params 	= $request->getParams();
-		$theme	 	= isset($params['theme']) ? $params['theme'] : false;
-
-		if($theme && preg_match('/^[A-Za-z0-9 _\-\+]+$/', $theme))
-		{
-			$themeSettings = \Typemill\Settings::getThemeSettings($theme);
 			
-			if($themeSettings)
-			{
-				return $response->withJson($themeSettings, 200);
-			}
+			$this->c->flash->addMessage('error', 'Please check your input and try again');
+			return $response->withRedirect($this->c->router->pathFor('setup.show'));
 		}
-		
-		return $response->withJson(['error' => 'no data found'], 404);		
 	}
 }

+ 5 - 7
system/Extensions/ParsedownExtension.php

@@ -15,7 +15,7 @@ class ParsedownExtension extends \ParsedownExtra
     {
         # make sure no definitions are set
         $this->DefinitionData = array();
-				
+
         # standardize line breaks
         $text = str_replace(array("\r\n", "\r"), "\n", $text);
 
@@ -38,27 +38,25 @@ class ParsedownExtension extends \ParsedownExtra
 		
         # trim line breaks
         $markup = trim($markup, "\n");
-		
-        if (isset($this->DefinitionData['TableOfContents']))
+
+        if(isset($this->DefinitionData['TableOfContents']))
         {
 			$TOC = $this->buildTOC($this->headlines);
 			
 			$markup = preg_replace('%(<p[^>]*>\[TOC\]</p>)%i', $TOC, $markup);
         }
-
+		
         # merge consecutive dl elements
-
         $markup = preg_replace('/<\/dl>\s+<dl>\s+/', '', $markup);
 
         # add footnotes
-		
         if (isset($this->DefinitionData['Footnote']))
         {
             $Element = $this->buildFootnoteElement();
 
             $markup .= "\n" . $this->element($Element);
         }
-				
+			
         return $markup;
     }
 		

+ 27 - 0
system/Middleware/RedirectIfAuthenticated.php

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

+ 27 - 0
system/Middleware/RedirectIfUnauthenticated.php

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

+ 86 - 0
system/Models/User.php

@@ -0,0 +1,86 @@
+<?php
+
+namespace Typemill\Models;
+
+class User extends WriteYaml
+{
+	public function getUsers()
+	{
+		$userDir = __DIR__ . '/../../settings/users';
+				
+		/* check if plugins directory exists */
+		if(!is_dir($userDir)){ return array(); }
+		
+		/* get all plugins folder */
+		$users = array_diff(scandir($userDir), array('..', '.'));
+		
+		$cleanUser	= array();
+		foreach($users as $key => $user)
+		{
+			$cleanUser[] = str_replace('.yaml', '', $user);
+		}
+		return $cleanUser;
+	}
+	
+	public function getUser($username)
+	{
+		$user = $this->getYaml('settings/users', $username . '.yaml');
+		return $user;
+	}
+	
+	public function createUser($params)
+	{		
+		$userdata = array(
+						'username' 	=> $params['username'],
+						'email'		=> $params['email'],
+						'password'	=> $this->generatePassword($params['password']),
+						'userrole' 	=> $params['userrole']
+					);
+	
+		if($this->updateYaml('settings/users', $userdata['username'] . '.yaml', $userdata))
+		{
+			return $userdata['username'];
+		}
+		return false;
+	}
+	
+	public function updateUser($params)
+	{
+		$userdata = $this->getUser($params['username']);
+		
+		if(isset($params['password']))
+		{
+			$params['password'] = $this->generatePassword($params['password']);
+		}
+		
+		$update = array_merge($userdata, $params);
+		
+		$this->updateYaml('settings/users', $userdata['username'] . '.yaml', $update);
+		
+		return $userdata['username'];		
+	}
+	
+	public function deleteUser($username)
+	{
+		if($this->getUser($username))
+		{
+			unlink('settings/users/' . $username . '.yaml');
+		}
+	}
+	
+	public function getUserroles()
+	{
+		return array('administrator', 'editor');
+	}	
+	
+	public function login($username)
+	{
+		$_SESSION['user'] = $username;
+		$_SESSION['login'] = true;
+	}
+	
+	public function generatePassword($password)
+	{
+		return \password_hash($password, PASSWORD_DEFAULT, ['cost' => 10]);
+	}	
+}

+ 61 - 88
system/Models/Validation.php

@@ -2,6 +2,7 @@
 
 namespace Typemill\Models;
 
+use Typemill\Models\User;
 use Valitron\Validator;
 
 class Validation
@@ -13,10 +14,33 @@ class Validation
 	*/
 
 	public function __construct()
-	{		
+	{
+		$user = new User();
+		
 		Validator::langDir(__DIR__.'/../vendor/vlucas/valitron/lang'); // always set langDir before lang.
 		Validator::lang('en');
 
+		Validator::addRule('userAvailable', function($field, $value, array $params, array $fields) use ($user)
+		{
+			$userdata = $user->getUser($value);
+			if($userdata){ return false; }
+			return true;
+		}, 'taken');
+
+		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('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('emailAvailable', function($field, $value, array $params, array $fields)
 		{
 			$email = 'testmail@gmail.com';
@@ -30,19 +54,6 @@ class Validation
 			if(!$email){ return false; }
 			return true;
 		}, 'unknown');
-
-		Validator::addRule('userAvailable', function($field, $value, array $params, array $fields)
-		{
-			$username = 'trendschau';
-			if($username){ return false; }
-			return true;
-		}, 'taken');
-		
-		Validator::addRule('checkPassword', function($field, $value, array $params, array $fields)
-		{
-			if(password_verify($value, $fields['user_password'])){ return true; }
-			return false;
-		}, 'not valid');
 		
 		Validator::addRule('noHTML', function($field, $value, array $params, array $fields)
 		{
@@ -61,13 +72,13 @@ class Validation
 	* @return obj $v the validation object passed to a result method.
 	*/
 	
-	public function validateSignin(array $params)
+	public function signin(array $params)
 	{
 		$v = new Validator($params);
-		$v->rule('required', ['username', 'password'])->message("notwendig");
-		$v->rule('alphaNum', 'username')->message("ungültig");
-		$v->rule('lengthBetween', 'password', 5, 20)->message("Länge 5 - 20");
-		$v->rule('lengthBetween', 'username', 5, 20)->message("Länge 5 - 20");
+		$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())
 		{
@@ -86,59 +97,55 @@ class Validation
 	* @return obj $v the validation object passed to a result method.
 	*/
 	
-	public function validateSignup(array $params)
-	{		
+	public function newUser(array $params, $userroles)
+	{
 		$v = new Validator($params);
-		$v->rule('required', ['signup_username', 'signup_email', 'signup_password'])->message("notwendig");
-		$v->rule('alphaNum', 'signup_username')->message("ungültig");
-		$v->rule('lengthBetween', 'signup_password', 5, 20)->message("Länge 5 - 20");
-		$v->rule('lengthBetween', 'signup_username', 5, 20)->message("Länge 5 - 20"); 
-		$v->rule('userAvailable', 'signup_username')->message("vergeben");
-		$v->rule('email', 'signup_email')->message("ungültig");
-		$v->rule('emailAvailable', 'signup_email')->message("vergeben");
+		$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('email', 'email')->message("e-mail is invalid");
+		$v->rule('in', 'userrole', $userroles);
 		
 		return $this->validationResult($v);
 	}
-
-	public function validateReset(array $params)
+	
+	public function existingUser(array $params, $userroles)
 	{
 		$v = new Validator($params);
-		$v->rule('required', 'email')->message("notwendig");
-		$v->rule('email', 'email')->message("ungültig");
-		$v->rule('emailKnown', 'email')->message("unbekannt");
-		
-		return $this->validationResult($v);
-	}
+		$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('email', 'email')->message("e-mail is invalid");
+		$v->rule('in', 'userrole', $userroles);
 
-	/**
-	* validation for changing the password
-	* 
-	* @param array $params with form data.
-	* @return obj $v the validation object passed to a result method.
-	*/
+		return $this->validationResult($v);		
+	}
 	
-	public function validatePassword(array $params)
+	public function username($username)
 	{
-		$v = new Validator($params);
-		$v->rule('required', ['password', 'password_old']);
-		$v->rule('lengthBetween', 'password', 5, 50);
-		$v->rule('checkPassword', 'password_old');
-		
+		$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 password reset
+	* validation for changing the password
 	* 
 	* @param array $params with form data.
 	* @return obj $v the validation object passed to a result method.
 	*/
 	
-	public function validateResetPassword(array $params)
+	public function newPassword(array $params)
 	{
 		$v = new Validator($params);
-		$v->rule('required', ['password', 'username']);
-		$v->rule(['lengthBetween' => [['password', 5, 50], ['username', 3,20]]]);
+		$v->rule('required', ['password', 'newpassword']);
+		$v->rule('lengthBetween', 'newpassword', 5, 20);
+		$v->rule('checkPassword', 'password')->message("Password is wrong");
 		
 		return $this->validationResult($v);
 	}
@@ -150,11 +157,11 @@ class Validation
 	* @return obj $v the validation object passed to a result method.
 	*/
 
-	public function settings(array $params, array $themes, array $copyright, $name = false)
+	public function settings(array $params, array $copyright, $name = false)
 	{
 		$v = new Validator($params);
 		
-		$v->rule('required', ['title', 'author', 'copyright', 'year', 'theme']);
+		$v->rule('required', ['title', 'author', 'copyright', 'year']);
 		$v->rule('lengthBetween', 'title', 2, 20);
 		$v->rule('lengthBetween', 'author', 2, 40);
 		$v->rule('regex', 'title', '/^[\pL0-9_ \-]*$/u');
@@ -162,7 +169,6 @@ class Validation
 		$v->rule('integer', 'year');
 		$v->rule('length', 'year', 4);
 		$v->rule('in', 'copyright', $copyright);
-		$v->rule('in', 'theme', $themes);
 		
 		return $this->validationResult($v, $name);
 	}
@@ -217,39 +223,6 @@ class Validation
 		return $this->validationResult($v, $pluginName);
 	}
 	
-	/**
-	* validation for election input
-	* 
-	* @param array $params with form data.
-	* @return obj $v the validation object passed to a result method.
-	*/
-
-	public function validateShowroom(array $params)
-	{
-		$v = new Validator($params);
-		$v->rule('required', ['title']);
-		$v->rule('lengthBetween', 'title', 2, 50);
-		$v->rule('regex', 'title', '/^[^-_\-\s][0-9A-Za-zÄäÜüÖöß_ \-]+$/');
-		$v->rule('integer', 'election' );
-		$v->rule('email', 'email');
-		$v->rule('length', 'invite', 40);
-		$v->rule('alphaNum', 'invite');
-		$v->rule('required', ['group_id', 'politician_id', 'ressort_id']);
-		$v->rule('integer', ['group_id', 'politician_id', 'ressort_id']);
-		
-		return $this->validationResult($v);
-	}
-
-
-	public function validateGroup(array $params)
-	{
-//		$v->rule('date', 'deadline');
-//		$v->rule('dateAfter', 'deadline', new \DateTime('now'));
-//		$v->rule('dateBefore', 'deadline', new \DateTime($params['election_date']));
-
-		return $this->validationResult($v);
-	}
-		
 	/**
 	* result for validation
 	* 

+ 30 - 13
system/Models/Write.php

@@ -11,27 +11,38 @@ class Write
 		$basePath			= getcwd() . DIRECTORY_SEPARATOR;
 		$this->basePath 	= $basePath;
 	}
-		
-	protected function checkPath($folder)
+
+	public function checkPath($folder)
 	{
 		$folderPath = $this->basePath . $folder;
 		
-		if(!is_dir($folderPath) AND !mkdir($folderPath, 0774, true))
+		if(!is_dir($folderPath))
 		{
-			throw new Exception("The folder '{$folder}' is missing and we could not create it. Please create the folder manually on your server.");
-			return false;
+			if(@mkdir($folderPath, 0774, true))
+			{
+				return true;
+			}
+			else
+			{
+				throw new \Exception("The folder '{$folder}' is missing and we could not create it. Please create the folder manually on your server.");
+				return false;				
+			}
 		}
 		
-		if(!is_writable($folderPath))
+		if(@is_writable($folderPath))
+		{
+			return true;
+		}
+		else
 		{
-			throw new Exception("Please make the folder '{$folder}' writable.");
+			throw new \Exception("Please make the folder '{$folder}' writable.");
 			return false;
 		}
 		return true;
 	}
 	
 	protected function checkFile($folder, $file)
-	{		
+	{
 		if(!file_exists($this->basePath . $folder . DIRECTORY_SEPARATOR . $file))
 		{
 			return false;
@@ -41,11 +52,17 @@ class Write
 
 	protected function writeFile($folder, $file, $data)
 	{
-		$filePath 	= $this->basePath . $folder . DIRECTORY_SEPARATOR . $file;
-		$openFile 	= fopen($filePath, "w");
-		
-		fwrite($openFile, $data);
-		fclose($openFile);
+		if($this->checkPath($folder))
+		{
+			$filePath 	= $this->basePath . $folder . DIRECTORY_SEPARATOR . $file;
+			$openFile 	= fopen($filePath, "w");
+			
+			fwrite($openFile, $data);
+			fclose($openFile);
+			
+			return true;
+		}
+		return false;
 	}
 	
 	public function getFile($folderName, $fileName)

+ 6 - 2
system/Models/WriteYaml.php

@@ -12,7 +12,7 @@ class WriteYaml extends Write
 	public function getYaml($folderName, $yamlFileName)
 	{
 		$yaml = $this->getFile($folderName, $yamlFileName);
-		
+			
 		if($yaml)
 		{
 			return \Symfony\Component\Yaml\Yaml::parse($yaml);
@@ -29,6 +29,10 @@ class WriteYaml extends Write
 	public function updateYaml($folderName, $yamlFileName, $contentArray)
 	{
 		$yaml = \Symfony\Component\Yaml\Yaml::dump($contentArray,6);
-		$this->writeFile($folderName, $yamlFileName, $yaml);
+		if($this->writeFile($folderName, $yamlFileName, $yaml))
+		{
+			return true;
+		}
+		return false;
 	}
 }

+ 2 - 2
system/Routes/Api.php

@@ -1,5 +1,5 @@
 <?php
 
-use Typemill\Controllers\SetupController;
+use Typemill\Controllers\SettingsController;
 
-$app->get('/api/v1/themes', SetupController::class . ':themes')->setName('themes');
+$app->get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes');

+ 31 - 4
system/Routes/Web.php

@@ -2,12 +2,39 @@
 
 use Typemill\Controllers\PageController;
 use Typemill\Controllers\SetupController;
+use Typemill\Controllers\AuthController;
+use Typemill\Controllers\SettingsController;
+use Typemill\Controllers\ContentController;
+use Typemill\Middleware\RedirectIfUnauthenticated;
+use Typemill\Middleware\RedirectIfAuthenticated;
 
 if($settings['settings']['setup'])
 {
-	$app->get('/setup', SetupController::class . ':setup')->setName('setup');
-	$app->post('/setup', SetupController::class . ':save')->setName('save');
+	$app->get('/setup', SetupController::class . ':show')->setName('setup.show');
+	$app->post('/setup', SetupController::class . ':create')->setName('setup.create');
 }
+else
+{
+	$app->get('/setup', AuthController::class . ':redirect');
+}
+
+$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']));
 
 foreach($routes as $pluginRoute)
 {
@@ -15,7 +42,7 @@ foreach($routes as $pluginRoute)
 	$route	= $pluginRoute['route'];
 	$class	= $pluginRoute['class'];
 
-	$app->{$method}($route, $class);		
+	$app->{$method}($route, $class);
 }
 
-$app->get('/[{params:.*}]', PageController::class . ':index');
+$app->get('/[{params:.*}]', PageController::class . ':index')->setName('home');

+ 3 - 2
system/Settings.php

@@ -26,7 +26,7 @@ class Settings
 		
 		return [
 			'determineRouteBeforeAppMiddleware' 	=> true,
-			'displayErrorDetails' 					=> false,
+			'displayErrorDetails' 					=> true,
 			'title'									=> 'TYPEMILL',
 			'author'								=> 'Unknown',
 			'copyright'								=> 'Copyright',
@@ -38,6 +38,7 @@ class Settings
 			'themeBasePath'							=> $rootPath,
 			'themePath'								=> $rootPath . $themeFolder . DIRECTORY_SEPARATOR . $theme,
 			'settingsPath'							=> $rootPath . 'settings',
+			'userPath'								=> $rootPath . 'settings' . DIRECTORY_SEPARATOR . 'users',
 			'authorPath'							=> __DIR__ . DIRECTORY_SEPARATOR . 'author' . DIRECTORY_SEPARATOR,
 			'contentFolder'							=> 'content',
 			'version'								=> '1.1.2',
@@ -45,7 +46,7 @@ class Settings
 		];
 	}
 	
-	private static function getUserSettings()
+	public static function getUserSettings()
 	{
 		$yaml = new Models\WriteYaml();
 		

+ 34 - 0
system/author/auth/login.twig

@@ -0,0 +1,34 @@
+{% extends 'layouts/layoutAuth.twig' %}
+{% block title %}Login{% endblock %}
+
+{% block content %}
+
+	<div class="setupWrapper">
+
+		<form method="POST" action="{{ path_for("auth.login") }}" autocomplete="off">
+		
+			<fieldset class="auth">
+				<div class="formElement{{ errors.username ? ' errors' : '' }}">
+					<label for="username">Username <abbr title="required">*</abbr></label>
+					<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' : '' }}">
+					<label for="password">Password <abbr title="required">*</abbr></label>
+					<input type="password" name="password" required>
+					{% if errors.password %}
+						<span class="error">{{ errors.password | first }}</span>
+					{% endif %}
+				</div>
+			</fieldset>
+
+			<input type="submit" value="Login" />
+			{{ csrf_field() | raw }}
+					
+		</form>
+		
+	</div>
+	
+{% endblock %}

+ 41 - 0
system/author/auth/setup.twig

@@ -0,0 +1,41 @@
+{% extends 'layouts/layoutAuth.twig' %}
+{% block title %}Setup{% endblock %}
+
+{% block content %}
+
+	<div class="setupWrapper">
+
+		<form method="POST" action="{{ path_for('setup.create') }}" autocomplete="off">
+		
+			<fieldset class="auth">
+				<div class="formElement{{ errors.username ? ' errors' : '' }}">
+					<label for="username">Username <abbr title="required">*</abbr></label>
+					<input type="text" name="username" value="{{ old.username }}" required>
+					{% if errors.username %}
+						<span class="error">{{ errors.username | first }}</span>
+					{% endif %}
+				</div>
+				<div class="formElement{{ errors.email ? ' errors' : '' }}">
+					<label for="email">E-Mail <abbr title="required">*</abbr></label>
+					<input type="text" name="email" value="{{ old.email }}" required>
+					{% if errors.email %}
+						<span class="error">{{ errors.email | first }}</span>
+					{% endif %}
+				</div>
+				<div class="formElement{{ errors.password ? ' errors' : '' }}">
+					<label for="password">Password <abbr title="required">*</abbr></label>
+					<input type="password" name="password" required>
+					{% if errors.password %}
+						<span class="error">{{ errors.password | first }}</span>
+					{% endif %}
+				</div>
+			</fieldset>
+
+			<input type="submit" value="Create User" />
+			{{ csrf_field() | raw }}
+					
+		</form>
+		
+	</div>
+	
+{% endblock %}

+ 49 - 0
system/author/auth/welcome.twig

@@ -0,0 +1,49 @@
+{% extends 'layouts/layoutAuth.twig' %}
+
+{% block title %}Setup Welcome{% endblock %}
+
+{% block content %}
+		
+	<div class="welcome">
+	
+		<div class="medium">
+			<div class="welcomeIntro">
+				<h1>Hurra!</h1>
+				<p>Your account has been created and you are logged in now.</p>
+				<p><strong>Next step:</strong> Visit the author panel and setup your new website. You can configure the system, choose themes and add plugins.</p>
+				<p><strong>Coming soon:</strong> You will probably miss a content editor in the author panel, but it will follow very soon. For time beeing you can write your content offline and upload the markdown files via FTP.</p>
+				<p><strong>Get help:</strong> If you have any questions, please consult the <a target="_blank" href="http://typemill.net/typemill"><i class="icon-link-ext"></i> docs</a> or open a new issue on <a target="_blank" href="https://github.com/trendschau/typemill"><i class="icon-link-ext"></i> github</a>.</p>
+			</div>
+			<a class="button" href="{{ path_for('settings.show') }}">Setup your website</a>
+ 		</div>
+		
+		<div class="small">
+			<div class="welcomeCard">
+				<a href="{{ path_for('settings.show') }}">
+					<div class="welcomeInner">
+						<h3>System</h3>
+						<p>Give your new website a name, add the author and choose a copyright.</p>				
+					</div>
+				</a>				
+			</div>
+			<div class="welcomeCard">
+				<a href="{{ path_for('themes.show') }}">
+					<div class="welcomeInner">
+						<h3>Themes</h3>
+						<p>Choose a theme for your website and configure the theme details.</p>
+					</div>
+				</a>
+			</div>
+			<div class="welcomeCard">
+				<a href="{{ path_for('plugins.show') }}">
+					<div class="welcomeInner">
+						<h3>Plugins</h3>
+						<p>Add new features to your website with plugins and configure them.</p>
+					</div>
+				</a>
+			</div>
+		</div>
+		
+	</div>
+	
+{% endblock %}

+ 23 - 0
system/author/content/content.twig

@@ -0,0 +1,23 @@
+{% extends 'layouts/layoutContent.twig' %}
+{% block title %}Content{% endblock %}
+
+{% block content %}
+	
+	<div class="formWrapper">
+
+		<section>
+
+			<header>
+				<h1>Coming Soon!</h1>
+			</header>
+			<p>Yes, we are now working on a content area and you can edit your text online very soon!</p>
+			<p>For time beeing, please write your pages offline with markdown and upload your markdown-files to the content folder via ftp.</p>
+			<p>Stay updated, the content editor will be added in one of the next releases ...</p>
+			<p>Happy writing</p>
+			<p>TYPEMILL</p>
+			
+		</section>
+		
+	</div>
+	
+{% endblock %}

+ 12 - 0
system/author/css/fontello/LICENSE.txt

@@ -0,0 +1,12 @@
+Font license info
+
+
+## Font Awesome
+
+   Copyright (C) 2016 by Dave Gandy
+
+   Author:    Dave Gandy
+   License:   SIL ()
+   Homepage:  http://fortawesome.github.com/Font-Awesome/
+
+

+ 75 - 0
system/author/css/fontello/README.txt

@@ -0,0 +1,75 @@
+This webfont is generated by http://fontello.com open source project.
+
+
+================================================================================
+Please, note, that you should obey original font licenses, used to make this
+webfont pack. Details available in LICENSE.txt file.
+
+- Usually, it's enough to publish content of LICENSE.txt file somewhere on your
+  site in "About" section.
+
+- If your project is open-source, usually, it will be ok to make LICENSE.txt
+  file publicly available in your repository.
+
+- Fonts, used in Fontello, don't require a clickable link on your site.
+  But any kind of additional authors crediting is welcome.
+================================================================================
+
+
+Comments on archive content
+---------------------------
+
+- /font/* - fonts in different formats
+
+- /css/*  - different kinds of css, for all situations. Should be ok with 
+  twitter bootstrap. Also, you can skip <i> style and assign icon classes
+  directly to text elements, if you don't mind about IE7.
+
+- demo.html - demo file, to show your webfont content
+
+- LICENSE.txt - license info about source fonts, used to build your one.
+
+- config.json - keeps your settings. You can import it back into fontello
+  anytime, to continue your work
+
+
+Why so many CSS files ?
+-----------------------
+
+Because we like to fit all your needs :)
+
+- basic file, <your_font_name>.css - is usually enough, it contains @font-face
+  and character code definitions
+
+- *-ie7.css - if you need IE7 support, but still don't wish to put char codes
+  directly into html
+
+- *-codes.css and *-ie7-codes.css - if you like to use your own @font-face
+  rules, but still wish to benefit from css generation. That can be very
+  convenient for automated asset build systems. When you need to update font -
+  no need to manually edit files, just override old version with archive
+  content. See fontello source code for examples.
+
+- *-embedded.css - basic css file, but with embedded WOFF font, to avoid
+  CORS issues in Firefox and IE9+, when fonts are hosted on the separate domain.
+  We strongly recommend to resolve this issue by `Access-Control-Allow-Origin`
+  server headers. But if you ok with dirty hack - this file is for you. Note,
+  that data url moved to separate @font-face to avoid problems with <IE9, when
+  string is too long.
+
+- animate.css - use it to get ideas about spinner rotation animation.
+
+
+Attention for server setup
+--------------------------
+
+You MUST setup server to reply with proper `mime-types` for font files -
+otherwise some browsers will fail to show fonts.
+
+Usually, `apache` already has necessary settings, but `nginx` and other
+webservers should be tuned. Here is list of mime types for our file extensions:
+
+- `application/vnd.ms-fontobject` - eot
+- `application/x-font-woff` - woff
+- `application/x-font-ttf` - ttf
+- `image/svg+xml` - svg

+ 46 - 0
system/author/css/fontello/config.json

@@ -0,0 +1,46 @@
+{
+  "name": "",
+  "css_prefix_text": "icon-",
+  "css_use_suffix": false,
+  "hinting": true,
+  "units_per_em": 1000,
+  "ascent": 850,
+  "glyphs": [
+    {
+      "uid": "381da2c2f7fd51f8de877c044d7f439d",
+      "css": "picture",
+      "code": 59392,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "41087bc74d4b20b55059c60a33bf4008",
+      "css": "edit",
+      "code": 59393,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "e15f0d620a7897e2035c18c80142f6d9",
+      "css": "link-ext",
+      "code": 61582,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "e99461abfef3923546da8d745372c995",
+      "css": "cog",
+      "code": 59394,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "8b9e6a8dd8f67f7c003ed8e7e5ee0857",
+      "css": "off",
+      "code": 59395,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "5408be43f7c42bccee419c6be53fdef5",
+      "css": "doc-text",
+      "code": 61686,
+      "src": "fontawesome"
+    }
+  ]
+}

+ 85 - 0
system/author/css/fontello/css/animation.css

@@ -0,0 +1,85 @@
+/*
+   Animation example, for spinners
+*/
+.animate-spin {
+  -moz-animation: spin 2s infinite linear;
+  -o-animation: spin 2s infinite linear;
+  -webkit-animation: spin 2s infinite linear;
+  animation: spin 2s infinite linear;
+  display: inline-block;
+}
+@-moz-keyframes spin {
+  0% {
+    -moz-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -moz-transform: rotate(359deg);
+    -o-transform: rotate(359deg);
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@-webkit-keyframes spin {
+  0% {
+    -moz-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -moz-transform: rotate(359deg);
+    -o-transform: rotate(359deg);
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@-o-keyframes spin {
+  0% {
+    -moz-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -moz-transform: rotate(359deg);
+    -o-transform: rotate(359deg);
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@-ms-keyframes spin {
+  0% {
+    -moz-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -moz-transform: rotate(359deg);
+    -o-transform: rotate(359deg);
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@keyframes spin {
+  0% {
+    -moz-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -moz-transform: rotate(359deg);
+    -o-transform: rotate(359deg);
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}

+ 7 - 0
system/author/css/fontello/css/fontello-codes.css

@@ -0,0 +1,7 @@
+
+.icon-picture:before { content: '\e800'; } /* '' */
+.icon-edit:before { content: '\e801'; } /* '' */
+.icon-cog:before { content: '\e802'; } /* '' */
+.icon-off:before { content: '\e803'; } /* '' */
+.icon-link-ext:before { content: '\f08e'; } /* '' */
+.icon-doc-text:before { content: '\f0f6'; } /* '' */

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 10 - 0
system/author/css/fontello/css/fontello-embedded.css


+ 7 - 0
system/author/css/fontello/css/fontello-ie7-codes.css

@@ -0,0 +1,7 @@
+
+.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
+.icon-edit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
+.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
+.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
+.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf08e;&nbsp;'); }
+.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0f6;&nbsp;'); }

+ 18 - 0
system/author/css/fontello/css/fontello-ie7.css

@@ -0,0 +1,18 @@
+[class^="icon-"], [class*=" icon-"] {
+  font-family: 'fontello';
+  font-style: normal;
+  font-weight: normal;
+ 
+  /* fix buttons height */
+  line-height: 1em;
+ 
+  /* you can be more comfortable with increased icons size */
+  /* font-size: 120%; */
+}
+ 
+.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
+.icon-edit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
+.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
+.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
+.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf08e;&nbsp;'); }
+.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0f6;&nbsp;'); }

+ 63 - 0
system/author/css/fontello/css/fontello.css

@@ -0,0 +1,63 @@
+@font-face {
+  font-family: 'fontello';
+  src: url('../font/fontello.eot?58763890');
+  src: url('../font/fontello.eot?58763890#iefix') format('embedded-opentype'),
+       url('../font/fontello.woff2?58763890') format('woff2'),
+       url('../font/fontello.woff?58763890') format('woff'),
+       url('../font/fontello.ttf?58763890') format('truetype'),
+       url('../font/fontello.svg?58763890#fontello') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
+/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
+/*
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+  @font-face {
+    font-family: 'fontello';
+    src: url('../font/fontello.svg?58763890#fontello') format('svg');
+  }
+}
+*/
+ 
+ [class^="icon-"]:before, [class*=" icon-"]:before {
+  font-family: "fontello";
+  font-style: normal;
+  font-weight: normal;
+  speak: none;
+ 
+  display: inline-block;
+  text-decoration: inherit;
+  width: 1em;
+  margin-right: .2em;
+  text-align: center;
+  /* opacity: .8; */
+ 
+  /* For safety - reset parent styles, that can break glyph codes*/
+  font-variant: normal;
+  text-transform: none;
+ 
+  /* fix buttons height, for twitter bootstrap */
+  line-height: 1em;
+ 
+  /* Animation center compensation - margins should be symmetric */
+  /* remove if not needed */
+  margin-left: .2em;
+ 
+  /* you can be more comfortable with increased icons size */
+  /* font-size: 120%; */
+ 
+  /* Font smoothing. That was taken from TWBS */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+ 
+  /* Uncomment for 3D effect */
+  /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
+}
+ 
+.icon-picture:before { content: '\e800'; } /* '' */
+.icon-edit:before { content: '\e801'; } /* '' */
+.icon-cog:before { content: '\e802'; } /* '' */
+.icon-off:before { content: '\e803'; } /* '' */
+.icon-link-ext:before { content: '\f08e'; } /* '' */
+.icon-doc-text:before { content: '\f0f6'; } /* '' */

+ 313 - 0
system/author/css/fontello/demo.html

@@ -0,0 +1,313 @@
+<!DOCTYPE html>
+<html>
+  <head><!--[if lt IE 9]><script language="javascript" type="text/javascript" src="//html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
+    <meta charset="UTF-8"><style>/*
+ * Bootstrap v2.2.1
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+.clearfix {
+  *zoom: 1;
+}
+.clearfix:before,
+.clearfix:after {
+  display: table;
+  content: "";
+  line-height: 0;
+}
+.clearfix:after {
+  clear: both;
+}
+html {
+  font-size: 100%;
+  -webkit-text-size-adjust: 100%;
+  -ms-text-size-adjust: 100%;
+}
+a:focus {
+  outline: thin dotted #333;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+a:hover,
+a:active {
+  outline: 0;
+}
+button,
+input,
+select,
+textarea {
+  margin: 0;
+  font-size: 100%;
+  vertical-align: middle;
+}
+button,
+input {
+  *overflow: visible;
+  line-height: normal;
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+  padding: 0;
+  border: 0;
+}
+body {
+  margin: 0;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 14px;
+  line-height: 20px;
+  color: #333;
+  background-color: #fff;
+}
+a {
+  color: #08c;
+  text-decoration: none;
+}
+a:hover {
+  color: #005580;
+  text-decoration: underline;
+}
+.row {
+  margin-left: -20px;
+  *zoom: 1;
+}
+.row:before,
+.row:after {
+  display: table;
+  content: "";
+  line-height: 0;
+}
+.row:after {
+  clear: both;
+}
+[class*="span"] {
+  float: left;
+  min-height: 1px;
+  margin-left: 20px;
+}
+.container,
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+  width: 940px;
+}
+.span12 {
+  width: 940px;
+}
+.span11 {
+  width: 860px;
+}
+.span10 {
+  width: 780px;
+}
+.span9 {
+  width: 700px;
+}
+.span8 {
+  width: 620px;
+}
+.span7 {
+  width: 540px;
+}
+.span6 {
+  width: 460px;
+}
+.span5 {
+  width: 380px;
+}
+.span4 {
+  width: 300px;
+}
+.span3 {
+  width: 220px;
+}
+.span2 {
+  width: 140px;
+}
+.span1 {
+  width: 60px;
+}
+[class*="span"].pull-right,
+.row-fluid [class*="span"].pull-right {
+  float: right;
+}
+.container {
+  margin-right: auto;
+  margin-left: auto;
+  *zoom: 1;
+}
+.container:before,
+.container:after {
+  display: table;
+  content: "";
+  line-height: 0;
+}
+.container:after {
+  clear: both;
+}
+p {
+  margin: 0 0 10px;
+}
+.lead {
+  margin-bottom: 20px;
+  font-size: 21px;
+  font-weight: 200;
+  line-height: 30px;
+}
+small {
+  font-size: 85%;
+}
+h1 {
+  margin: 10px 0;
+  font-family: inherit;
+  font-weight: bold;
+  line-height: 20px;
+  color: inherit;
+  text-rendering: optimizelegibility;
+}
+h1 small {
+  font-weight: normal;
+  line-height: 1;
+  color: #999;
+}
+h1 {
+  line-height: 40px;
+}
+h1 {
+  font-size: 38.5px;
+}
+h1 small {
+  font-size: 24.5px;
+}
+body {
+  margin-top: 90px;
+}
+.header {
+  position: fixed;
+  top: 0;
+  left: 50%;
+  margin-left: -480px;
+  background-color: #fff;
+  border-bottom: 1px solid #ddd;
+  padding-top: 10px;
+  z-index: 10;
+}
+.footer {
+  color: #ddd;
+  font-size: 12px;
+  text-align: center;
+  margin-top: 20px;
+}
+.footer a {
+  color: #ccc;
+  text-decoration: underline;
+}
+.the-icons {
+  font-size: 14px;
+  line-height: 24px;
+}
+.switch {
+  position: absolute;
+  right: 0;
+  bottom: 10px;
+  color: #666;
+}
+.switch input {
+  margin-right: 0.3em;
+}
+.codesOn .i-name {
+  display: none;
+}
+.codesOn .i-code {
+  display: inline;
+}
+.i-code {
+  display: none;
+}
+@font-face {
+      font-family: 'fontello';
+      src: url('./font/fontello.eot?16442487');
+      src: url('./font/fontello.eot?16442487#iefix') format('embedded-opentype'),
+           url('./font/fontello.woff?16442487') format('woff'),
+           url('./font/fontello.ttf?16442487') format('truetype'),
+           url('./font/fontello.svg?16442487#fontello') format('svg');
+      font-weight: normal;
+      font-style: normal;
+    }
+     
+     
+    .demo-icon
+    {
+      font-family: "fontello";
+      font-style: normal;
+      font-weight: normal;
+      speak: none;
+     
+      display: inline-block;
+      text-decoration: inherit;
+      width: 1em;
+      margin-right: .2em;
+      text-align: center;
+      /* opacity: .8; */
+     
+      /* For safety - reset parent styles, that can break glyph codes*/
+      font-variant: normal;
+      text-transform: none;
+     
+      /* fix buttons height, for twitter bootstrap */
+      line-height: 1em;
+     
+      /* Animation center compensation - margins should be symmetric */
+      /* remove if not needed */
+      margin-left: .2em;
+     
+      /* You can be more comfortable with increased icons size */
+      /* font-size: 120%; */
+     
+      /* Font smoothing. That was taken from TWBS */
+      -webkit-font-smoothing: antialiased;
+      -moz-osx-font-smoothing: grayscale;
+     
+      /* Uncomment for 3D effect */
+      /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
+    }
+     </style>
+    <link rel="stylesheet" href="css/animation.css"><!--[if IE 7]><link rel="stylesheet" href="css/" + font.fontname + "-ie7.css"><![endif]-->
+    <script>
+      function toggleCodes(on) {
+        var obj = document.getElementById('icons');
+      
+        if (on) {
+          obj.className += ' codesOn';
+        } else {
+          obj.className = obj.className.replace(' codesOn', '');
+        }
+      }
+      
+    </script>
+  </head>
+  <body>
+    <div class="container header">
+      <h1>fontello <small>font demo</small></h1>
+      <label class="switch">
+        <input type="checkbox" onclick="toggleCodes(this.checked)">show codes
+      </label>
+    </div>
+    <div class="container" id="icons">
+      <div class="row">
+        <div class="the-icons span3" title="Code: 0xe800"><i class="demo-icon icon-picture">&#xe800;</i> <span class="i-name">icon-picture</span><span class="i-code">0xe800</span></div>
+        <div class="the-icons span3" title="Code: 0xe801"><i class="demo-icon icon-edit">&#xe801;</i> <span class="i-name">icon-edit</span><span class="i-code">0xe801</span></div>
+        <div class="the-icons span3" title="Code: 0xe802"><i class="demo-icon icon-cog">&#xe802;</i> <span class="i-name">icon-cog</span><span class="i-code">0xe802</span></div>
+        <div class="the-icons span3" title="Code: 0xe803"><i class="demo-icon icon-off">&#xe803;</i> <span class="i-name">icon-off</span><span class="i-code">0xe803</span></div>
+      </div>
+      <div class="row">
+        <div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext">&#xf08e;</i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
+        <div class="the-icons span3" title="Code: 0xf0f6"><i class="demo-icon icon-doc-text">&#xf0f6;</i> <span class="i-name">icon-doc-text</span><span class="i-code">0xf0f6</span></div>
+      </div>
+    </div>
+    <div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>
+  </body>
+</html>

binární
system/author/css/fontello/font/fontello.eot


+ 22 - 0
system/author/css/fontello/font/fontello.svg

@@ -0,0 +1,22 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>Copyright (C) 2018 by original authors @ fontello.com</metadata>
+<defs>
+<font id="fontello" horiz-adv-x="1000" >
+<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
+<missing-glyph horiz-adv-x="1000" />
+<glyph glyph-name="picture" unicode="&#xe800;" d="M357 529q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
+
+<glyph glyph-name="edit" unicode="&#xe801;" d="M496 189l64 65-85 85-64-65v-31h53v-54h32z m245 402q-9 9-18 0l-196-196q-9-9 0-18t18 0l196 196q9 9 0 18z m45-331v-106q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14 9-4 10-13 2-10-5-16l-27-28q-8-8-18-4-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v70q0 7 5 12l36 36q8 8 20 4t11-16z m-54 411l161-160-375-375h-161v160z m248-73l-51-52-161 161 51 52q16 15 38 15t38-15l85-85q16-16 16-38t-16-38z" horiz-adv-x="1000" />
+
+<glyph glyph-name="cog" unicode="&#xe802;" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="off" unicode="&#xe803;" d="M857 350q0-87-34-166t-91-137-137-92-166-34-167 34-136 92-92 137-34 166q0 102 45 191t126 151q24 18 54 14t46-28q18-23 14-53t-28-47q-54-41-84-101t-30-127q0-58 23-111t61-91 91-61 111-23 110 23 92 61 61 91 22 111q0 68-30 127t-84 101q-23 18-28 47t14 53q17 24 47 28t53-14q81-61 126-151t45-191z m-357 429v-358q0-29-21-50t-50-21-51 21-21 50v358q0 29 21 50t51 21 50-21 21-50z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="link-ext" unicode="&#xf08e;" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
+
+<glyph glyph-name="doc-text" unicode="&#xf0f6;" d="M819 638q16-16 27-42t11-50v-642q0-23-15-38t-38-16h-750q-23 0-38 16t-16 38v892q0 23 16 38t38 16h500q22 0 49-11t42-27z m-248 136v-210h210q-5 17-12 23l-175 175q-6 7-23 12z m215-853v572h-232q-23 0-38 16t-16 37v233h-429v-858h715z m-572 483q0 7 5 12t13 5h393q8 0 13-5t5-12v-36q0-8-5-13t-13-5h-393q-8 0-13 5t-5 13v36z m411-125q8 0 13-5t5-13v-36q0-8-5-13t-13-5h-393q-8 0-13 5t-5 13v36q0 8 5 13t13 5h393z m0-143q8 0 13-5t5-13v-36q0-8-5-13t-13-5h-393q-8 0-13 5t-5 13v36q0 8 5 13t13 5h393z" horiz-adv-x="857.1" />
+</font>
+</defs>
+</svg>

binární
system/author/css/fontello/font/fontello.ttf


binární
system/author/css/fontello/font/fontello.woff


binární
system/author/css/fontello/font/fontello.woff2


+ 502 - 56
system/author/css/style.css

@@ -2,7 +2,7 @@
 *  		HELPERS		  *
 **********************/
 
-a, a:link, a:visited, a:focus, a:hover, a:active, button, .button, input, .control-group{
+a, a:link, a:visited, a:focus, a:hover, a:active, button, .button, input, .control-group, .sidebar-menu, .menu-action{
 	-webkit-transition: all 0.2s ease;
 	-moz-transition: all 0.2s ease;
 	-o-transition: all 0.2s ease;
@@ -14,8 +14,8 @@ a, a:link, a:visited, a:focus, a:hover, a:active, button, .button, input, .contr
 *    	 COLORS 	  *
 **********************/
 
-body, .pluginHead header{ 			background: #f9f8f6; color: #444; }
-.errors .pluginHead header{			background: #e0474c; color: #fff; }
+body, .cardHead header{ 			background: #f9f8f6; color: #444; }
+.errors .cardHead header{			background: #e0474c; color: #fff; }
 .onoffswitch-label {				border: 1px solid #999; }
 .onoffswitch-inner:before,
 .onoffswitch-inner:after {			color: #FFFFFF; }
@@ -27,9 +27,11 @@ input, select, textarea,
 section, .welcome{					background: #FFFFFF; box-shadow: 0 0 2px #ddd; }
 select:focus, input:focus,
 textarea:focus{						border: 1px solid #FFF; outline: none; box-shadow: 0 0 2px #000; }
+button[type="button"].has-settings:hover{ background: #efece7; }
 button[type="button"].active{		background: #70c1b3; color: #f9f8f6; border-top: 1px solid #70c1b3; }
-button[type="button"].active:hover{	background: #66b0a3; border-top: 1px solid #66b0a3; }
+button[type="button"].has-settings.active:hover{	background: #66b0a3; border-top: 1px solid #66b0a3; }
 button[type="button"]{				background: #f9f8f6; color: #444; border-top: 1px solid #ddd; }
+input:disabled{						background: #f9f8f6; }
 input[type="submit"],
 a.button,a.button:link,
 a.button:visited{					border: 2px solid #e0474c; background: #e0474c; color: #f9f8f6; }
@@ -40,7 +42,7 @@ a.button:active{					border: 2px solid #cc4146; background: #cc4146; }
 .error input, .error select,
 .error textarea{					border: 1px solid #e0474c; }
 .error, .error small, .error p{		color: #e0474c; }
-.pluginInner{						border: 1px solid #ddd; background: #FFF; }
+.cardInner{							border: 1px solid #ddd; background: #FFF; }
 .color-box{							border: 1px solid #ddd; }
 label .help, .label .help{ 			color: #FFF; background: #999; }
 .help .tooltip { 					color: #fff; background-color: #999; }
@@ -55,55 +57,312 @@ label .help, .label .help{ 			color: #FFF; background: #999; }
 .control-group .radiomark:after { 				background: #fff; }
 .control-group input:disabled ~ .checkmark,
 .control-group input:disabled ~ .radiomark {	border: 2px solid #eee; background-color: #eee; }
-.pluginInfo a, .pluginInfo a:link,
-.pluginInfo a:visited,
-.themeInfo a, .themeInfo a:link,
-.themeInfo a:visited{							color: #70c1b3; text-decoration: none;}
-.pluginInfo a:focus,.pluginInfo a:hover,
-.pluginInfo a:active,
-.themeInfo a:focus,.themeInfo a:hover,
-.themeInfo a:active{							color: #70c1b3; text-decoration: underline;}
+.cardInfo a, .cardInfo a:link,
+.cardInfo a:visited{							color: #70c1b3; text-decoration: none;}
+.cardInfo a:focus,.cardInfo a:hover,
+.cardInfo a:active{								color: #70c1b3; text-decoration: underline;}
 
 
 /********************
-*  		FONTS 	    *
+*  	COMMONS 	    *
 ********************/
 
 html,body{
 	padding: 0;
 	margin:0;
 }
-
 body{
-	padding: 20px;
 	font-size: 16px;
 	font-family: Helvetica, Calibri, Arial, sans-serif;
 	-webkit-font-smoothing: antialiased;
 	-moz-osx-font-smoothing: grayscale;	
 }
-
 header, nav, h1, h2, h3, h4, h5, h6{
 	font-family: arial, sans-serif;
 }
+h1{ font-size: 1.8em; margin: 1em 0 0.8em; font-weight: 700: }
+h2{ font-size: 1.6em; margin: 1em 0 0.8em; font-weight: 700; }
+h3{	font-size: 1em; margin: 0.6em 0 0.6em; text-transform: uppercase; font-weight: 300; }
+
+/********************
+*  	SETUP FORM 	    *
+********************/
+
+.setupWrapper{
+	max-width: 300px;
+	margin-left: auto;
+	margin-right: auto;
+	margin-top: 8%;
+	background: #fff;
+	padding: 40px;
+	border-radius: 3px;
+	box-shadow: 0px 0px 10px rgba(0,0,0,0.2);
+}
+.setupWrapper label{
+	font-weight: 700;
+}
+.setupWrapper input[type="submit"]
+{
+	border-radius: 0px;
+	margin-bottom: 0px;
+}
+.setupWrapper .formElement{
+	margin: 0 0 20px 0;
+}
 
 /********************
-*  	   HEADLINES    *
+*  	AUTHOR NAVI     *
 ********************/
 
-h1, h2, h3, h4, h5, h6{ font-weight: 700; }
-h1{ font-size: 1.8em; margin: 1em 0 0.8em; }
-h2{ font-size: 1.6em; margin: 1em 0 0.8em; }
-h3{	font-size: 1.1em; margin: 0.6em 0 0.6em; }
+.main-header{
+	display: inline-block;
+	width: 100%;
+	background: #fff;
+	border-bottom: 1px solid #eee;
+}
+.header-navi, .main{
+	margin: auto;
+	max-width: 1200px;
+}
+.header-navi .logo{
+	display: inline-block;
+	font-size: 1.6em;
+	font-weight: 700;
+	width: 18%;
+	box-sizing: border-box;
+}
+.header-navi .logo a{
+	color: #000;
+	text-decoration: none;
+	line-height: 2em;
+	margin-left: 10px;
+}
+.header-navi ul{
+	display: inline-block;
+	width: 80%;
+	box-sizing: border-box;
+	text-align: right;
+	list-style: none;
+	padding:0px;
+	margin:0px;
+	font-size: 1.2em;
+}
+.header-navi li{
+	display: inline-block;
+	padding: 0px;
+	margin: 0px;
+}
+.navi-items a{
+	color: #000;
+	font-size: 0.9em;
+	text-transform: uppercase;
+	text-decoration: none;
+	margin: 0px 1px;
+	padding: 20px 10px 10px;
+	line-height: 2em;
+	border-bottom: 3px solid transparent;
+}
+.navi-items a:hover, .navi-items a:focus, .navi-items a:active, .navi-items a.active{
+	background: #f9f8f6;
+	border-bottom: 3px solid #e0474c;
+}
+.navi-items i{
+	color: #ddd;
+}
+.navi-items span{
+	display: none;
+}
+.navi-items a.active,.navi-items a.active i{
+	color: #e0474c;
+}
+
+.main{
+	margin: 0px auto;
+	box-sizing:border-box;
+	padding: 20px;
+}
+article{
+	width: 100%;
+}
+aside.sidebar{
+	display: block;
+	width: 100%;
+	background: #fff;
+	margin-bottom: 10px;
+	box-sizing: border-box;
+}
+.sidebar-menu{
+	max-height: 40px;
+	padding: 0px 20px;
+	overflow: hidden;
+	box-sizing: border-box;
+	font-size: 0.9em;	
+}
+.sidebar-menu.expand{
+	max-height: 500px;
+}
+.menu-action{
+	display: block;
+	width: 100%;
+	height: 40px;
+	text-align: center;
+	line-height: 40px;
+	text-transform: uppercase;
+	font-weight: 300;
+	
+	background-image:
+		linear-gradient(45deg, transparent 50%, #444 50%),
+		linear-gradient(135deg, #444 50%, transparent 50%),
+		linear-gradient(to right, #fff, #fff);
+	background-position:
+		calc(50% + 30px) calc(1em + 2px),
+		calc(50% + 35px) calc(1em + 2px),
+		100% 0;
+	background-size: 
+		5px 5px,
+		5px 5px,
+		2.8em 2.8em;
+	background-repeat: no-repeat;
+	cursor: pointer;	
+}
+.expand .menu-action{
+	background-image:
+		linear-gradient(135deg, transparent 50%, #444 50%),
+		linear-gradient(45deg, #444 50%, transparent 50%),
+		linear-gradient(to right, #fff, #fff);	
+}
+ul.menu-list{
+	list-style: none;
+	margin:5px 0 30px;
+	padding:0;
+}
+li.menu-item{
+	padding: 0;
+	margin: 8px 0;	
+}
+.menu-item a, .menu-item a:link, .menu-item a:visited{
+	text-decoration: none;
+	color: #444;
+	padding: 0px 10px;
+	display: inline-block;
+	line-height: 2px;
+	width: auto;
+	height: 0; 
+	border-top: 8px solid transparent;
+	border-bottom: 8px solid transparent; 
+	border-left: 10px solid white;
+	border-radius: 3px;
+}
+.menu-item a:hover, .menu-item a:focus, .menu-item a:active, .menu-item a.active{
+	border-left: 10px solid #e0474c;
+}
+.menu-item a.active{
+	color: #e0474c;
+}
+
+/*************
+**  MODAL	**
+*************/
 
+.modal{
+	position:fixed;
+	display: none;
+	width: 100%;
+	height: 100%;
+	padding: 20px;
+	box-sizing: border-box;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	background: #FFF;
+	background: rgba(255,255,255,0.9);
+}
+.modal.show{
+	display: block;
+}
+.modalInner{
+	position: relative;
+	max-width: 350px;
+	margin: 10% auto;
+	background: #FFF;
+	padding: 50px 50px 20px 50px;
+	border-radius: 3px;
+	box-shadow: 0px 0px 10px rgba(0,0,0,0.2);
+}
+.closeModal{
+	position: absolute;
+	top: 0px;
+	right: 0px;
+	font-weight: 700; 
+	padding: 0.6em;
+	cursor: pointer;
+}
+.closeModal:focus, .closeModal:hover{
+	background: #f9f8f6;
+}
+.modal .actionLink{
+	display: block;
+	width: 100%;
+	text-align: center;
+	margin-top: -20px;
+	margin-bottom: 20px;
+}
+a.openModal:link,a.openModal:active,a.openModal:visited{
+	color: #cc4146;
+	padding: 0px 20px 20px;
+	text-decoration: none;
+	display: block;
+	margin: auto;
+	text-align: center;
+	width: 100px;
+}
+a.openModal:focus,a.openModal:hover{
+	text-decoration: underline;
+}
+ul.userlist{
+	padding: 0;
+	margin: 10px 20px 30px;
+	list-style: none;
+}
+li.row{
+	display: inline-block;
+	width: 100%;
+	background: #f9f8f6;
+	box-sizing: border-box;
+}
+li.row ul{
+	margin: 0px;
+	padding: 0px;
+	width: 100%;
+}
+li.col{
+	display: inline-block;
+	padding: 15px 10px;
+	box-sizing: border-box;
+	margin: 0px;
+}
+li.col.username, li.col.email, li.col.userrole, li.col.edit{
+	border: 1px solid #fff;
+}
+li.col.username{
+	border-top: 2px solid #70c1b3;
+}
+.col.username,.col.email,.col.userrole, .col.edit{
+	width: 100%;
+}
+.settings .medium a.button{
+	width: 100%;
+	display: block;
+	box-sizing:border-box;
+	text-align: center;
+}
 section{
 	position: relative;
 	margin-bottom: 40px;
 	padding: 20px 20px 40px;
 	box-sizing: border-box;
 }
-header{
-	padding: 0 20px;
-}
 header h1, header h2{
 	display: inline-block;
 }
@@ -121,12 +380,24 @@ header input[type="submit"]{
 	max-width: 900px;
 	margin: auto;
 }
+
+/*********************
+**  WELCOME PAGE	**
+*********************/
+
 .welcome{
 	padding: 30px 50px;
 	box-sizing: border-box;
+	box-shadow: 0px 0px 10px rgba(0,0,0,0.2);
+}
+.welcome .welcomeIntro a, .welcome .welcomeCard a:link, .welcome .welcomeCard a:visited{
+	text-decoration: none;
+	color: #e0474c;
+}
+.welcome .welcomeIntro a:hover, .welcome .welcomeCard a:focus, .welcome .welcomeCard a:active{
+	text-decoration: underline;
 }
 .welcome p{
-	font-size: 1.1em;
 	line-height: 1.5em;
 	padding: 0px;
 }
@@ -139,6 +410,47 @@ header input[type="submit"]{
 	border-radius: 3px;
 	text-decoration: none;	
 }
+.welcome .welcomeCard{
+	position: relative;
+	margin-top: -1px;
+	border-top: 1px solid #efece7;
+	border-bottom: 1px solid #efece7;
+	width: 100%;
+}
+.welcome .welcomeCard h3{
+	font-weight: 700;
+}
+.welcome .welcomeCard a, .welcome .welcomeCard a:link, .welcome .welcomeCard a:visited{
+	color: #444;
+	text-decoration: none;
+	width: 100%;
+}
+.welcome .welcomeCard a:hover, .welcome .welcomeCard a:focus, .welcome .welcomeCard a:active{
+	color: #e0474c;
+}
+.welcome .welcomeCard a:after{
+	content: '';
+	display: block;
+	position: absolute;
+	left: 100%;
+	top: 20px;
+	bottom: 0;
+	width: 0;
+	height: 0;
+	border-top: 50px solid transparent;
+	border-right: 0px solid transparent;
+	border-bottom: 50px solid transparent;
+	border-left: 20px solid #efece7;
+}
+.welcome .welcomeCard a:hover:after{
+	border-left: 20px solid #e0474c;	
+}
+.welcome .welcomeInner{
+	margin-top: 20px;
+	width: 90%;
+	height: 120px;
+}
+
 
 fieldset{
 	display: block;
@@ -207,70 +519,120 @@ input[type="submit"]{
 button,input[type="submit"]:hover{
 	cursor: pointer;
 }
+.button{
+	color: #fff;
+	padding: 10px;
+	border-radius: 3px;
+}
+
+/*****************
+** CARDS		**
+*****************/
 
-fieldset.plugin{
+.settings header
+{
+	padding: 0px 20px;
+}
+.userlist a, .userlist a:link, .userlist a:visited{
+	color: #e0474c;
+	text-decoration: none;
+}
+.userlist a:hover, .userlist a:focus, .userlist a:active{
+	text-decoration: underline;
+}
+a.button{
+	color: #fff;
+	text-decoration: none;
+}
+fieldset.card{
 	width: 100%;
 	display: inline-block;
 	padding: 15px 20px;
 	box-sizing: border-box;
-	vertical-align: top;	
+	vertical-align: top;
+}
+.card > .cardInner{
+	box-shadow: 0px 0px 5px rgba(0,0,0,0.2)	
 }
-.pluginInner{
+.cardInner{
 	box-sizing: border-box;
 }
-.pluginHead header, .pluginHead p, .pluginHead ul{
+.cardHead h2{
+	padding: 5px 20px 0;
+	margin-bottom: 0px;
+}
+.cardHead header, .cardHead p, .cardHead ul{
 	padding: 15px 20px;
 }
-.pluginHead header{
+.cardHead header{
 	width: 100%;
 	display: inline-block;
 	box-sizing: border-box;
 }
-.pluginHead legend{
+.cardHead legend{
 	font-size: 1.2em;
 	font-weight: 700;
 	float:left;
 }
-.pluginHead .pluginActive{
+.cardHead .cardActive{
 	float: right;
 }
-.pluginHead header label{
+.cardHead header label{
 	display: inline-block;
 	width: auto;
 	line-height: 20px;
 	margin: 0;
 	padding: 0 25px 0 0;
 }
-.pluginHead header .control-group .checkmark{
+.cardHead header .control-group .checkmark{
 	right:-5px;
 	left: auto;
 }
-.pluginHead header input{
+.cardHead header input{
 	width: auto;
 	min-height: 0px;
 	padding: 0;
 	margin: 0;
 	vertical-align: middle;
 }
-.pluginHead button[type="button"]{
+.cardContent{
+	line-height: 1.5em;
+}
+.card button[type="button"]{
 	width: 100%;
 	text-align: center;
-	padding: 15px 0;
+}
+.card button[type="button"].plugin-button{
 	border-left: 0px;
 	border-right: 0px;
 	border-bottom: 0px;
 	margin-top: 10px;
+	padding: 15px 0;
 }
-ul.pluginInfo,ul.themeInfo{
-	font-size: 0.8em;
+.card button[type="button"].theme-button{
+	border: 0px;
+	margin: 5px 0 20px;
+	padding: 12px;
+	border-radius: 3px;
+	min-height: 42px;
 }
-ul.pluginInfo{
-	padding: 0 20px;	
+.card button[type="button"].theme-button:hover{
+	border:0px;
+}
+.card button[type="button"].no-settings:hover{
+	cursor: default;
+}
+.card .medium{
+	padding: 0px 20px;
 }
-ul.themeInfo{
-	padding: 0px 0px;
+.card .medium button[type="button"]{
+	margin: 10px 0;
+}
+ul.cardInfo{
+	font-size: 0.8em;
+	padding: 0 20px;	
 }
-.pluginInfo li,.themeInfo li{
+.cardInfo li{
 	display: inline-block;
 	box-sizing: border-box;
 	vertical-align: top;
@@ -279,33 +641,34 @@ ul.themeInfo{
 	color: grey;
 	border-right: 1px solid grey;
 }
-.pluginInfo li:last-child,.themeInfo li:last-child{
+.cardInfo li:last-child{
 	border-right: 0px;
 }
-.pluginFields{
+.cardFields{
 	max-height: 0;
 	transition: max-height 0.5s ease-out;
 	overflow: hidden;
 	padding: 0px 20px;
 	border: 0px;
 }
-.pluginFields.open{
+.cardFields.open{
 	max-height: 1800px;
 	transition: max-height 0.5s ease-in;
 	overflow: hidden;
 	border: 1px;
 }
-.pluginFields label{
+.cardFields label{
 	margin-top: 12px;
 }
-.pluginFields .pluginField:first-child{
+.cardFields .cardField:first-child{
 	margin-top: 50px;
 }
-.pluginFields input, .pluginFields select, .pluginFields textarea, .pluginFields .onoffswitch{
+.cardFields input, .cardFields select, .cardFields textarea, .cardFields .onoffswitch{
 	margin-bottom: 12px;
 }
-
-
+.card img{
+	width: 100%;
+}
 
 /*
 ** SELECT BUTTON
@@ -336,12 +699,28 @@ span.error{
 	font-size: 0.8em;
 	line-height: 1em;
 }
-.pluginInner span.error{
+.cardInner span.error{
 	display: block;	
 	margin-top: -8px;
 	margin-bottom: 8px;	
 }
 
+.alert{
+	color: #fff;
+	width: 100%;
+	line-height: 20px;
+	display: block;
+	margin:0px;
+	text-align: center;
+	padding: 5px 0px;
+}
+.alert-info{
+	background: #70c1b3;
+}
+.alert-error{
+	background: #e0474c;
+}
+
 /*
 ** ON OFF SWITCH
 ** https://proto.io/freebies/onoff/
@@ -610,7 +989,6 @@ span.error{
 	top:auto;
 }	
 
-
 @media only screen and (min-width: 600px) {
 	form .medium{
 		width: 49.5%;
@@ -620,9 +998,77 @@ span.error{
 		width: 33.3%;
 		display: inline-block;
 	}
+	.settings .medium a.button{
+		display: inline-block;
+		width: auto;
+	}	
 }
-@media only screen and (min-width: 750px) {
+@media only screen and (min-width: 800px) {
 	fieldset.plugin{
 		width: 49.5%;
 	}
+	.welcome .medium{
+		width: 66%;
+		display: inline-block;
+	}
+	.welcome .small{
+		width: 33.3%;
+		display: inline-block;		
+	}
+	.navi-items span{
+		display: inline;
+	}	
+	.header-navi ul{
+		font-size: 1em;
+	}
+	.main{
+		margin: 30px auto;
+	}
+	article{
+		width: 80%;
+		display: inline-block;
+		vertical-align: top;
+	}
+	aside.sidebar{
+		width: 19%;
+		display: inline-block;
+		vertical-align: top;
+		background: transparent;
+	}
+	.sidebar-menu{
+		max-height: 2000px;
+		padding: 0px 20px;
+		overflow: hidden;
+		box-sizing: border-box;
+		font-size: 1em;
+	}
+	.menu-action{
+		display: none;
+		width: 0px;
+		height: 0px;
+	}
+	ul.menu-list{
+		margin:5px 0 50px;
+	}
+	.card .medium{
+		padding: 0px 20px;
+	}
+	.card .medium button[type="button"]{
+		margin-top: 10px;
+		margin-bottom: 40px;
+	}
+	.card .medium input[type="submit"]{
+		margin-top: 10px;
+		margin-bottom: 40px;
+	}
+	.col.username,.col.email,.col.userrole{
+		width: 30%;
+	}
+	.col.edit{
+		width: 10%;
+	}	
+	li.col.username{
+		border-top: 0px;
+		border-left: 2px solid #70c1b3;
+	}
 }

binární
system/author/img/apple-touch-icon-144x144.png


binární
system/author/img/apple-touch-icon-152x152.png


binární
system/author/img/favicon-16x16.png


binární
system/author/img/favicon-32x32.png


binární
system/author/img/favicon.ico


binární
system/author/img/mstile-144x144.png


+ 40 - 168
system/author/js/author.js

@@ -93,198 +93,70 @@
 		}
 	}
 
-	/**********************************
-	** 		START THEMESWITCH	 	 **
-	**********************************/
-	
-	/* change the theme if choosen in selectbox */
-	var themeSwitch		= document.getElementById("themeSwitch"),
-		pluginVersions 	= document.getElementsByClassName("fc-plugin-version");
-	
-	
-	if(themeSwitch)
-	{
-		getTheme(themeSwitch.value);
-		getVersions(pluginVersions, themeSwitch.value);
+	var openModal 	= document.getElementById("openModal"),
+		closeModal	= document.getElementById("closeModal");
 		
-		themeSwitch.addEventListener('change', function()
-		{
-			removeVersionBanner('theme-banner');
-			getTheme(themeSwitch.value);
-			getVersions(false, themeSwitch.value);
-		});
-	}
-	
-	function removeVersionBanner(bannerID)
+	if(openModal && closeModal)
 	{
-		var banner = document.getElementById(bannerID);
-		if(banner)
-		{
-			banner.parentElement.removeChild(banner);
-		}
-	}
-
-	/* use API to get theme informations from theme folder */
-	function getTheme(themeName)
-	{
-		var getUrl 		= window.location,
-			baseUrl 	= getUrl .protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1],
-			url 		= baseUrl+'/api/v1/themes?theme='+themeName,
-			getPost 	= 'GET',
-			themeImg	= document.getElementById("themePrev");
-
-		themeImg.src = baseUrl + '/themes/' + themeName + '/' + themeName + '.jpg';
-		
-		sendJson(function(response)
-		{
-			if(response !== 'error')
-			{
-				var themeData 	= JSON.parse(response),
-					fields		= themeData.forms.fields ? themeData.forms.fields : false,
-					settings	= themeData.settings ? themeData.settings : false;
-				
-				/* add the theme information and the theme fields to frontend */
-				addThemeInfo(themeData);
-				addThemeFields(fields, settings);
-			}
-			else
-			{
-				return false;
-			}
-		}, getPost, url, false);
+		openModal.addEventListener("click", function(e){ e.preventDefault(); toggle("modalWindow", "show"); });
+		closeModal.addEventListener("click", function(e){ e.preventDefault(); toggle("modalWindow", "show"); });
 	}
 	
-	function addThemeInfo(themeData)
-	{
-		var themeVersion 	= document.getElementById('themeVersion'),
-			themeLicence 	= document.getElementById('themeLicence'),
-			themeAuthor 	= document.getElementById('themeAuthor'),
-			themeUrl 		= document.getElementById('themeUrl');
-			
-		if(themeVersion && themeLicence && themeAuthor && themeUrl)
-		{
-			themeVersion.innerHTML 	= themeData.version;
-			themeLicence.innerHTML 	= themeData.licence;
-			themeAuthor.innerHTML 	= themeData.author;
-			themeUrl.innerHTML 		= '<a id="themeLink" href="' + themeData.homepage + '" target="_blank">Web</a>';		
-		}
-	}
+	var mobileMenu	= document.getElementById("mobile-menu");
 	
-	/* add input fields for theme configurations in frontend */
-	function addThemeFields(fields, settings)
+	if(mobileMenu)
 	{
-		var themeFields = document.getElementById('themeFields');
-		themeFields.innerHTML = '';
-		
-		for (var fieldName in fields) 
-		{
-			if (fields.hasOwnProperty(fieldName)) 
-			{
-				var newField = document.createElement('div');
-				newField.className = 'medium';
-				newField.innerHTML = generateHtmlField(fieldName, fields[fieldName], settings);
-				themeFields.appendChild(newField);
-			}
-		}
+		mobileMenu.addEventListener("click", function(e){ toggle("sidebar-menu", "expand"); });		
 	}
 	
-	/* generate an input field */
-	function generateHtmlField(fieldName, fieldDefinitions, settings)
+	function toggle(myid, myclass)
 	{
-		var html = 	'<span class="label">' + fieldDefinitions.label + '</span>';
-		
-		if(fieldDefinitions.type == 'textarea')
-		{
-			var content = settings[fieldName] ? settings[fieldName] : '';
-			var attributes = generateHtmlAttributes(fieldDefinitions);
-			html += '<textarea name="themesettings['+ fieldName + ']"' + attributes + '>' + content + '</textarea>';
-		}
-		else if(fieldDefinitions.type == 'checkbox')
-		{
-			var attributes = generateHtmlAttributes(fieldDefinitions);
-			
-			html += '<label class="control-group">' + fieldDefinitions.description +
-					  '<input type="checkbox" name="themesettings[' + fieldName + ']"'+ attributes + '>' +
-					  '<span class="checkmark"></span>' +
-					'</label>';
-		}
-		else if(fieldDefinitions.type == 'checkboxlist')
-		{
-			
-		}
-		else if(fieldDefinitions.type == 'select')
-		{
-			
-		}
-		else if(fieldDefinitions.type == 'radio')
-		{
-			
-		}
-		else
-		{
-			var value = settings[fieldName] ? settings[fieldName] : '';
-			var attributes = generateHtmlAttributes(fieldDefinitions);
-			html += '<input name="themesettings[' + fieldName + ']" type="' + fieldDefinitions.type + '" value="'+value+'"' + attributes + '>';
-		}
-		
-		return html;
+		var toggleElement = document.getElementById(myid);
+		toggleElement.classList.toggle(myclass);
 	}
 	
-	/* generate field attributes */
-	function generateHtmlAttributes(fieldDefinitions)
-	{
-		var attributes 	= '',
-			attr 		= getAttributes(),
-			attrValues	= getAttributeValues();
+	/**********************************
+	** 		START THEMESWITCH	 	 **
+	**********************************/
 		
-		for(var fieldName in fieldDefinitions)
-		{
-			if(attr.indexOf(fieldName) > -1)
-			{
-				attributes += ' ' + fieldName;
-			}
-			if(attrValues.indexOf(fieldName) > -1)
-			{
-				attributes += ' ' + fieldName + '="' + fieldDefinitions[fieldName] + '"';
-			}
-		}
-		return attributes;
+	if(document.getElementById("baseapp"))
+	{
+		getVersions('app', false);
 	}
 	
-	function getAttributes()
-	{	
-		return ['autofocus','checked','disabled','formnovalidate','multiple','readonly','required'];
+	if(document.getElementById("plugins"))
+	{
+		getVersions('plugins', document.getElementsByClassName("fc-plugin-version"));
 	}
 	
-	function getAttributeValues()
+	if(document.getElementById("themes"))
 	{
-		return ['id','autocomplete','placeholder','size','rows','cols','class','pattern'];
+		// getVersions('theme', themeSwitch.value);
 	}
 	
-	
 	/**********************************
 	** 		START VERSIONING	 	 **
 	**********************************/
 			
-	function getVersions(plugins, theme)
+	function getVersions(name, value)
 	{
 		var getPost 	= 'GET';
 		url 			= 'http://typemill.net/api/v1/checkversion?';
 		
-		if(plugins)
+		if(name == 'plugins')
 		{
 			var pluginList = '&plugins=';
-			for (var i = 0, len = plugins.length; i < len; i++)
+			for (var i = 0, len = value.length; i < len; i++)
 			{
-				pluginList += plugins[i].id + ',';
+				pluginList += value[i].id + ',';
 			}
 			
 			url += pluginList;
 		}
 
-		if(theme)
+		if(name == 'theme')
 		{
-			url += '&themes=' + theme; 
+			url += '&themes=' + value; 
 		}
 
 		sendJson(function(response)
@@ -293,17 +165,17 @@
 			{
 				var versions = JSON.parse(response);
 				
-				if(versions.version)
+				if(name == 'app' && versions.version)
 				{
 					updateTypemillVersion(versions.version);
 				}
-				if(versions.plugins)
+				if(name == 'plugins' && versions.plugins)
 				{
 					updatePluginVersions(versions.plugins);
 				}
-				if(versions.themes[theme])
+				if(name == 'theme' && versions.themes[value])
 				{
-					updateThemeVersion(versions.themes[theme]);					
+					updateThemeVersion(versions.themes[value]);					
 				}
 			}
 			else
@@ -333,7 +205,7 @@
 		if(!document.getElementById('app-banner'))
 		{
 			var localTypemillVersion = document.getElementById('baseapp').dataset.version;
-			if(cmpVersions(typemillVersion,localTypemillVersion) > 0)
+			if(localTypemillVersion && cmpVersions(typemillVersion,localTypemillVersion) > 0)
 			{
 				addUpdateNotice('baseapp', 'app-banner', typemillVersion, 'http://typemill.net');
 			}			
@@ -344,7 +216,7 @@
 	{
 		var localThemeVersion = document.getElementById('themeVersion').innerHTML;
 		var themeUrl = document.getElementById('themeLink').href;
-		if(cmpVersions(themeVersion,localThemeVersion) > 0)
+		if(localThemeVersion && themeUrl && cmpVersions(themeVersion,localThemeVersion) > 0)
 		{
 			addUpdateNotice('themes', 'theme-banner', themeVersion, themeUrl);
 		}
@@ -382,15 +254,15 @@
 
 	
 	/*************************************
-	**	PLUGINS: ACTIVATE/OPEN CLOSE	**
+	** 		CARDS: ACTIVATE/OPEN CLOSE	**
 	*************************************/
 	
-	var plugins = document.getElementsByClassName("plugin");
-	if(plugins)
+	var cards = document.getElementsByClassName("card");
+	if(cards)
 	{
-		for (var i = 0, len = plugins.length; i < len; i++)
+		for (var i = 0, len = cards.length; i < len; i++)
 		{
-			plugins[i].addEventListener("click", function(e)
+			cards[i].addEventListener("click", function(e)
 			{
 				if(e.target.classList.contains("fc-active"))
 				{
@@ -398,7 +270,7 @@
 				}
 				if(e.target.classList.contains("fc-settings"))
 				{
-					this.getElementsByClassName("pluginFields")[0].classList.toggle("open");
+					this.getElementsByClassName("cardFields")[0].classList.toggle("open");
 				}
 			});
 		}

+ 0 - 21
system/author/layout.twig

@@ -1,21 +0,0 @@
-<!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="Setup your TYPEMILL website">
-		
-		<link rel="stylesheet" href="{{ base_url }}/system/author/css/normalize.css" />
-		<link rel="stylesheet" href="{{ base_url }}/system/author/css/style.css" />
-		<link rel="stylesheet" href="{{ base_url }}/system/author/css/color-picker.min.css" />
-	</head>
-	<body>
-		<div class="main">
-			{% block content %}{% endblock %}
-		</div>
-		<script src="{{ base_url }}/system/author/js/color-picker.min.js"></script>
-		<script src="{{ base_url }}/system/author/js/author.js"></script>
-	</body>
-</html>

+ 39 - 0
system/author/layouts/layout.twig

@@ -0,0 +1,39 @@
+<!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/mstile-144x144.png" />		
+		<link rel="icon" type="image/png" href="{{ base_url }}/system/author/img/favicon-32x32.png" sizes="32x32" />
+		<link rel="icon" type="image/png" href="{{ base_url }}/system/author/img/favicon-16x16.png" sizes="16x16" />
+		<link rel="apple-touch-icon-precomposed" sizes="144x144" href="{{ base_url }}/system/author/img/apple-touch-icon-144x144.png" />
+		<link rel="apple-touch-icon-precomposed" sizes="152x152" href="{{ base_url }}/system/author/img/apple-touch-icon-152x152.png" />		
+		
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/fontello/css/fontello.css" />
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/normalize.css" />
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/style.css" />
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/color-picker.min.css" />
+	</head>
+	<body>	
+		<header class="main-header">
+			{% include 'partials/navi.twig' %}
+		</header>
+		{% include 'partials/flash.twig' %}
+		<div class="main">
+			<aside class="sidebar">
+				{% include 'partials/aside.twig' %}
+			</aside>
+			<article>
+				{% block content %}{% endblock %}
+			</article>
+			<footer></footer>
+		</div>
+		<script src="{{ base_url }}/system/author/js/color-picker.min.js"></script>
+		<script src="{{ base_url }}/system/author/js/author.js"></script>
+	</body>
+</html>

+ 31 - 0
system/author/layouts/layoutAuth.twig

@@ -0,0 +1,31 @@
+<!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/mstile-144x144.png" />		
+		<link rel="icon" type="image/png" href="{{ base_url }}/system/author/img/favicon-32x32.png" sizes="32x32" />
+		<link rel="icon" type="image/png" href="{{ base_url }}/system/author/img/favicon-16x16.png" sizes="16x16" />
+		<link rel="apple-touch-icon-precomposed" sizes="144x144" href="{{ base_url }}/system/author/img/apple-touch-icon-144x144.png" />
+		<link rel="apple-touch-icon-precomposed" sizes="152x152" href="{{ base_url }}/system/author/img/apple-touch-icon-152x152.png" />
+		
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/fontello/css/fontello.css" />		
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/normalize.css" />
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/style.css" />
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/color-picker.min.css" />
+	</head>
+	<body>	
+		{% include 'partials/flash.twig' %}
+		<div class="main">
+			
+			{% block content %}{% endblock %}
+
+		</div>
+	</body>
+</html>

+ 33 - 0
system/author/layouts/layoutContent.twig

@@ -0,0 +1,33 @@
+<!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="Edit your TYPEMILL website" />
+		
+		<meta name="msapplication-TileColor" content="#F9F8F6" />
+		<meta name="msapplication-TileImage" content="{{ base_url }}/system/author/img/mstile-144x144.png" />		
+		<link rel="icon" type="image/png" href="{{ base_url }}/system/author/img/favicon-32x32.png" sizes="32x32" />
+		<link rel="icon" type="image/png" href="{{ base_url }}/system/author/img/favicon-16x16.png" sizes="16x16" />
+		<link rel="apple-touch-icon-precomposed" sizes="144x144" href="{{ base_url }}/system/author/img/apple-touch-icon-144x144.png" />
+		<link rel="apple-touch-icon-precomposed" sizes="152x152" href="{{ base_url }}/system/author/img/apple-touch-icon-152x152.png" />
+
+		
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/fontello/css/fontello.css" />
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/normalize.css" />
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/style.css" />
+		<link rel="stylesheet" href="{{ base_url }}/system/author/css/color-picker.min.css" />
+	</head>
+	<body>	
+		<header class="main-header">
+			{% include 'partials/navi.twig' %}
+		</header>
+		{% include 'partials/flash.twig' %}
+		<div class="main">
+			{% block content %}{% endblock %}
+		</div>
+		<script src="{{ base_url }}/system/author/js/author.js"></script>
+	</body>
+</html>

+ 17 - 0
system/author/partials/aside.twig

@@ -0,0 +1,17 @@
+<nav id="sidebar-menu" class="sidebar-menu">
+	<div id="mobile-menu" class="menu-action">Menu</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>
+</nav>

+ 5 - 0
system/author/partials/asideContent.twig

@@ -0,0 +1,5 @@
+<nav class="side-menu">
+	<h3>Contribute</h3>
+	<p>Are you a frontend developer and do you want to participate and contribute?</p>
+	<p>Contribute are welcome on <a href="https://github.com/trendschau/typemill">GITHUB</a>.</p>
+</nav>

+ 31 - 0
system/author/partials/flash.twig

@@ -0,0 +1,31 @@
+{% if flash.getMessage('info') %}
+
+	<div class="alert alert-info">
+		{{ flash.getMessage('info') | first }}
+	</div>
+
+{% endif %}
+
+{% if messages.info %}
+
+	<div class="alert alert-error">
+		{{ messages.info | first }}
+	</div>
+
+{% endif %}
+
+{% if flash.getMessage('error') %}
+
+	<div class="alert alert-error">
+		{{ flash.getMessage('error') | first }}
+	</div>
+
+{% endif %}
+
+{% if messages.error %}
+
+	<div class="alert alert-error">
+		{{ messages.error | first }}
+	</div>
+
+{% endif %}

+ 11 - 0
system/author/partials/navi.twig

@@ -0,0 +1,11 @@
+<nav class="header-navi">
+	<div class="logo">
+		<a href="#">Typemill</a>
+	</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>
+			<a href="{{ path_for('auth.logout') }}"><i class="icon-off"></i><span class="nav-label">  Logout</span></a></li>
+	</ul>
+</nav>

+ 124 - 0
system/author/settings/plugins.twig

@@ -0,0 +1,124 @@
+{% extends 'layouts/layout.twig' %}
+{% block title %}Setup{% endblock %}
+{% set startpage = old.settings.startpage ? old.settings.startpage : settings.startpage %}
+
+{% block content %}
+	
+	<div class="formWrapper">
+
+		<form method="POST" action="{{ path_for('plugins.save') }}">
+
+			<section id="plugins" class="settings">
+			
+				<header>
+					<h1>Plugins</h1>
+				</header>
+				
+				{% for pluginName,plugin in plugins %}
+				
+					<fieldset class="medium card{{ errors[pluginName] ? ' errors' : '' }}">
+						<div class="cardInner cardHead">
+							<header>
+								<legend>{{ pluginName }}</legend>							
+								<div class="cardActive">
+									<label class="control-group">Active
+										<input type="checkbox" class="fc-active" name="{{pluginName}}[active]"{% if plugin.settings.active %} checked {% endif %}>
+										<span class="checkmark"></span>
+									</label>
+								</div>
+							</header>
+
+							<p>{{ plugin.description ? plugin.description : 'No description' }}</p>
+							
+							<ul class="cardInfo">
+								<li id="{{ pluginName }}" class="fc-plugin-version">{{ 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="cardInner cardFields{{ errors[pluginName] ? ' open' : '' }}">
+							{% for field in plugin.forms.fields %}
+													
+							<div class="cardField{{ errors[pluginName][field.name] ? ' error' : '' }}">
+								<label for="{{ pluginName }}[{{ field.name }}]">{{ field.getLabel() }}
+									{% if field.getAttribute('required') %}<strong><abbr title="required">*</abbr></strong>{% endif %}
+									{% if field.help %}<div class="help">?<span class="tooltip">{{field.help|slice(0,100)}}</span></div>{% endif %}
+								</label>
+								
+								{% if field.type == 'textarea' %}
+								
+									<textarea name="{{ pluginName }}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>{{ field.getContent() }}</textarea>
+								
+								{% elseif field.type == 'checkbox' %}
+												
+									<label class="control-group">{{ field.description }}
+										<input type="checkbox" name="{{pluginName}}[{{ field.name }}]"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
+										<span class="checkmark"></span>
+									</label>							
+								
+								{% elseif field.type == 'checkboxlist' %}
+
+									{% set options = field.getOptions() %}
+									
+									{% for value,label in options %}
+												
+										<label class="control-group">{{ label }}
+											<input type="checkbox" name="{{pluginName}}[{{ field.name }}]" value="{{value}}">
+											<span class="checkmark"></span>
+										</label>
+												
+									{% endfor %}
+								
+								{% elseif field.type == 'select' %}
+								
+									{% set options = field.getOptions() %}
+									
+									<select name="{{pluginName}}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>
+										{% for value,label in options %}
+											<option value="{{ value }}">{{ label }}</option>
+										{% endfor %}
+									</select>
+
+								{% elseif field.type == 'radio' %}
+									
+									{% set options = field.getOptions() %}
+
+									{% for value,label in options %}
+
+										<label class="control-group">{{ label }}
+											<input type="radio" name="{{pluginName}}[{{ field.name }}]" value="{{value}}">
+											<span class="radiomark"></span>
+										</label>
+									
+									{% endfor %}
+									
+								{% else %}
+								
+									<input name="{{pluginName}}[{{ field.name }}]" type="{{ field.type }}"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
+								
+								{% endif %}
+								
+								{% if errors[pluginName][field.name] %}
+									<span class="error">{{ errors[pluginName][field.name] | first }}</span>
+								{% endif %}
+								
+							</div>
+							{% endfor %}
+						</div>
+							<button type="button" class="plugin-button fc-settings{{ plugin.settings.active ? ' active' : '' }}{{ plugin.forms.fields|length > 0 ? ' has-settings' : ' no-settings'}}">{{ plugin.forms.fields|length > 0 ? 'Settings' : 'No Settings'}}</button>
+						</div>
+					</fieldset>
+					
+				{% endfor %}
+			
+			</section>
+			
+			<input type="submit" value="Save All Settings" />
+
+			{{ csrf_field() | raw }}
+			
+		</form>
+	
+	</div>
+	
+{% endblock %}

+ 75 - 0
system/author/settings/system.twig

@@ -0,0 +1,75 @@
+{% extends 'layouts/layout.twig' %}
+{% block title %}Setup{% endblock %}
+{% set startpage = old.settings.startpage ? old.settings.startpage : settings.startpage %}
+
+{% block content %}
+	
+	<div class="formWrapper">
+
+		<form method="POST" action="{{ path_for('settings.save') }}">
+		
+			<section id="baseapp" class="settings" data-version="{{ settings.version }}">
+
+				<header>
+					<h1>System</h1>
+				</header>
+				
+				<fieldset>
+				
+					<div class="medium{{ errors.settings.title ? ' error' : '' }}">
+						<label for="settings[title]">Website Title *</label>
+						<input type="text" name="settings[title]" id="title" pattern=".{2,20}" required title="Use 2 to 20 characters." value="{{ old.settings.title ? old.settings.title : settings.title }}" />
+						{% if errors.settings.title %}
+							<span class="error">{{ errors.settings.title | first }}</span>
+						{% endif %}
+					</div><div class="medium{{ errors.settings.author ? ' error' : '' }}">
+						<label for="settings[author]">Author</label>
+						<input type="text" name="settings[author]" id="author" pattern="[^()/><\]\{\}\?\$@#!*%§=[\\\x22;:|]{2,40}" value="{{ old.settings.author ? old.settings.author : settings.author }}" title="Use 2 to 40 characters. Only the following special characters are allowed: a,b a.b a-b a_b a&b a+b" />
+						{% if errors.settings.author %}
+							<span class="error">{{ errors.settings.author | first }}</span>
+						{% endif %}
+					</div><div class="medium{{ errors.settings.copyright ? ' error' : '' }}">
+						<label for="settings[copyright]">Copyright/Licence</label>
+						<select name="settings[copyright]" id="copyright">
+							{% for copy in copyright %}
+								<option value="{{ copy }}"{% if copy == old.settings.copyright %} selected{% endif %}>{{ copy }}</option>
+							{% endfor %}
+						</select>
+						{% if errors.settings.copyright %}
+							<span class="error">{{ errors.settings.copyright | first }}</span>
+						{% endif %}
+					</div><div class="medium{{ errors.settings.year ? ' error' : '' }}">
+						<label for="settings[year]">Year *</label>
+						<input type="text" name="settings[year]" id="year" value="{{ old.settings.year ? old.settings.year : "now"|date("Y") }}" pattern="[0-9]{4}" required title="Use a valid year, e.g. 2017" />
+						{% if errors.settings.year %}
+							<span class="error">{{ errors.settings.year | first }}</span>
+						{% endif %}
+					</div><div class="medium{{ errors.settings.language ? ' error' : '' }}">
+						<label for="settings[language]">Language</label>
+						<select name="settings[language]" id="language">
+							{% for key,lang in languages %}
+								<option value="{{ key }}"{% if (key == old.settings.language or key == locale) %} selected{% endif %}>{{ lang }}</option>
+							{% endfor %}
+						</select>
+						{% if errors.settings.language %}
+							<span class="error">{{ errors.settings.language | first }}</span>
+						{% endif %}
+					</div><div class="medium">
+						<span class="label">Startpage</span>
+						<label class="control-group">Startpage is designed as landing-page.
+							<input name="settings[startpage]" type="checkbox" id="startpage"{{ startpage ? ' checked' : '' }}>
+							<span class="checkmark"></span>
+						</label>
+					</div>
+				</fieldset>
+
+			</section>
+			<input type="submit" value="Save All Settings" />
+
+			{{ csrf_field() | raw }}
+			
+		</form>
+	
+	</div>
+	
+{% endblock %}

+ 137 - 0
system/author/settings/themes.twig

@@ -0,0 +1,137 @@
+{% extends 'layouts/layout.twig' %}
+{% block title %}Setup{% endblock %}
+{% set startpage = old.settings.startpage ? old.settings.startpage : settings.startpage %}
+
+{% block content %}
+	
+	<div class="formWrapper">
+
+			<section id="themes" class="settings">
+			
+				<header>
+					<h1>Themes</h1>
+				</header>
+				
+				{% for themeName, theme in themes %}
+				
+					<form method="POST" action="{{ path_for('themes.save') }}">
+					
+						<fieldset class="card{{ errors[themeName] ? ' errors' : '' }}">
+							<div class="cardInner cardHead">
+								
+								{% if theme.img %}
+									<img src="{{ base_url() }}/themes/{{themeName}}/{{themeName}}.jpg" />
+								{% else %}
+									<div class="no-preview">No Preview</div>
+								{% endif %}
+
+								<div class="cardContent">
+									<h2>{{ themeName }}</h2>
+									<p>{{ theme.description }}</p>
+									<ul class="cardInfo">
+										<li id="{{ themeName }}">{{ 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 class="cardInner cardFields{{ errors[themeName] ? ' open' : '' }}">
+								
+									{% for field in theme.forms.fields %}
+															
+										<div class="cardField{{ errors[themeName][field.name] ? ' error' : '' }}">
+											<label for="{{ themeName }}[{{ field.name }}]">{{ field.getLabel() }}
+												{% if field.getAttribute('required') %}<strong><abbr title="required">*</abbr></strong>{% endif %}
+												{% if field.help %}<div class="help">?<span class="tooltip">{{field.help|slice(0,100)}}</span></div>{% endif %}
+											</label>
+										
+											{% if field.type == 'textarea' %}
+										
+												<textarea name="{{ themeName }}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>{{ field.getContent() }}</textarea>
+										
+											{% elseif field.type == 'checkbox' %}
+														
+												<label class="control-group">{{ field.description }}
+													<input type="checkbox" name="{{themeName}}[{{ field.name }}]"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
+													<span class="checkmark"></span>
+												</label>							
+										
+											{% elseif field.type == 'checkboxlist' %}
+
+												{% set options = field.getOptions() %}
+											
+												{% for value,label in options %}
+												
+													<label class="control-group">{{ label }}
+														<input type="checkbox" name="{{fieldName}}[{{ field.name }}]" value="{{value}}">
+														<span class="checkmark"></span>
+													</label>
+														
+												{% endfor %}
+										
+											{% elseif field.type == 'select' %}
+										
+												{% set options = field.getOptions() %}
+											
+												<select name="{{themeName}}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>
+													{% for value,label in options %}
+														<option value="{{ value }}">{{ label }}</option>
+													{% endfor %}
+												</select>
+
+											{% elseif field.type == 'radio' %}
+											
+												{% set options = field.getOptions() %}
+
+												{% for value,label in options %}
+
+													<label class="control-group">{{ label }}
+														<input type="radio" name="{{ themeName }}[{{ field.name }}]" value="{{value}}">
+														<span class="radiomark"></span>
+													</label>
+											
+												{% endfor %}
+											
+											{% else %}
+										
+												<input name="{{themeName}}[{{ field.name }}]" type="{{ field.type }}"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
+										
+											{% endif %}
+										
+											{% if errors[themeName][field.name] %}
+												<span class="error">{{ errors[themeName][field.name] | first }}</span>
+											{% endif %}
+										
+										</div>
+								
+									{% 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' : 'No Settings'}}</button>
+								</div>
+								<div class="medium">
+									<input type="submit" value="Save Theme" />								
+								</div>
+								
+							</div>
+							
+						</fieldset>
+						
+						{{ csrf_field() | raw }}
+
+					</form>
+				
+				{% endfor %}
+
+			</section>
+			
+		</form>
+	
+	</div>
+	
+{% endblock %}

+ 89 - 0
system/author/settings/user.twig

@@ -0,0 +1,89 @@
+{% extends 'layouts/layout.twig' %}
+{% block title %}User{% endblock %}
+
+{% block content %}
+	
+	<div class="formWrapper">
+
+		<form method="POST" action="{{ path_for('user.update') }}">
+		
+			<section id="user" class="settings">
+
+				<header>
+					<h1>Edit User</h1>
+				</header>
+				
+				<fieldset class="auth">
+				
+					<div class="large{{ errors.username ? ' errors' : '' }}">
+						<label for="username">Username <small>(not editable)</small></label>
+						<input type="text" name="showusername" value="{{ old.username ? old.username : userdata.username }}" required disabled>
+						<input type="hidden" name="username" value="{{ userdata.username }}">
+						{% if errors.username %}
+							<span class="error">{{ errors.username | first }}</span>
+						{% endif %}
+					</div>
+					
+					<div class="large{{ errors.email ? ' errors' : '' }}">
+						<label for="email">E-Mail <abbr title="required">*</abbr></label>
+						<input type="text" name="email" value="{{ old.email ? old.email : userdata.email }}" required>
+						{% if errors.email %}
+							<span class="error">{{ errors.email | first }}</span>
+						{% 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>					
+					
+					<div class="large{{ errors.password ? ' errors' : '' }}">
+						<label for="password">Actual Password</label>
+						<input type="password" name="password">
+						{% if errors.password %}
+							<span class="error">{{ errors.password | first }}</span>
+						{% endif %}
+					</div>
+
+					<div class="large{{ errors.newpassword ? ' errors' : '' }}">
+						<label for="newpassword">New Password</label>
+						<input type="password" name="newpassword">
+						{% if errors.newpassword %}
+							<span class="error">{{ errors.newpassword | first }}</span>
+						{% endif %}
+					</div>
+					
+				</fieldset>
+
+			</section>
+			<input type="submit" value="Save User" />
+
+			{{ csrf_field() | raw }}
+			<div class="actionLink">
+				<a href="#" id="openModal" class="openModal">Delete User</a>
+			</div>
+		</form>		
+	</div>
+	
+	<div id="modalWindow" class="modal">
+		<div class="modalInner">
+			<div id="closeModal" class="closeModal">X</div>
+			<form method="POST" action="{{ path_for('user.delete') }}">
+				<h2>Delete {{ userdata.username }}</h2>
+				<p>Do you really want to delete the user {{userdata.username}}? Please confirm.</p>
+				<input type="hidden" name="username" value="{{userdata.username}}">
+				<input type="submit" value="Delete user">
+				{{ csrf_field() | raw }}
+			</form>
+		</div>
+	</div>
+	
+	
+{% endblock %}

+ 36 - 0
system/author/settings/userlist.twig

@@ -0,0 +1,36 @@
+{% extends 'layouts/layout.twig' %}
+{% block title %}User{% endblock %}
+
+{% block content %}
+	
+	<div class="formWrapper">
+
+		<section id="users" class="settings">
+
+			<header>
+				<h1>All Users</h1>
+			</header>
+			
+			<ul class="userlist">
+				{% for user in userdata %}
+				
+					<li class="row">
+						<ul>
+							<li class="col username">{{ user.username }}
+							</li><li class="col userrole">{{ user.userrole }}
+							</li><li class="col email">{{ user.email }}
+							</li><li class="col edit"><a href="{{ path_for('user.show', {'username' : user.username }) }}">edit</a></li>
+						</ul>
+					</li>
+				
+				{% endfor %}
+			</ul>
+			
+			<div class="medium">
+				<a class="button" href="{{ path_for('user.new') }}">Create New User</a>
+			</div>
+		</section>
+		
+	</div>
+	
+{% endblock %}

+ 63 - 0
system/author/settings/usernew.twig

@@ -0,0 +1,63 @@
+{% extends 'layouts/layout.twig' %}
+{% block title %}User{% endblock %}
+
+{% block content %}
+	
+	<div class="formWrapper">
+
+		<form method="POST" action="{{ path_for('user.create') }}">
+		
+			<section id="new-user" class="settings">
+
+				<header>
+					<h1>Create New User</h1>
+				</header>
+				
+				<fieldset class="auth">
+
+					<div class="large{{ errors.username ? ' errors' : '' }}">
+						<label for="username">Username <abbr title="required">*</abbr></label>
+						<input type="text" name="username" value="{{ old.username ? old.username : '' }}" required>
+						{% if errors.username %}
+							<span class="error">{{ errors.username | first }}</span>
+						{% endif %}
+					</div>
+					
+					<div class="large{{ errors.email ? ' errors' : '' }}">
+						<label for="email">E-Mail <abbr title="required">*</abbr></label>
+						<input type="text" name="email" value="{{ old.email ? old.email : '' }}" required>
+						{% if errors.email %}
+							<span class="error">{{ errors.email | first }}</span>
+						{% 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) %} selected{% endif %}>{{ role }}</option>
+							{% endfor %}
+						</select>
+						{% if errors.userrole %}
+							<span class="error">{{ errors.userrole | first }}</span>
+						{% endif %}
+					</div>					
+					
+					<div class="large{{ errors.password ? ' errors' : '' }}">
+						<label for="password">Password <abbr title="required">*</abbr></label>
+						<input type="password" name="password" required>
+						{% if errors.password %}
+							<span class="error">{{ errors.password | first }}</span>
+						{% endif %}
+					</div>
+					
+				</fieldset>
+
+			</section>
+			<input type="submit" value="Create User" />
+
+			{{ csrf_field() | raw }}
+		</form>		
+	</div>	
+	
+{% endblock %}

+ 0 - 218
system/author/setup.twig

@@ -1,218 +0,0 @@
-{% extends '/layout.twig' %}
-{% block title %}Setup{% endblock %}
-{% set startpage = old.settings.startpage ? old.settings.startpage : settings.startpage %}
-
-{% block content %}
-	
-	<div class="formWrapper">
-
-		<form method="POST" action="{{ base_url() }}/setup">
-
-			<input type="submit" value="Save All Settings" />
-		
-			<section id="baseapp" data-version="{{ settings.version }}">
-
-				<header>
-					<h1>Basic Settings</h1>
-				</header>
-				
-				<fieldset>
-				
-					<div class="medium{{ errors.settings.title ? ' error' : '' }}">
-						<label for="settings[title]">Website Title *</label>
-						<input type="text" name="settings[title]" id="title" pattern=".{2,20}" required title="Use 2 to 20 characters." value="{{ old.settings.title ? old.settings.title : settings.title }}" />
-						{% if errors.settings.title %}
-							<span class="error">{{ errors.settings.title | first }}</span>
-						{% endif %}
-					</div><div class="medium{{ errors.settings.author ? ' error' : '' }}">
-						<label for="settings[author]">Author</label>
-						<input type="text" name="settings[author]" id="author" pattern="[^()/><\]\{\}\?\$@#!*%§=[\\\x22;:|]{2,40}" value="{{ old.settings.author ? old.settings.author : settings.author }}" title="Use 2 to 40 characters. Only the following special characters are allowed: a,b a.b a-b a_b a&b a+b" />
-						{% if errors.settings.author %}
-							<span class="error">{{ errors.settings.author | first }}</span>
-						{% endif %}
-					</div><div class="medium{{ errors.settings.copyright ? ' error' : '' }}">
-						<label for="settings[copyright]">Copyright/Licence</label>
-						<select name="settings[copyright]" id="copyright">
-							{% for copy in copyright %}
-								<option value="{{ copy }}"{% if copy == old.settings.copyright %} selected{% endif %}>{{ copy }}</option>
-							{% endfor %}
-						</select>
-						{% if errors.settings.copyright %}
-							<span class="error">{{ errors.settings.copyright | first }}</span>
-						{% endif %}
-					</div><div class="medium{{ errors.settings.year ? ' error' : '' }}">
-						<label for="settings[year]">Year *</label>
-						<input type="text" name="settings[year]" id="year" value="{{ old.settings.year ? old.settings.year : "now"|date("Y") }}" pattern="[0-9]{4}" required title="Use a valid year, e.g. 2017" />
-						{% if errors.settings.year %}
-							<span class="error">{{ errors.settings.year | first }}</span>
-						{% endif %}
-					</div><div class="medium{{ errors.settings.language ? ' error' : '' }}">
-						<label for="settings[language]">Language</label>
-						<select name="settings[language]" id="language">
-							{% for key,lang in languages %}
-								<option value="{{ key }}"{% if (key == old.settings.language or key == locale) %} selected{% endif %}>{{ lang }}</option>
-							{% endfor %}
-						</select>
-						{% if errors.settings.language %}
-							<span class="error">{{ errors.settings.language | first }}</span>
-						{% endif %}
-					</div><div class="medium">
-						<span class="label">Startpage</span>
-						<label class="control-group">Startpage is designed as landing-page.
-							<input name="settings[startpage]" type="checkbox" id="startpage"{{ startpage ? ' checked' : '' }}>
-							<span class="checkmark"></span>
-						</label>
-					</div>
-				</fieldset>
-
-			</section>
-
-			<section id="themes">
-			
-				<header>
-					<h2>Themes</h2>
-				</header>
-				
-				<div class="medium{{ errors.settings.theme ? ' error' : '' }}">
-					<label for="settings[theme]">Theme</label>
-					<select name="settings[theme]" id="themeSwitch">
-						{% for theme in themes %}
-							<option value="{{ theme }}"{% if theme == old.settings.theme %} selected{% endif %}>{{ theme }}</option>
-						{% endfor %}
-					</select>
-					{% if errors.settings.theme %}
-						<span class="error">{{ errors.settings.theme | first }}</span>
-					{% endif %}
-				</div><div class="medium">
-					<label>Theme Info</label>
-					<ul class="themeInfo">
-						<li id="themeVersion">
-						</li><li id="themeLicence">
-						</li><li id="themeAuthor">
-						</li><li id="themeUrl">
-						</li>
-					</ul>
-				</div>
-				
-				<div class="large">
-					<img id="themePrev" src="">
-				</div>
-				
-				<div id="themeFields"></div>
-				
-			</section>
-			
-			<section id="plugins">
-			
-				<header>
-					<h2>Plugins</h2>
-				</header>
-				
-				{% for pluginName,plugin in plugins %}
-				
-					<fieldset class="plugin{{ errors[pluginName] ? ' errors' : '' }}">
-						<div class="pluginInner pluginHead">
-							<header>
-								<legend>{{ pluginName }}</legend>							
-								<div class="pluginActive">
-									<label class="control-group">Active
-										<input type="checkbox" class="fc-active" name="{{pluginName}}[active]"{% if plugin.settings.active %} checked {% endif %}>
-										<span class="checkmark"></span>
-									</label>
-								</div>
-							</header>
-
-							<p>{{ plugin.description ? plugin.description : 'No description' }}</p>
-							
-							<ul class="pluginInfo">
-								<li id="{{ pluginName }}" class="fc-plugin-version">{{ 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="pluginInner pluginFields{{ errors[pluginName] ? ' open' : '' }}">
-							{% for field in plugin.forms.fields %}
-													
-							<div class="pluginField{{ errors[pluginName][field.name] ? ' error' : '' }}">
-								<label for="{{ pluginName }}[{{ field.name }}]">{{ field.getLabel() }}
-									{% if field.getAttribute('required') %}<strong><abbr title="required">*</abbr></strong>{% endif %}
-									{% if field.help %}<div class="help">?<span class="tooltip">{{field.help|slice(0,100)}}</span></div>{% endif %}
-								</label>
-								
-								{% if field.type == 'textarea' %}
-								
-									<textarea name="{{ pluginName }}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>{{ field.getContent() }}</textarea>
-								
-								{% elseif field.type == 'checkbox' %}
-												
-									<label class="control-group">{{ field.description }}
-										<input type="checkbox" name="{{pluginName}}[{{ field.name }}]"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
-										<span class="checkmark"></span>
-									</label>							
-								
-								{% elseif field.type == 'checkboxlist' %}
-
-									{% set options = field.getOptions() %}
-									
-									{% for value,label in options %}
-												
-										<label class="control-group">{{ label }}
-											<input type="checkbox" name="{{pluginName}}[{{ field.name }}]" value="{{value}}">
-											<span class="checkmark"></span>
-										</label>
-												
-									{% endfor %}
-								
-								{% elseif field.type == 'select' %}
-								
-									{% set options = field.getOptions() %}
-									
-									<select name="{{pluginName}}[{{ field.name }}]"{{field.getAttributeValues() }}{{ field.getAttributes() }}>
-										{% for value,label in options %}
-											<option value="{{ value }}">{{ label }}</option>
-										{% endfor %}
-									</select>
-
-								{% elseif field.type == 'radio' %}
-									
-									{% set options = field.getOptions() %}
-
-									{% for value,label in options %}
-
-										<label class="control-group">{{ label }}
-											<input type="radio" name="{{pluginName}}[{{ field.name }}]" value="{{value}}">
-											<span class="radiomark"></span>
-										</label>
-									
-									{% endfor %}
-									
-								{% else %}
-								
-									<input name="{{pluginName}}[{{ field.name }}]" type="{{ field.type }}"{{ field.getAttributeValues() }}{{ field.getAttributes() }}>
-								
-								{% endif %}
-								
-								{% if errors[pluginName][field.name] %}
-									<span class="error">{{ errors[pluginName][field.name] | first }}</span>
-								{% endif %}
-								
-							</div>
-							{% endfor %}
-						</div>
-							<button type="button" class="fc-settings{{ plugin.settings.active ? ' active' : '' }}">{{ plugin.forms.fields|length > 0 ? 'Settings' : 'No Settings'}}</button>
-						</div>
-					</fieldset>
-					
-				{% endfor %}
-			
-			</section>
-			
-			<input type="submit" value="Save All Settings" />
-
-			{{ csrf_field() | raw }}
-			
-		</form>
-	
-	</div>
-	
-{% endblock %}

+ 0 - 18
system/author/welcome.twig

@@ -1,18 +0,0 @@
-{% extends '/layout.twig' %}
-
-{% block title %}Setup Welcome{% endblock %}
-
-{% block content %}
-		
-	<div class="welcome">
-
-		<h1>Hurra!!!</h1>
-			
-		<p>Hello {{ author }}!</p>
-		<p>Your settings are stored in your settings folder now. If you want to change the settings, simply open the file "settings.yaml" in the folder "settings" and edit the setting-data.</p>
-		<p>Not sure how to start? Simply create some content for your new website or visit the homepage and read the documentation... </p>
-		<a class="button" href="{{ base_url }}">Homepage</a>
-		
-	</div>
-	
-{% endblock %}

+ 2 - 5
themes/typemill/css/style.css

@@ -54,12 +54,9 @@ pre{ border-left: 4px solid #e0474c;	}
 *  		FONTS 	    *
 ********************/
 
-header, nav, h1, h2, h3, h4, h5, h6, article .paging, article .breadcrumb, .cover .lead a, a.readMore{
-	font-family: Calibri, Helvetica, Arial, sans-serif;
+body{
+	font-family: Calibri, Helvetica, Arial, sans-serif;	
 }
-article{
-	font-family: Calibri, Helvetica, Arial, sans-serif;
-}	
 pre,code{
 	font-family: monospace;
 }

+ 6 - 6
themes/typemill/typemill.yaml

@@ -1,6 +1,6 @@
 name: Typemill Theme
 version: 1.0.4
-description: The Standard Theme For Typemill
+description: The Standard Theme For Typemill. Responsive, minimal and without any dependencies. It uses the system fonts Calibri and Helvetica. Only JavaScript is for code highlighting, skip it, if you don't need it. 
 author: Sebastian Schürmanns
 homepage: http://typemill.net
 licence: MIT
@@ -13,13 +13,13 @@ forms:
   fields:
 
     chapter:
-      type: input
+      type: text
       label: chapter
       placeholder: Add Name for Chapter
-      falsch: falsch
-      id: MyId
+      required: true
 
     start:
-      type: input
+      type: text
       label: Start-Button
-      placeholder: Add Label for Start-Button      
+      placeholder: Add Label for Start-Button
+      required: true

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů