1
0
mirror of https://github.com/typemill/typemill.git synced 2025-04-14 17:21:59 +02:00

Version 1.1.3. Admin Dashboard

This commit is contained in:
Sebastian 2018-04-18 19:49:12 +02:00
parent c5c84c96bc
commit f8dc7093bf
65 changed files with 3285 additions and 1044 deletions

2
.gitignore vendored
View File

@ -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

View File

@ -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]

2
cache/lastCache.txt vendored
View File

@ -1 +1 @@
1521057612
1524067221

View File

@ -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));
}
}
}
}

View File

@ -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'));
}
}

View File

@ -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));
}
}

View File

@ -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);
}

View File

@ -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'];
}

View File

@ -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'
);
}
}

View File

@ -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)
$user = new User();
/* set user as admin */
$params['userrole'] = 'administrator';
/* get userroles for validation */
$userroles = $user->getUserroles();
/* validate user */
if($validate->newUser($params, $userroles))
{
$copyright = $this->getCopyright();
$themes = $this->getThemes();
$appSettings['startpage'] = isset($appSettings['startpage']) ? true : false;
$userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']);
$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']);
// 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;
}
/* 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);
}
}
}
/* use the input data */
$pluginSettings[$pluginName] = $params[$pluginName];
}
/* create initial user */
$username = $user->createUser($userdata);
/* deactivate the plugin, if there is no active flag */
if(!isset($params[$pluginName]['active']))
if($username)
{
$pluginSettings[$pluginName]['active'] = false;
/* login user */
$user->login($username);
/* 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());
}
}
if(!is_writable($this->c->get('settings')['settingsPath']))
{
$_SESSION['errors']['folder'] = 'Your settings-folder is not writable';
}
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);
$this->c->flash->addMessage('error', 'Please check your input and try again');
return $response->withRedirect($this->c->router->pathFor('setup.show'));
}
}
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;
}
}
}
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);
}
}
return $response->withJson(['error' => 'no data found'], 404);
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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
system/Models/User.php Normal file
View File

@ -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]);
}
}

View File

@ -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,27 +97,39 @@ 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");
$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);
return $this->validationResult($v);
}
public function username($username)
{
$v = new Validator($username);
$v->rule('alphaNum', 'username')->message("Only alpha-numeric characters allowed");
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
return $this->validationResult($v);
}
@ -117,28 +140,12 @@ class Validation
* @return obj $v the validation object passed to a result method.
*/
public function validatePassword(array $params)
public function newPassword(array $params)
{
$v = new Validator($params);
$v->rule('required', ['password', 'password_old']);
$v->rule('lengthBetween', 'password', 5, 50);
$v->rule('checkPassword', 'password_old');
return $this->validationResult($v);
}
/**
* validation for password reset
*
* @param array $params with form data.
* @return obj $v the validation object passed to a result method.
*/
public function validateResetPassword(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
*

View File

@ -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))
{
throw new Exception("Please make the folder '{$folder}' writable.");
return true;
}
else
{
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)

View File

@ -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;
}
}

View File

@ -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');

View File

@ -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');

View File

@ -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();

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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/

View File

@ -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

View File

@ -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"
}
]
}

View File

@ -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);
}
}

View File

@ -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'; } /* '' */

File diff suppressed because one or more lines are too long

View File

@ -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;'); }

View File

@ -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;'); }

View File

@ -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'; } /* '' */

View File

@ -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>

Binary file not shown.

View File

@ -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>

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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; }
/********************
* HEADLINES *
* SETUP FORM *
********************/
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; }
.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;
}
/********************
* AUTHOR NAVI *
********************/
.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;
}
fieldset.plugin{
/*****************
** CARDS **
*****************/
.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;
}
.pluginInner{
.card > .cardInner{
box-shadow: 0px 0px 5px rgba(0,0,0,0.2)
}
.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{
.card button[type="button"].theme-button{
border: 0px;
margin: 5px 0 20px;
padding: 12px;
border-radius: 3px;
min-height: 42px;
}
.card button[type="button"].theme-button:hover{
border:0px;
}
.card button[type="button"].no-settings:hover{
cursor: default;
}
.card .medium{
padding: 0px 20px;
}
.card .medium button[type="button"]{
margin: 10px 0;
}
ul.cardInfo{
font-size: 0.8em;
}
ul.pluginInfo{
padding: 0 20px;
}
ul.themeInfo{
padding: 0px 0px;
}
.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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -93,198 +93,70 @@
}
}
var openModal = document.getElementById("openModal"),
closeModal = document.getElementById("closeModal");
if(openModal && closeModal)
{
openModal.addEventListener("click", function(e){ e.preventDefault(); toggle("modalWindow", "show"); });
closeModal.addEventListener("click", function(e){ e.preventDefault(); toggle("modalWindow", "show"); });
}
var mobileMenu = document.getElementById("mobile-menu");
if(mobileMenu)
{
mobileMenu.addEventListener("click", function(e){ toggle("sidebar-menu", "expand"); });
}
function toggle(myid, myclass)
{
var toggleElement = document.getElementById(myid);
toggleElement.classList.toggle(myclass);
}
/**********************************
** 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);
themeSwitch.addEventListener('change', function()
{
removeVersionBanner('theme-banner');
getTheme(themeSwitch.value);
getVersions(false, themeSwitch.value);
});
}
function removeVersionBanner(bannerID)
if(document.getElementById("baseapp"))
{
var banner = document.getElementById(bannerID);
if(banner)
{
banner.parentElement.removeChild(banner);
}
getVersions('app', false);
}
/* use API to get theme informations from theme folder */
function getTheme(themeName)
if(document.getElementById("plugins"))
{
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);
getVersions('plugins', document.getElementsByClassName("fc-plugin-version"));
}
function addThemeInfo(themeData)
if(document.getElementById("themes"))
{
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>';
}
// getVersions('theme', themeSwitch.value);
}
/* add input fields for theme configurations in frontend */
function addThemeFields(fields, settings)
{
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);
}
}
}
/* generate an input field */
function generateHtmlField(fieldName, fieldDefinitions, settings)
{
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;
}
/* generate field attributes */
function generateHtmlAttributes(fieldDefinitions)
{
var attributes = '',
attr = getAttributes(),
attrValues = getAttributeValues();
for(var fieldName in fieldDefinitions)
{
if(attr.indexOf(fieldName) > -1)
{
attributes += ' ' + fieldName;
}
if(attrValues.indexOf(fieldName) > -1)
{
attributes += ' ' + fieldName + '="' + fieldDefinitions[fieldName] + '"';
}
}
return attributes;
}
function getAttributes()
{
return ['autofocus','checked','disabled','formnovalidate','multiple','readonly','required'];
}
function getAttributeValues()
{
return ['id','autocomplete','placeholder','size','rows','cols','class','pattern'];
}
/**********************************
** 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");
}
});
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 %}

View File

@ -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>

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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;
}

View File

@ -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