From 52b077a48c743ba4d08ac00520a0bf1ef6deef5f Mon Sep 17 00:00:00 2001 From: Thomas Lange Date: Fri, 24 Feb 2017 21:27:59 +0100 Subject: Initial commit. --- .gitignore | 2 + .htaccess | 42 + admin/auth.php | 86 ++ admin/database.php | 49 + admin/index.php | 88 ++ admin/page/delete.php | 72 + admin/page/index.php | 76 + admin/page/insert.php | 86 ++ admin/page/update.php | 96 ++ admin/post/delete.php | 72 + admin/post/index.php | 76 + admin/post/insert.php | 86 ++ admin/post/update.php | 96 ++ admin/user/delete.php | 72 + admin/user/index.php | 72 + admin/user/insert.php | 78 ++ admin/user/update.php | 89 ++ core/application.php | 136 ++ core/configuration-example.php | 103 ++ core/functions.php | 322 +++++ core/language/de.php | 194 +++ core/language/en.php | 194 +++ core/namespace/Application.php | 148 ++ core/namespace/Attribute.php | 74 + core/namespace/AttributeInterface.php | 7 + core/namespace/Database.php | 7 + core/namespace/ExceptionHandler.php | 8 + core/namespace/Factory.php | 19 + core/namespace/FactoryInterface.php | 5 + core/namespace/HTTP.php | 234 ++++ core/namespace/Item.php | 150 ++ core/namespace/ItemFactory.php | 12 + core/namespace/ItemInterface.php | 5 + core/namespace/Language.php | 48 + core/namespace/Page/Attribute.php | 22 + core/namespace/Page/Exception.php | 5 + core/namespace/Page/Factory.php | 9 + core/namespace/Page/Item.php | 29 + core/namespace/Parsedown.php | 1548 +++++++++++++++++++++ core/namespace/Post/Attribute.php | 22 + core/namespace/Post/Exception.php | 5 + core/namespace/Post/Factory.php | 9 + core/namespace/Post/Item.php | 50 + core/namespace/Template/Exception.php | 5 + core/namespace/Template/Factory.php | 18 + core/namespace/Template/Template.php | 72 + core/namespace/User/Attribute.php | 24 + core/namespace/User/Exception.php | 5 + core/namespace/User/Factory.php | 13 + core/namespace/User/Item.php | 36 + database.sql | 66 + index.php | 54 + readme.md | 20 + rsrc/image/content/computer-guy-public-domain.svg | 242 ++++ system/403.php | 29 + system/404.php | 29 + system/feed/main.php | 76 + system/page/list.php | 64 + system/page/main.php | 89 ++ system/post/list.php | 64 + system/post/main.php | 89 ++ system/search/main.php | 89 ++ system/user/list.php | 60 + system/user/main.php | 97 ++ template/admin/html/403.php | 2 + template/admin/html/404.php | 2 + template/admin/html/auth.php | 30 + template/admin/html/database.php | 26 + template/admin/html/home.php | 34 + template/admin/html/main.php | 52 + template/admin/html/page/delete.php | 4 + template/admin/html/page/form.php | 91 ++ template/admin/html/page/index.php | 10 + template/admin/html/page/insert.php | 4 + template/admin/html/page/item.php | 16 + template/admin/html/page/update.php | 4 + template/admin/html/pagination.php | 45 + template/admin/html/post/delete.php | 4 + template/admin/html/post/form.php | 91 ++ template/admin/html/post/index.php | 10 + template/admin/html/post/insert.php | 4 + template/admin/html/post/item.php | 16 + template/admin/html/post/update.php | 4 + template/admin/html/user/delete.php | 6 + template/admin/html/user/form.php | 97 ++ template/admin/html/user/index.php | 10 + template/admin/html/user/insert.php | 4 + template/admin/html/user/item.php | 15 + template/admin/html/user/update.php | 4 + template/admin/lang/de.php | 118 ++ template/admin/lang/en.php | 118 ++ template/admin/rsrc/background.png | Bin 0 -> 1245 bytes template/admin/rsrc/font/font-awesome.woff2 | Bin 0 -> 77160 bytes template/admin/rsrc/font/kadwa-n-400.woff2 | Bin 0 -> 18108 bytes template/admin/rsrc/font/ruda-n-400.woff2 | Bin 0 -> 9120 bytes template/admin/rsrc/font/ruda-n-700.woff2 | Bin 0 -> 9116 bytes template/admin/rsrc/icon-public-domain.svg | 1 + template/admin/rsrc/main.css | 275 ++++ template/admin/rsrc/main.js | 94 ++ template/standard/html/403.php | 11 + template/standard/html/404.php | 11 + template/standard/html/feed/item_page.php | 25 + template/standard/html/feed/item_post.php | 26 + template/standard/html/feed/main.php | 50 + template/standard/html/home.php | 19 + template/standard/html/main.php | 92 ++ template/standard/html/page/item.php | 20 + template/standard/html/page/list.php | 19 + template/standard/html/page/main.php | 46 + template/standard/html/pagination.php | 54 + template/standard/html/post/item.php | 20 + template/standard/html/post/list.php | 19 + template/standard/html/post/main.php | 46 + template/standard/html/search/main.php | 34 + template/standard/html/search/result.php | 36 + template/standard/html/user/item.php | 20 + template/standard/html/user/list.php | 19 + template/standard/html/user/main.php | 43 + template/standard/lang/de.php | 51 + template/standard/lang/en.php | 51 + template/standard/rsrc/font-awesome.min.css | 4 + template/standard/rsrc/font/font-awesome.woff2 | Bin 0 -> 77160 bytes template/standard/rsrc/font/ruda-n-400.woff2 | Bin 0 -> 9120 bytes template/standard/rsrc/font/ruda-n-700.woff2 | Bin 0 -> 9116 bytes template/standard/rsrc/logo.png | Bin 0 -> 2595 bytes template/standard/rsrc/main.css | 202 +++ 126 files changed, 7953 insertions(+) create mode 100644 .gitignore create mode 100644 .htaccess create mode 100644 admin/auth.php create mode 100644 admin/database.php create mode 100644 admin/index.php create mode 100644 admin/page/delete.php create mode 100644 admin/page/index.php create mode 100644 admin/page/insert.php create mode 100644 admin/page/update.php create mode 100644 admin/post/delete.php create mode 100644 admin/post/index.php create mode 100644 admin/post/insert.php create mode 100644 admin/post/update.php create mode 100644 admin/user/delete.php create mode 100644 admin/user/index.php create mode 100644 admin/user/insert.php create mode 100644 admin/user/update.php create mode 100644 core/application.php create mode 100644 core/configuration-example.php create mode 100644 core/functions.php create mode 100644 core/language/de.php create mode 100644 core/language/en.php create mode 100644 core/namespace/Application.php create mode 100644 core/namespace/Attribute.php create mode 100644 core/namespace/AttributeInterface.php create mode 100644 core/namespace/Database.php create mode 100644 core/namespace/ExceptionHandler.php create mode 100644 core/namespace/Factory.php create mode 100644 core/namespace/FactoryInterface.php create mode 100644 core/namespace/HTTP.php create mode 100644 core/namespace/Item.php create mode 100644 core/namespace/ItemFactory.php create mode 100644 core/namespace/ItemInterface.php create mode 100644 core/namespace/Language.php create mode 100644 core/namespace/Page/Attribute.php create mode 100644 core/namespace/Page/Exception.php create mode 100644 core/namespace/Page/Factory.php create mode 100644 core/namespace/Page/Item.php create mode 100644 core/namespace/Parsedown.php create mode 100644 core/namespace/Post/Attribute.php create mode 100644 core/namespace/Post/Exception.php create mode 100644 core/namespace/Post/Factory.php create mode 100644 core/namespace/Post/Item.php create mode 100644 core/namespace/Template/Exception.php create mode 100644 core/namespace/Template/Factory.php create mode 100644 core/namespace/Template/Template.php create mode 100644 core/namespace/User/Attribute.php create mode 100644 core/namespace/User/Exception.php create mode 100644 core/namespace/User/Factory.php create mode 100644 core/namespace/User/Item.php create mode 100644 database.sql create mode 100644 index.php create mode 100644 readme.md create mode 100644 rsrc/image/content/computer-guy-public-domain.svg create mode 100644 system/403.php create mode 100644 system/404.php create mode 100644 system/feed/main.php create mode 100644 system/page/list.php create mode 100644 system/page/main.php create mode 100644 system/post/list.php create mode 100644 system/post/main.php create mode 100644 system/search/main.php create mode 100644 system/user/list.php create mode 100644 system/user/main.php create mode 100644 template/admin/html/403.php create mode 100644 template/admin/html/404.php create mode 100644 template/admin/html/auth.php create mode 100644 template/admin/html/database.php create mode 100644 template/admin/html/home.php create mode 100644 template/admin/html/main.php create mode 100644 template/admin/html/page/delete.php create mode 100644 template/admin/html/page/form.php create mode 100644 template/admin/html/page/index.php create mode 100644 template/admin/html/page/insert.php create mode 100644 template/admin/html/page/item.php create mode 100644 template/admin/html/page/update.php create mode 100644 template/admin/html/pagination.php create mode 100644 template/admin/html/post/delete.php create mode 100644 template/admin/html/post/form.php create mode 100644 template/admin/html/post/index.php create mode 100644 template/admin/html/post/insert.php create mode 100644 template/admin/html/post/item.php create mode 100644 template/admin/html/post/update.php create mode 100644 template/admin/html/user/delete.php create mode 100644 template/admin/html/user/form.php create mode 100644 template/admin/html/user/index.php create mode 100644 template/admin/html/user/insert.php create mode 100644 template/admin/html/user/item.php create mode 100644 template/admin/html/user/update.php create mode 100644 template/admin/lang/de.php create mode 100644 template/admin/lang/en.php create mode 100644 template/admin/rsrc/background.png create mode 100644 template/admin/rsrc/font/font-awesome.woff2 create mode 100644 template/admin/rsrc/font/kadwa-n-400.woff2 create mode 100644 template/admin/rsrc/font/ruda-n-400.woff2 create mode 100644 template/admin/rsrc/font/ruda-n-700.woff2 create mode 100644 template/admin/rsrc/icon-public-domain.svg create mode 100644 template/admin/rsrc/main.css create mode 100644 template/admin/rsrc/main.js create mode 100644 template/standard/html/403.php create mode 100644 template/standard/html/404.php create mode 100644 template/standard/html/feed/item_page.php create mode 100644 template/standard/html/feed/item_post.php create mode 100644 template/standard/html/feed/main.php create mode 100644 template/standard/html/home.php create mode 100644 template/standard/html/main.php create mode 100644 template/standard/html/page/item.php create mode 100644 template/standard/html/page/list.php create mode 100644 template/standard/html/page/main.php create mode 100644 template/standard/html/pagination.php create mode 100644 template/standard/html/post/item.php create mode 100644 template/standard/html/post/list.php create mode 100644 template/standard/html/post/main.php create mode 100644 template/standard/html/search/main.php create mode 100644 template/standard/html/search/result.php create mode 100644 template/standard/html/user/item.php create mode 100644 template/standard/html/user/list.php create mode 100644 template/standard/html/user/main.php create mode 100644 template/standard/lang/de.php create mode 100644 template/standard/lang/en.php create mode 100644 template/standard/rsrc/font-awesome.min.css create mode 100644 template/standard/rsrc/font/font-awesome.woff2 create mode 100644 template/standard/rsrc/font/ruda-n-400.woff2 create mode 100644 template/standard/rsrc/font/ruda-n-700.woff2 create mode 100644 template/standard/rsrc/logo.png create mode 100644 template/standard/rsrc/main.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c0cee83 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +core/configuration.php \ No newline at end of file diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..c354b0b --- /dev/null +++ b/.htaccess @@ -0,0 +1,42 @@ +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# Apache configuration rules [Thomas Lange ] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# This file contains rewrite rules for Apache. It's recommended to include the # +# rules directly into your Apache configuration for a better performance. Also # +# disable the AllowOverride directive within your Apache virtualhost (if it is # +# not already globally disabled for performance and security reasons both). # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +#=============================================================================== +# Enable RewriteEngine and define RewriteBase +#=============================================================================== +RewriteEngine On +RewriteBase / + +#=============================================================================== +# ErrorDocument handler +#=============================================================================== +ErrorDocument 403 /system/403.php +ErrorDocument 404 /system/404.php + +#=============================================================================== +# Forbidden directories +#=============================================================================== +RewriteRule ^core|template/(.*)/(html|lang)/ - [F] + +#=============================================================================== +# Main rules +#=============================================================================== +RewriteRule ^(page|post|user)/([^/]+)/$ system/$1/main.php?param=$2 [L] +RewriteRule ^(page|post|user)/$ system/$1/list.php [L] +RewriteRule ^feed/(page|post)/$ system/feed/main.php?item=$1 [L] +RewriteRule ^(feed|search)/$ system/$1/main.php [L] + +#=============================================================================== +# Trailing slashes +#=============================================================================== +RewriteRule ^(page|post|user)/([^/]+)$ $1/$2/ [R,L] +RewriteRule ^(page|post|user|feed|search)$ $1/ [R,L] +RewriteRule ^feed/(post|page)$ feed/$1/ [R,L] \ No newline at end of file diff --git a/admin/auth.php b/admin/auth.php new file mode 100644 index 0000000..dbdd3ef --- /dev/null +++ b/admin/auth.php @@ -0,0 +1,86 @@ + Application::getSecurityToken(), ['action' => 'logout']])) { + session_destroy(); + HTTP::redirect(Application::getAdminURL('auth.php')); + } + + HTTP::redirect(Application::getAdminURL()); +} + +#=============================================================================== +# ELSE: Not authenticated +#=============================================================================== +else { + #=============================================================================== + # IF: Login action + #=============================================================================== + if(HTTP::issetPOST(['token' => Application::getSecurityToken()], 'username', 'password')) { + try { + $User = User\Factory::buildByUsername(HTTP::POST('username')); + + if($User->comparePassword(HTTP::POST('password'))) { + $_SESSION['auth'] = $User->getID(); + HTTP::redirect(Application::getAdminURL()); + } + + else { + $messages[] = $Language->text('authentication_failure'); + } + } catch(User\Exception $Exception){ + $fake_hash = '$2y$10$xpnwDU2HumOgGQhVpMOP9uataEF82YXizniFhSUhYjUiXF8aoDk0C'; + $fake_pass = HTTP::POST('password'); + + password_verify($fake_pass, $fake_hash); + + $messages[] = $Language->text('authentication_failure'); + } + } +} + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + $AuthTemplate = Template\Factory::build('auth'); + $AuthTemplate->set('FORM', [ + 'INFO' => [ + 'LIST' => $messages ?? [], + ], + 'DATA' => [ + 'USERNAME' => HTTP::POST('username'), + 'PASSWORD' => HTTP::POST('password'), + ], + 'TOKEN' => Application::getSecurityToken() + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', 'Authentication'); + $MainTemplate->set('HTML', $AuthTemplate); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> + diff --git a/admin/database.php b/admin/database.php new file mode 100644 index 0000000..47ffd29 --- /dev/null +++ b/admin/database.php @@ -0,0 +1,49 @@ + Application::getSecurityToken()], 'command')) { + try { + $Statement = $Database->query(HTTP::POST('command')); + $result = print_r($Statement->fetchAll(), TRUE); + } catch(PDOException $Exception) { + $messages[] = $Exception->getMessage(); + } +} + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + $DatabaseTemplate = Template\Factory::build('database'); + $DatabaseTemplate->set('FORM', [ + 'INFO' => $messages ?? [], + 'TOKEN' => Application::getSecurityToken(), + 'RESULT' => $result ?? NULL, + 'COMMAND' => HTTP::POST('command'), + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', 'SQL'); + $MainTemplate->set('HTML', $DatabaseTemplate); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000..c2ef0e0 --- /dev/null +++ b/admin/index.php @@ -0,0 +1,88 @@ +query(sprintf('SELECT id FROM %s ORDER BY time_insert DESC LIMIT 1', Page\Attribute::TABLE)); + $LastPostStatement = $Database->query(sprintf('SELECT id FROM %s ORDER BY time_insert DESC LIMIT 1', Post\Attribute::TABLE)); + $LastUserStatement = $Database->query(sprintf('SELECT id FROM %s ORDER BY time_insert DESC LIMIT 1', User\Attribute::TABLE)); + + $PageCountStatement = $Database->query(sprintf('SELECT COUNT(*) FROM %s', Page\Attribute::TABLE)); + $PostCountStatement = $Database->query(sprintf('SELECT COUNT(*) FROM %s', Post\Attribute::TABLE)); + $UserCountStatement = $Database->query(sprintf('SELECT COUNT(*) FROM %s', User\Attribute::TABLE)); +} + +#=============================================================================== +# CATCH: PDOException +#=============================================================================== +catch(PDOException $Exception) { + exit($Exception->getMessage()); +} + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + try { + $LastPage = Page\Factory::build($LastPageStatement->fetchColumn()); + $LastPageUser = User\Factory::build($LastPage->attr('user')); + + $PageItemTemplate = generatePageItemTemplate($LastPage, $LastPageUser); + } + + catch(Page\Exception $Exception){} + catch(User\Exception $Exception){} + + try { + $LastPost = Post\Factory::build($LastPostStatement->fetchColumn()); + $LastPostUser = User\Factory::build($LastPost->attr('user')); + + $PostItemTemplate = generatePostItemTemplate($LastPost, $LastPostUser); + } + + catch(Post\Exception $Exception){} + catch(User\Exception $Exception){} + + try { + $LastUser = User\Factory::build($LastUserStatement->fetchColumn()); + $UserItemTemplate = generateUserItemTemplate($LastUser); + } catch(User\Exception $Exception){} + + $HomeTemplate = Template\Factory::build('home'); + $HomeTemplate->set('LAST', [ + 'PAGE' => $PageItemTemplate ?? FALSE, + 'POST' => $PostItemTemplate ?? FALSE, + 'USER' => $UserItemTemplate ?? FALSE, + + ]); + + $HomeTemplate->set('COUNT', [ + 'PAGE' => $PageCountStatement->fetchColumn(), + 'POST' => $PostCountStatement->fetchColumn(), + 'USER' => $UserCountStatement->fetchColumn(), + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', 'Dashboard'); + $MainTemplate->set('HTML', $HomeTemplate); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/admin/page/delete.php b/admin/page/delete.php new file mode 100644 index 0000000..163bbdb --- /dev/null +++ b/admin/page/delete.php @@ -0,0 +1,72 @@ +getAttribute(); + + if(HTTP::issetPOST(['token' => Application::getSecurityToken()], 'delete')) { + try { + if($Attribute->databaseDELETE($Database)) { + HTTP::redirect(Application::getAdminURL('page/')); + } + } catch(PDOException $Exception) { + $messages[] = $Exception->getMessage(); + } + } + + #=============================================================================== + # TRY: Template\Exception + #=============================================================================== + try { + $FormTemplate = Template\Factory::build('page/form'); + $FormTemplate->set('HTML', $Page->getHTML()); + $FormTemplate->set('FORM', [ + 'TYPE' => 'DELETE', + 'INFO' => $messages ?? [], + 'DATA' => [ + 'ID' => $Attribute->get('id'), + 'BODY' => $Attribute->get('body'), + 'TIME_INSERT' => $Attribute->get('time_insert'), + 'TIME_UPDATE' => $Attribute->get('time_update'), + ], + 'TOKEN' => Application::getSecurityToken() + ]); + + $DeleteTemplate = Template\Factory::build('page/delete'); + $DeleteTemplate->set('HTML', $FormTemplate); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_page_delete')); + $MainTemplate->set('HTML', $DeleteTemplate); + echo $MainTemplate; + } + + #=============================================================================== + # CATCH: Template\Exception + #=============================================================================== + catch(Template\Exception $Exception) { + $Exception->defaultHandler(); + } +} + +#=============================================================================== +# CATCH: Page\Exception +#=============================================================================== +catch(Page\Exception $Exception) { + Application::exit(404); +} +?> + diff --git a/admin/page/index.php b/admin/page/index.php new file mode 100644 index 0000000..56233a9 --- /dev/null +++ b/admin/page/index.php @@ -0,0 +1,76 @@ +query(sprintf('SELECT COUNT(id) FROM %s', Page\Attribute::TABLE))->fetchColumn() / $site_size); + +$currentSite = HTTP::GET('site') ?? 1; +$currentSite = abs(intval($currentSite)); + +if($currentSite < 1 OR ($currentSite > $lastSite AND $lastSite > 0)) { + Application::exit(404); +} + +#=============================================================================== +# Fetch page IDs from database +#=============================================================================== +$execSQL = "SELECT id FROM %s ORDER BY {$site_sort} LIMIT ".(($currentSite-1) * $site_size).", {$site_size}"; +$pageIDs = $Database->query(sprintf($execSQL, Page\Attribute::TABLE))->fetchAll($Database::FETCH_COLUMN); + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + foreach($pageIDs as $pageID) { + try { + $Page = Page\Factory::build($pageID); + $User = User\Factory::build($Page->attr('user')); + + $ItemTemplate = generatePageItemTemplate($Page, $User); + + $pages[] = $ItemTemplate; + } + catch(Page\Exception $Exception){} + catch(User\Exception $Exception){} + } + + $PaginationTemplate = Template\Factory::build('pagination'); + $PaginationTemplate->set('THIS', $currentSite); + $PaginationTemplate->set('LAST', $lastSite); + $PaginationTemplate->set('HREF', Application::getAdminURL('page/?site=%d')); + + $ListTemplate = Template\Factory::build('page/index'); + $ListTemplate->set('LIST', [ + 'PAGES' => $pages ?? [] + ]); + + $ListTemplate->set('PAGINATION', [ + 'THIS' => $currentSite, + 'LAST' => $lastSite, + 'HTML' => $PaginationTemplate + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_page_overview', $currentSite)); + $MainTemplate->set('HTML', $ListTemplate); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/admin/page/insert.php b/admin/page/insert.php new file mode 100644 index 0000000..0f421f6 --- /dev/null +++ b/admin/page/insert.php @@ -0,0 +1,86 @@ +set('id', HTTP::POST('id') ? HTTP::POST('id') : FALSE); + $Attribute->set('user', HTTP::POST('user')); + $Attribute->set('slug', HTTP::POST('slug') ? HTTP::POST('slug') : makeSlugURL(HTTP::POST('name'))); + $Attribute->set('name', HTTP::POST('name') ? HTTP::POST('name') : NULL); + $Attribute->set('body', HTTP::POST('body') ? HTTP::POST('body') : FALSE); + $Attribute->set('time_insert', HTTP::POST('time_insert') ? HTTP::POST('time_insert') : date('Y-m-d H:i:s')); + $Attribute->set('time_update', HTTP::POST('time_update') ? HTTP::POST('time_update') : date('Y-m-d H:i:s')); + + if(HTTP::issetPOST(['token' => Application::getSecurityToken()])) { + try { + if($Attribute->databaseINSERT($Database)) { + HTTP::redirect(Application::getAdminURL('page/')); + } + } catch(PDOException $Exception) { + $messages[] = $Exception->getMessage(); + } + } + + else { + $messages[] = $Language->text('error_security_csrf'); + } +} + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + $userIDs = $Database->query(sprintf('SELECT id FROM %s ORDER BY fullname ASC', User\Attribute::TABLE)); + + foreach($userIDs->fetchAll(PDO::FETCH_COLUMN) as $userID) { + $User = User\Factory::build($userID); + $userAttributes[] = [ + 'ID' => $User->attr('id'), + 'FULLNAME' => $User->attr('fullname'), + 'USERNAME' => $User->attr('username'), + ]; + } + + $FormTemplate = Template\Factory::build('page/form'); + $FormTemplate->set('FORM', [ + 'TYPE' => 'INSERT', + 'INFO' => $messages ?? [], + 'DATA' => [ + 'ID' => $Attribute->get('id'), + 'USER' => $Attribute->get('user'), + 'SLUG' => $Attribute->get('slug'), + 'NAME' => $Attribute->get('name'), + 'BODY' => $Attribute->get('body'), + 'TIME_INSERT' => $Attribute->get('time_insert'), + 'TIME_UPDATE' => $Attribute->get('time_update'), + ], + 'USER_LIST' => $userAttributes ?? [], + 'TOKEN' => Application::getSecurityToken() + ]); + + $InsertTemplate = Template\Factory::build('page/insert'); + $InsertTemplate->set('HTML', $FormTemplate); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_page_insert')); + $MainTemplate->set('HTML', $InsertTemplate); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/admin/page/update.php b/admin/page/update.php new file mode 100644 index 0000000..0489406 --- /dev/null +++ b/admin/page/update.php @@ -0,0 +1,96 @@ +getAttribute(); + + if(HTTP::issetPOST('user', 'slug', 'name', 'body', 'time_insert', 'time_update', 'update')) { + $Attribute->set('user', HTTP::POST('user')); + $Attribute->set('slug', HTTP::POST('slug') ? HTTP::POST('slug') : makeSlugURL(HTTP::POST('name'))); + $Attribute->set('name', HTTP::POST('name') ? HTTP::POST('name') : NULL); + $Attribute->set('body', HTTP::POST('body') ? HTTP::POST('body') : FALSE); + $Attribute->set('time_insert', HTTP::POST('time_insert') ? HTTP::POST('time_insert') : date('Y-m-d H:i:s')); + $Attribute->set('time_update', HTTP::POST('time_update') ? HTTP::POST('time_update') : date('Y-m-d H:i:s')); + + if(HTTP::issetPOST(['token' => Application::getSecurityToken()])) { + try { + $Attribute->databaseUPDATE($Database); + } catch(PDOException $Exception) { + $messages[] = $Exception->getMessage(); + } + } + + else { + $messages[] = $Language->text('error_security_csrf'); + } + } + + #=============================================================================== + # TRY: Template\Exception + #=============================================================================== + try { + $userIDs = $Database->query(sprintf('SELECT id FROM %s ORDER BY fullname ASC', User\Attribute::TABLE)); + + foreach($userIDs->fetchAll(PDO::FETCH_COLUMN) as $userID) { + $User = User\Factory::build($userID); + $userAttributes[] = [ + 'ID' => $User->attr('id'), + 'FULLNAME' => $User->attr('fullname'), + 'USERNAME' => $User->attr('username'), + ]; + } + + $FormTemplate = Template\Factory::build('page/form'); + $FormTemplate->set('FORM', [ + 'TYPE' => 'UPDATE', + 'INFO' => $messages ?? [], + 'DATA' => [ + 'ID' => $Attribute->get('id'), + 'USER' => $Attribute->get('user'), + 'SLUG' => $Attribute->get('slug'), + 'NAME' => $Attribute->get('name'), + 'BODY' => $Attribute->get('body'), + 'TIME_INSERT' => $Attribute->get('time_insert'), + 'TIME_UPDATE' => $Attribute->get('time_update'), + ], + 'USER_LIST' => $userAttributes ?? [], + 'TOKEN' => Application::getSecurityToken() + ]); + + $PageUpdateTemplate = Template\Factory::build('page/update'); + $PageUpdateTemplate->set('HTML', $FormTemplate); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_page_update')); + $MainTemplate->set('HTML', $PageUpdateTemplate); + echo $MainTemplate; + } + + #=============================================================================== + # CATCH: Template\Exception + #=============================================================================== + catch(Template\Exception $Exception) { + $Exception->defaultHandler(); + } +} + +#=============================================================================== +# CATCH: Page\Exception +#=============================================================================== +catch(Page\Exception $Exception) { + Application::exit(404); +} +?> \ No newline at end of file diff --git a/admin/post/delete.php b/admin/post/delete.php new file mode 100644 index 0000000..82e71da --- /dev/null +++ b/admin/post/delete.php @@ -0,0 +1,72 @@ +getAttribute(); + + if(HTTP::issetPOST(['token' => Application::getSecurityToken()], 'delete')) { + try { + if($Attribute->databaseDELETE($Database)) { + HTTP::redirect(Application::getAdminURL('post/')); + } + } catch(PDOException $Exception) { + $messages[] = $Exception->getMessage(); + } + } + + #=============================================================================== + # TRY: Template\Exception + #=============================================================================== + try { + $FormTemplate = Template\Factory::build('post/form'); + $FormTemplate->set('HTML', $Post->getHTML()); + $FormTemplate->set('FORM', [ + 'TYPE' => 'DELETE', + 'INFO' => $messages ?? [], + 'DATA' => [ + 'ID' => $Attribute->get('id'), + 'BODY' => $Attribute->get('body'), + 'TIME_INSERT' => $Attribute->get('time_insert'), + 'TIME_UPDATE' => $Attribute->get('time_update'), + ], + 'TOKEN' => Application::getSecurityToken() + ]); + + $DeleteTemplate = Template\Factory::build('post/delete'); + $DeleteTemplate->set('HTML', $FormTemplate); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_post_delete')); + $MainTemplate->set('HTML', $DeleteTemplate); + echo $MainTemplate; + } + + #=============================================================================== + # CATCH: Template\Exception + #=============================================================================== + catch(Template\Exception $Exception) { + $Exception->defaultHandler(); + } +} + +#=============================================================================== +# CATCH: Post\Exception +#=============================================================================== +catch(Post\Exception $Exception) { + Application::exit(404); +} +?> + diff --git a/admin/post/index.php b/admin/post/index.php new file mode 100644 index 0000000..bf7afdf --- /dev/null +++ b/admin/post/index.php @@ -0,0 +1,76 @@ +query(sprintf('SELECT COUNT(id) FROM %s', Post\Attribute::TABLE))->fetchColumn() / $site_size); + +$currentSite = HTTP::GET('site') ?? 1; +$currentSite = abs(intval($currentSite)); + +if($currentSite < 1 OR ($currentSite > $lastSite AND $lastSite > 0)) { + Application::exit(404); +} + +#=============================================================================== +# Fetch post IDs from database +#=============================================================================== +$execSQL = "SELECT id FROM %s ORDER BY {$site_sort} LIMIT ".(($currentSite-1) * $site_size).", {$site_size}"; +$postIDs = $Database->query(sprintf($execSQL, Post\Attribute::TABLE))->fetchAll($Database::FETCH_COLUMN); + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + foreach($postIDs as $postID) { + try { + $Post = Post\Factory::build($postID); + $User = User\Factory::build($Post->attr('user')); + + $ItemTemplate = generatePostItemTemplate($Post, $User); + + $posts[] = $ItemTemplate; + } + catch(Post\Exception $Exception){} + catch(User\Exception $Exception){} + } + + $PaginationTemplate = Template\Factory::build('pagination'); + $PaginationTemplate->set('THIS', $currentSite); + $PaginationTemplate->set('LAST', $lastSite); + $PaginationTemplate->set('HREF', Application::getAdminURL('post/?site=%d')); + + $ListTemplate = Template\Factory::build('post/index'); + $ListTemplate->set('LIST', [ + 'POSTS' => $posts ?? [] + ]); + + $ListTemplate->set('PAGINATION', [ + 'THIS' => $currentSite, + 'LAST' => $lastSite, + 'HTML' => $PaginationTemplate + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_post_overview', $currentSite)); + $MainTemplate->set('HTML', $ListTemplate); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/admin/post/insert.php b/admin/post/insert.php new file mode 100644 index 0000000..ab6bb87 --- /dev/null +++ b/admin/post/insert.php @@ -0,0 +1,86 @@ +set('id', HTTP::POST('id') ? HTTP::POST('id') : FALSE); + $Attribute->set('user', HTTP::POST('user')); + $Attribute->set('slug', HTTP::POST('slug') ? HTTP::POST('slug') : makeSlugURL(HTTP::POST('name'))); + $Attribute->set('name', HTTP::POST('name') ? HTTP::POST('name') : NULL); + $Attribute->set('body', HTTP::POST('body') ? HTTP::POST('body') : NULL); + $Attribute->set('time_insert', HTTP::POST('time_insert') ? HTTP::POST('time_insert') : date('Y-m-d H:i:s')); + $Attribute->set('time_update', HTTP::POST('time_update') ? HTTP::POST('time_update') : date('Y-m-d H:i:s')); + + if(HTTP::issetPOST(['token' => Application::getSecurityToken()])) { + try { + if($Attribute->databaseINSERT($Database)) { + HTTP::redirect(Application::getAdminURL('post/')); + } + } catch(PDOException $Exception) { + $messages[] = $Exception->getMessage(); + } + } + + else { + $messages[] = $Language->text('error_security_csrf'); + } +} + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + $userIDs = $Database->query(sprintf('SELECT id FROM %s ORDER BY fullname ASC', User\Attribute::TABLE)); + + foreach($userIDs->fetchAll(PDO::FETCH_COLUMN) as $userID) { + $User = User\Factory::build($userID); + $userAttributes[] = [ + 'ID' => $User->attr('id'), + 'FULLNAME' => $User->attr('fullname'), + 'USERNAME' => $User->attr('username'), + ]; + } + + $FormTemplate = Template\Factory::build('post/form'); + $FormTemplate->set('FORM', [ + 'TYPE' => 'INSERT', + 'INFO' => $messages ?? [], + 'DATA' => [ + 'ID' => $Attribute->get('id'), + 'USER' => $Attribute->get('user'), + 'SLUG' => $Attribute->get('slug'), + 'NAME' => $Attribute->get('name'), + 'BODY' => $Attribute->get('body'), + 'TIME_INSERT' => $Attribute->get('time_insert'), + 'TIME_UPDATE' => $Attribute->get('time_update'), + ], + 'USER_LIST' => $userAttributes ?? [], + 'TOKEN' => Application::getSecurityToken() + ]); + + $InsertTemplate = Template\Factory::build('post/insert'); + $InsertTemplate->set('HTML', $FormTemplate); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_post_insert')); + $MainTemplate->set('HTML', $InsertTemplate); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/admin/post/update.php b/admin/post/update.php new file mode 100644 index 0000000..875d947 --- /dev/null +++ b/admin/post/update.php @@ -0,0 +1,96 @@ +getAttribute(); + + if(HTTP::issetPOST('user', 'slug', 'name', 'body', 'time_insert', 'time_update', 'update')) { + $Attribute->set('user', HTTP::POST('user')); + $Attribute->set('slug', HTTP::POST('slug') ? HTTP::POST('slug') : makeSlugURL(HTTP::POST('name'))); + $Attribute->set('name', HTTP::POST('name') ? HTTP::POST('name') : NULL); + $Attribute->set('body', HTTP::POST('body') ? HTTP::POST('body') : FALSE); + $Attribute->set('time_insert', HTTP::POST('time_insert') ? HTTP::POST('time_insert') : date('Y-m-d H:i:s')); + $Attribute->set('time_update', HTTP::POST('time_update') ? HTTP::POST('time_update') : date('Y-m-d H:i:s')); + + if(HTTP::issetPOST(['token' => Application::getSecurityToken()])) { + try { + $Attribute->databaseUPDATE($Database); + } catch(PDOException $Exception) { + $messages[] = $Exception->getMessage(); + } + } + + else { + $messages[] = $Language->text('error_security_csrf'); + } + } + + #=============================================================================== + # TRY: Template\Exception + #=============================================================================== + try { + $userIDs = $Database->query(sprintf('SELECT id FROM %s ORDER BY fullname ASC', User\Attribute::TABLE)); + + foreach($userIDs->fetchAll(PDO::FETCH_COLUMN) as $userID) { + $User = User\Factory::build($userID); + $userAttributes[] = [ + 'ID' => $User->attr('id'), + 'FULLNAME' => $User->attr('fullname'), + 'USERNAME' => $User->attr('username'), + ]; + } + + $FormTemplate = Template\Factory::build('post/form'); + $FormTemplate->set('FORM', [ + 'TYPE' => 'UPDATE', + 'INFO' => $messages ?? [], + 'DATA' => [ + 'ID' => $Attribute->get('id'), + 'USER' => $Attribute->get('user'), + 'SLUG' => $Attribute->get('slug'), + 'NAME' => $Attribute->get('name'), + 'BODY' => $Attribute->get('body'), + 'TIME_INSERT' => $Attribute->get('time_insert'), + 'TIME_UPDATE' => $Attribute->get('time_update'), + ], + 'USER_LIST' => $userAttributes ?? [], + 'TOKEN' => Application::getSecurityToken() + ]); + + $PostUpdateTemplate = Template\Factory::build('post/update'); + $PostUpdateTemplate->set('HTML', $FormTemplate); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_post_update')); + $MainTemplate->set('HTML', $PostUpdateTemplate); + echo $MainTemplate; + } + + #=============================================================================== + # CATCH: Template\Exception + #=============================================================================== + catch(Template\Exception $Exception) { + $Exception->defaultHandler(); + } +} + +#=============================================================================== +# CATCH: Post\Exception +#=============================================================================== +catch(Post\Exception $Exception) { + Application::exit(404); +} +?> \ No newline at end of file diff --git a/admin/user/delete.php b/admin/user/delete.php new file mode 100644 index 0000000..ed8f925 --- /dev/null +++ b/admin/user/delete.php @@ -0,0 +1,72 @@ +getAttribute(); + + if(HTTP::issetPOST(['token' => Application::getSecurityToken()], 'delete')) { + try { + if($Attribute->databaseDELETE($Database)) { + HTTP::redirect(Application::getAdminURL('user/')); + } + } catch(PDOException $Exception) { + $messages[] = $Exception->getMessage(); + } + } + + #=============================================================================== + # TRY: Template\Exception + #=============================================================================== + try { + $FormTemplate = Template\Factory::build('user/form'); + $FormTemplate->set('HTML', $User->getHTML()); + $FormTemplate->set('FORM', [ + 'TYPE' => 'DELETE', + 'INFO' => $messages ?? [], + 'DATA' => [ + 'ID' => $Attribute->get('id'), + 'BODY' => $Attribute->get('body'), + 'TIME_INSERT' => $Attribute->get('time_insert'), + 'TIME_UPDATE' => $Attribute->get('time_update'), + ], + 'TOKEN' => Application::getSecurityToken() + ]); + + $DeleteTemplate = Template\Factory::build('user/delete'); + $DeleteTemplate->set('HTML', $FormTemplate); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_user_delete')); + $MainTemplate->set('HTML', $DeleteTemplate); + echo $MainTemplate; + } + + #=============================================================================== + # CATCH: Template\Exception + #=============================================================================== + catch(Template\Exception $Exception) { + $Exception->defaultHandler(); + } +} + +#=============================================================================== +# CATCH: User\Exception +#=============================================================================== +catch(User\Exception $Exception) { + Application::exit(404); +} +?> + diff --git a/admin/user/index.php b/admin/user/index.php new file mode 100644 index 0000000..3dca93a --- /dev/null +++ b/admin/user/index.php @@ -0,0 +1,72 @@ +query(sprintf('SELECT COUNT(id) FROM %s', User\Attribute::TABLE))->fetchColumn() / $site_size); + +$currentSite = HTTP::GET('site') ?? 1; +$currentSite = abs(intval($currentSite)); + +if($currentSite < 1 OR ($currentSite > $lastSite AND $lastSite > 0)) { + Application::exit(404); +} + +#=============================================================================== +# Fetch user IDs from database +#=============================================================================== +$execSQL = "SELECT id FROM %s ORDER BY {$site_sort} LIMIT ".(($currentSite-1) * $site_size).", {$site_size}"; +$userIDs = $Database->query(sprintf($execSQL, User\Attribute::TABLE))->fetchAll($Database::FETCH_COLUMN); + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + foreach($userIDs as $userID) { + try { + $User = User\Factory::build($userID); + $ItemTemplate = generateUserItemTemplate($User); + + $users[] = $ItemTemplate; + } catch(User\Exception $Exception){} + } + + $PaginationTemplate = Template\Factory::build('pagination'); + $PaginationTemplate->set('THIS', $currentSite); + $PaginationTemplate->set('LAST', $lastSite); + $PaginationTemplate->set('HREF', Application::getAdminURL('user/?site=%d')); + + $ListTemplate = Template\Factory::build('user/index'); + $ListTemplate->set('LIST', [ + 'USERS' => $users ?? [] + ]); + + $ListTemplate->set('PAGINATION', [ + 'THIS' => $currentSite, + 'LAST' => $lastSite, + 'HTML' => $PaginationTemplate + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_user_overview', $currentSite)); + $MainTemplate->set('HTML', $ListTemplate); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/admin/user/insert.php b/admin/user/insert.php new file mode 100644 index 0000000..7c892d4 --- /dev/null +++ b/admin/user/insert.php @@ -0,0 +1,78 @@ +set('id', HTTP::POST('id') ? HTTP::POST('id') : FALSE); + $Attribute->set('slug', HTTP::POST('slug') ? HTTP::POST('slug') : makeSlugURL(HTTP::POST('username'))); + $Attribute->set('username', HTTP::POST('username') ? HTTP::POST('username') : NULL); + $Attribute->set('password', HTTP::POST('password') ? password_hash(HTTP::POST('password'), PASSWORD_BCRYPT, ['cost' => 10]) : FALSE); + $Attribute->set('fullname', HTTP::POST('fullname') ? HTTP::POST('fullname') : NULL); + $Attribute->set('mailaddr', HTTP::POST('mailaddr') ? HTTP::POST('mailaddr') : NULL); + $Attribute->set('body', HTTP::POST('body') ? HTTP::POST('body') : NULL); + $Attribute->set('time_insert', HTTP::POST('time_insert') ? HTTP::POST('time_insert') : date('Y-m-d H:i:s')); + $Attribute->set('time_update', HTTP::POST('time_update') ? HTTP::POST('time_update') : date('Y-m-d H:i:s')); + + if(HTTP::issetPOST(['token' => Application::getSecurityToken()])) { + try { + if($Attribute->databaseINSERT($Database)) { + HTTP::redirect(Application::getAdminURL('user/')); + } + } catch(PDOException $Exception) { + $messages[] = $Exception->getMessage(); + } + } + + else { + $messages[] = $Language->text('error_security_csrf'); + } +} + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + $FormTemplate = Template\Factory::build('user/form'); + $FormTemplate->set('FORM', [ + 'TYPE' => 'INSERT', + 'INFO' => $messages ?? [], + 'DATA' => [ + 'ID' => $Attribute->get('id'), + 'SLUG' => $Attribute->get('slug'), + 'USERNAME' => $Attribute->get('username'), + 'PASSWORD' => NULL, + 'FULLNAME' => $Attribute->get('fullname'), + 'MAILADDR' => $Attribute->get('mailaddr'), + 'BODY' => $Attribute->get('body'), + 'TIME_INSERT' => $Attribute->get('time_insert'), + 'TIME_UPDATE' => $Attribute->get('time_update'), + ], + 'TOKEN' => Application::getSecurityToken() + ]); + + $InsertTemplate = Template\Factory::build('user/insert'); + $InsertTemplate->set('HTML', $FormTemplate); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_user_insert')); + $MainTemplate->set('HTML', $InsertTemplate); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/admin/user/update.php b/admin/user/update.php new file mode 100644 index 0000000..cab582e --- /dev/null +++ b/admin/user/update.php @@ -0,0 +1,89 @@ +getAttribute(); + + if(HTTP::issetPOST('slug', 'username', 'password', 'fullname', 'mailaddr', 'body', 'time_insert', 'time_update', 'update')) { + $Attribute->set('slug', HTTP::POST('slug') ? HTTP::POST('slug') : makeSlugURL(HTTP::POST('username'))); + $Attribute->set('username', HTTP::POST('username') ? HTTP::POST('username') : NULL); + $Attribute->set('password', HTTP::POST('password') ? password_hash(HTTP::POST('password'), PASSWORD_BCRYPT, ['cost' => 10]) : FALSE); + $Attribute->set('fullname', HTTP::POST('fullname') ? HTTP::POST('fullname') : NULL); + $Attribute->set('mailaddr', HTTP::POST('mailaddr') ? HTTP::POST('mailaddr') : NULL); + $Attribute->set('body', HTTP::POST('body') ? HTTP::POST('body') : NULL); + $Attribute->set('time_insert', HTTP::POST('time_insert') ? HTTP::POST('time_insert') : date('Y-m-d H:i:s')); + $Attribute->set('time_update', HTTP::POST('time_update') ? HTTP::POST('time_update') : date('Y-m-d H:i:s')); + + if(HTTP::issetPOST(['token' => Application::getSecurityToken()])) { + try { + if($Attribute->databaseUPDATE($Database)) { + } + } catch(PDOException $Exception) { + $messages[] = $Exception->getMessage(); + } + } + + else { + $messages[] = $Language->text('error_security_csrf'); + } + } + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== + try { + $FormTemplate = Template\Factory::build('user/form'); + $FormTemplate->set('FORM', [ + 'TYPE' => 'UPDATE', + 'INFO' => $messages ?? [], + 'DATA' => [ + 'ID' => $Attribute->get('id'), + 'SLUG' => $Attribute->get('slug'), + 'USERNAME' => $Attribute->get('username'), + 'PASSWORD' => NULL, + 'FULLNAME' => $Attribute->get('fullname'), + 'MAILADDR' => $Attribute->get('mailaddr'), + 'BODY' => $Attribute->get('body'), + 'TIME_INSERT' => $Attribute->get('time_insert'), + 'TIME_UPDATE' => $Attribute->get('time_update'), + ], + 'TOKEN' => Application::getSecurityToken() + ]); + + $InsertTemplate = Template\Factory::build('user/update'); + $InsertTemplate->set('HTML', $FormTemplate); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('NAME', $Language->text('title_user_update')); + $MainTemplate->set('HTML', $InsertTemplate); + echo $MainTemplate; + } + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== + catch(Template\Exception $Exception) { + $Exception->defaultHandler(); + } +} + +#=============================================================================== +# CATCH: User\Exception +#=============================================================================== +catch(User\Exception $Exception) { + Application::exit(404); +} +?> \ No newline at end of file diff --git a/core/application.php b/core/application.php new file mode 100644 index 0000000..58dd728 --- /dev/null +++ b/core/application.php @@ -0,0 +1,136 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# This file is included by each file from the system or admin directory. # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +#=============================================================================== +# Document root +#=============================================================================== +define('ROOT', dirname(__DIR__).'/'); + +#=============================================================================== +# Autoload register for classes +#=============================================================================== +spl_autoload_register(function($classname) { + $classname = str_replace('\\', '/', $classname); + require_once "namespace/{$classname}.php"; +}); + +#=============================================================================== +# Exception handler for non-caught exceptions +#=============================================================================== +set_exception_handler(function($Exception) { + http_response_code(503); + exit($Exception->getMessage()); +}); + +#=============================================================================== +# Initialize HTTP class and remove all arrays from $_GET and $_POST +#=============================================================================== +HTTP::init($_GET, $_POST, $_FILES, TRUE); + +#=============================================================================== +# Include configuration +#=============================================================================== +require_once 'configuration.php'; + +#=============================================================================== +# Overwrite configuration if admin +#=============================================================================== +if(defined('ADMINISTRATION') AND ADMINISTRATION === TRUE) { + + #=========================================================================== + # Enable sessions + #=========================================================================== + session_start(); + + #=========================================================================== + # Authentication check + #=========================================================================== + if(defined('AUTHENTICATION') AND !Application::isAuthenticated()) { + HTTP::redirect(Application::getAdminURL('auth.php')); + } + + #=========================================================================== + # Overwrite configuration + #=========================================================================== + Application::set('CORE.LANGUAGE', Application::get('ADMIN.LANGUAGE')); + Application::set('TEMPLATE.NAME', Application::get('ADMIN.TEMPLATE')); + Application::set('TEMPLATE.LANG', Application::get('ADMIN.LANGUAGE')); +} + +#=============================================================================== +# Include functions +#=============================================================================== +require_once 'functions.php'; + +#=============================================================================== +# TRY: PDOException +#=============================================================================== +try { + $Language = Application::getLanguage(); + $Database = Application::getDatabase(); + + $Database->setAttribute($Database::ATTR_DEFAULT_FETCH_MODE, $Database::FETCH_OBJ); + $Database->setAttribute($Database::ATTR_ERRMODE, $Database::ERRMODE_EXCEPTION); +} + +#=============================================================================== +# CATCH: PDOException +#=============================================================================== +catch(PDOException $Exception) { + http_response_code(503); + exit("PDO database connection error: {$Exception->getMessage()}"); +} + +#=============================================================================== +# Check if "304 Not Modified" and ETag header should be send +#=============================================================================== +if(Application::get('CORE.SEND_304') === TRUE AND !defined('ADMINISTRATION')) { + #=========================================================================== + # Select edit time from last edited items (page, post, user) + #=========================================================================== + $execute = 'SELECT time_update FROM %s ORDER BY time_update DESC LIMIT 1'; + + $PageStatement = $Database->query(sprintf($execute, Page\Attribute::TABLE)); + $PostStatement = $Database->query(sprintf($execute, Post\Attribute::TABLE)); + $UserStatement = $Database->query(sprintf($execute, User\Attribute::TABLE)); + + #=========================================================================== + # Define HTTP ETag header identifier + #=========================================================================== + $HTTP_ETAG_IDENTIFIER = sha1(implode(NULL, [ + serialize(Application::getConfiguration()), + $PageStatement->fetchColumn(), + $PostStatement->fetchColumn(), + $UserStatement->fetchColumn(), + 'CUSTOM-STRING-0123456789' + ])); + + #=========================================================================== + # Send ETag header within the HTTP response + #=========================================================================== + HTTP::responseHeader(HTTP::HEADER_ETAG, "\"{$HTTP_ETAG_IDENTIFIER}\""); + + #=========================================================================== + # Validate ETag header from the HTTP request + #=========================================================================== + if(isset($_SERVER['HTTP_IF_NONE_MATCH'])) { + $HTTP_IF_NONE_MATCH = $_SERVER['HTTP_IF_NONE_MATCH']; + $HTTP_IF_NONE_MATCH = trim($HTTP_IF_NONE_MATCH, '"'); + + # If the server adds the extensions to the response header + $HTTP_IF_NONE_MATCH = rtrim($HTTP_IF_NONE_MATCH, '-br'); + $HTTP_IF_NONE_MATCH = rtrim($HTTP_IF_NONE_MATCH, '-gzip'); + + if($HTTP_IF_NONE_MATCH === $HTTP_ETAG_IDENTIFIER) { + http_response_code(304); + exit(); + } + } +} +?> \ No newline at end of file diff --git a/core/configuration-example.php b/core/configuration-example.php new file mode 100644 index 0000000..80192cf --- /dev/null +++ b/core/configuration-example.php @@ -0,0 +1,103 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +#=============================================================================== +# Core configuration +#=============================================================================== +Application::set('CORE.LANGUAGE', 'en'); +Application::set('CORE.SEND_304', FALSE); + +#=============================================================================== +# Blog configuration +#=============================================================================== +Application::set('BLOGMETA.NAME', 'My Techblog'); +Application::set('BLOGMETA.DESC', '[a creative description]'); +Application::set('BLOGMETA.HOME', 'Home'); +Application::set('BLOGMETA.MAIL', 'mail@example.org'); +Application::set('BLOGMETA.LANG', 'en'); + +#=============================================================================== +# Settings for database connection +#=============================================================================== +Application::set('DATABASE.HOSTNAME', 'localhost'); +Application::set('DATABASE.BASENAME', 'blog'); +Application::set('DATABASE.USERNAME', ''); +Application::set('DATABASE.PASSWORD', ''); + +#=============================================================================== +# Backend configuration +#=============================================================================== +Application::set('ADMIN.TEMPLATE', 'admin'); +Application::set('ADMIN.LANGUAGE', Application::get('CORE.LANGUAGE')); + +#=============================================================================== +# Settings for template configuration +#=============================================================================== +Application::set('TEMPLATE.NAME', 'standard'); +Application::set('TEMPLATE.LANG', Application::get('CORE.LANGUAGE')); + +#=============================================================================== +# Protocol, hostname and path for this installation +#=============================================================================== +Application::set('PATHINFO.PROT', isset($_SERVER['HTTPS']) ? 'https' : 'http'); +Application::set('PATHINFO.HOST', $_SERVER['HTTP_HOST']); +Application::set('PATHINFO.BASE', ''); + +#=============================================================================== +# Enable or disable the use of slug URLs for item permalinks +#=============================================================================== +Application::set('PAGE.SLUG_URLS', TRUE); +Application::set('POST.SLUG_URLS', TRUE); +Application::set('USER.SLUG_URLS', TRUE); + +#=============================================================================== +# Number of items to display on feed and overview sites +#=============================================================================== +Application::set('PAGE.LIST_SIZE', 10); +Application::set('POST.LIST_SIZE', 10); +Application::set('USER.LIST_SIZE', 10); +Application::set('PAGE.FEED_SIZE', 25); +Application::set('POST.FEED_SIZE', 25); + +#=============================================================================== +# Settings for item URL generation (you have to change the .htaccess as well!) +#=============================================================================== +Application::set('PAGE.DIRECTORY', 'page'); +Application::set('POST.DIRECTORY', 'post'); +Application::set('USER.DIRECTORY', 'user'); + +#=============================================================================== +# Enable or disable the use of emoticons for item content +#=============================================================================== +Application::set('PAGE.EMOTICONS', TRUE); +Application::set('POST.EMOTICONS', TRUE); +Application::set('USER.EMOTICONS', TRUE); + +#=============================================================================== +# Number of characters to display in the items description +#=============================================================================== +Application::set('PAGE.DESCRIPTION_SIZE', 200); +Application::set('POST.DESCRIPTION_SIZE', 200); +Application::set('USER.DESCRIPTION_SIZE', 200); + +#=============================================================================== +# "ORDER BY" clause for item sorting on feed and overview sites +#=============================================================================== +Application::set('PAGE.LIST_SORT', 'time_insert DESC'); +Application::set('POST.LIST_SORT', 'time_insert DESC'); +Application::set('USER.LIST_SORT', 'time_insert DESC'); +Application::set('PAGE.FEED_SORT', 'time_insert DESC'); +Application::set('POST.FEED_SORT', 'time_insert DESC'); + +#=============================================================================== +# Item attributes used to generate the hash for feed items +#=============================================================================== +Application::set('PAGE.FEED_GUID', ['id', 'time_insert']); +Application::set('POST.FEED_GUID', ['id', 'time_insert']); +?> \ No newline at end of file diff --git a/core/functions.php b/core/functions.php new file mode 100644 index 0000000..726e96f --- /dev/null +++ b/core/functions.php @@ -0,0 +1,322 @@ +query(sprintf('SELECT COUNT(id) FROM %s', Page\Attribute::TABLE)); + + $lastSite = ceil($Statement->fetchColumn() / Application::get('PAGE.LIST_SIZE')); + + $PaginationTemplate = Template\Factory::build('pagination'); + $PaginationTemplate->set('THIS', $currentSite); + $PaginationTemplate->set('LAST', $lastSite); + $PaginationTemplate->set('HREF', Application::getPageURL('?site=%d')); + + return $PaginationTemplate; +} + +#=============================================================================== +# Helper function to reduce duplicate code +#=============================================================================== +function generatePostNaviTemplate($currentSite): Template\Template { + $Database = Application::getDatabase(); + $Statement = $Database->query(sprintf('SELECT COUNT(id) FROM %s', Post\Attribute::TABLE)); + + $lastSite = ceil($Statement->fetchColumn() / Application::get('POST.LIST_SIZE')); + + $PaginationTemplate = Template\Factory::build('pagination'); + $PaginationTemplate->set('THIS', $currentSite); + $PaginationTemplate->set('LAST', $lastSite); + $PaginationTemplate->set('HREF', Application::getPostURL('?site=%d')); + + return $PaginationTemplate; +} + +#=============================================================================== +# Helper function to reduce duplicate code +#=============================================================================== +function generateUserNaviTemplate($currentSite): Template\Template { + $Database = Application::getDatabase(); + $Statement = $Database->query(sprintf('SELECT COUNT(id) FROM %s', User\Attribute::TABLE)); + + $lastSite = ceil($Statement->fetchColumn() / Application::get('USER.LIST_SIZE')); + + $PaginationTemplate = Template\Factory::build('pagination'); + $PaginationTemplate->set('THIS', $currentSite); + $PaginationTemplate->set('LAST', $lastSite); + $PaginationTemplate->set('HREF', Application::getUserURL('?site=%d')); + + return $PaginationTemplate; +} + +#=============================================================================== +# Helper function to reduce duplicate code +#=============================================================================== +function generatePageItemTemplate(Page\Item $Page, User\Item $User): Template\Template { + $Template = Template\Factory::build('page/item'); + $Template->set('PAGE', generatePageItemData($Page)); + $Template->set('USER', generateUserItemData($User)); + + return $Template; +} + +#=============================================================================== +# Helper function to reduce duplicate code +#=============================================================================== +function generatePostItemTemplate(Post\Item $Post, User\Item $User): Template\Template { + $Template = Template\Factory::build('post/item'); + $Template->set('POST', generatePostItemData($Post)); + $Template->set('USER', generateUserItemData($User)); + + return $Template; +} + +#=============================================================================== +# Helper function to reduce duplicate code +#=============================================================================== +function generateUserItemTemplate(User\Item $User): Template\Template { + $Template = Template\Factory::build('user/item'); + $Template->set('USER', generateUserItemData($User)); + + return $Template; +} + +#=============================================================================== +# Helper function to reduce duplicate code +#=============================================================================== +function generateItemData(Item $Item): array { + return [ + 'ID' => $Item->getID(), + 'URL' => $Item->getURL(), + 'GUID' => $Item->getGUID(), + + 'PREV' => FALSE, + 'NEXT' => FALSE, + + 'FILE' => [ + 'LIST' => $Item->getFiles() + ], + + 'BODY' => [ + 'TEXT' => $Item->getBody(), + 'HTML' => $Item->getHTML() + ], + + 'ATTR' => [ + 'USER' => $Item->attr('user'), + 'SLUG' => $Item->attr('slug'), + 'NAME' => $Item->attr('name'), + 'BODY' => $Item->attr('body'), + 'TIME_INSERT' => $Item->attr('time_insert'), + 'TIME_UPDATE' => $Item->attr('time_update') + ] + ]; +} + +#=============================================================================== +# Helper function to reduce duplicate code +#=============================================================================== +function generatePageItemData(Page\Item $Page): array { + return generateItemData($Page); +} + +#=============================================================================== +# Helper function to reduce duplicate code +#=============================================================================== +function generatePostItemData(Post\Item $Post): array { + return generateItemData($Post); +} + +#=============================================================================== +# Helper function to reduce duplicate code +#=============================================================================== +function generateUserItemData(User\Item $User): array { + return [ + 'ID' => $User->getID(), + 'URL' => $User->getURL(), + 'GUID' => $User->getGUID(), + + 'PREV' => FALSE, + 'NEXT' => FALSE, + + 'FILE' => [ + 'LIST' => $User->getFiles() + ], + + 'BODY' => [ + 'TEXT' => $User->getBody(), + 'HTML' => $User->getHTML() + ], + + 'ATTR' => [ + 'SLUG' => $User->attr('slug'), + 'BODY' => $User->attr('body'), + 'USERNAME' => $User->attr('username'), + 'FULLNAME' => $User->attr('fullname'), + 'MAILADDR' => $User->attr('mailaddr'), + 'TIME_INSERT' => $User->attr('time_insert'), + 'TIME_UPDATE' => $User->attr('time_update') + ] + ]; +} + +#=============================================================================== +# Parser for datetime formatted strings [YYYY-MM-DD HH:II:SS] +#=============================================================================== +function parseDatetime($datetime, $format): string { + $Language = Application::getLanguage(); + + list($date, $time) = explode(' ', $datetime); + + list($DATE['Y'], $DATE['M'], $DATE['D']) = explode('-', $date); + list($TIME['H'], $TIME['M'], $TIME['S']) = explode(':', $time); + + $M_LIST = [ + '01' => $Language->text('month_01'), + '02' => $Language->text('month_02'), + '03' => $Language->text('month_03'), + '04' => $Language->text('month_04'), + '05' => $Language->text('month_05'), + '06' => $Language->text('month_06'), + '07' => $Language->text('month_07'), + '08' => $Language->text('month_08'), + '09' => $Language->text('month_09'), + '10' => $Language->text('month_10'), + '11' => $Language->text('month_11'), + '12' => $Language->text('month_12'), + ]; + + $D_LIST = [ + 0 => $Language->text('day_6'), + 1 => $Language->text('day_0'), + 2 => $Language->text('day_1'), + 3 => $Language->text('day_2'), + 4 => $Language->text('day_3'), + 5 => $Language->text('day_4'), + 6 => $Language->text('day_5'), + ]; + + return strtr($format, [ + '[Y]' => $DATE['Y'], + '[M]' => $DATE['M'], + '[D]' => $DATE['D'], + '[H]' => $TIME['H'], + '[I]' => $TIME['M'], + '[S]' => $TIME['S'], + '[W]' => $D_LIST[date('w', strtotime($datetime))], + '[F]' => $M_LIST[date('m', strtotime($datetime))], + '[DATE]' => $date, + '[TIME]' => $time, + '[RFC2822]' => date('r', strtotime($datetime)) + ]); +} + +#=============================================================================== +# Get emoticons with unicode characters and description +#=============================================================================== +function getEmoticons(): array { + $Language = Application::getLanguage(); + + return [ + ':)' => ['😊', $Language->text('emoticon_1F60A')], + ':(' => ['😞', $Language->text('emoticon_1F61E')], + ':D' => ['😃', $Language->text('emoticon_1F603')], + ':P' => ['😛', $Language->text('emoticon_1F61B')], + ':O' => ['😲', $Language->text('emoticon_1F632')], + ';)' => ['😉', $Language->text('emoticon_1F609')], + ';(' => ['😢', $Language->text('emoticon_1F622')], + ':|' => ['😐', $Language->text('emoticon_1F610')], + ':X' => ['😵', $Language->text('emoticon_1F635')], + ':/' => ['😒', $Language->text('emoticon_1F612')], + '8)' => ['😎', $Language->text('emoticon_1F60E')], + ':S' => ['😟', $Language->text('emoticon_1F61F')], + 'xD' => ['😂', $Language->text('emoticon_1F602')], + '^^' => ['😄', $Language->text('emoticon_1F604')], + ]; +} + +#=============================================================================== +# Parse emoticons to HTML encoded unicode characters +#=============================================================================== +function parseEmoticons($string): string { + foreach(getEmoticons() as $emoticon => $data) { + $pattern = '#(^|\s)'.preg_quote($emoticon).'#'; + $replace = " {$data[0]}"; + + $string = preg_replace($pattern, $replace, $string); + } + + return $string; +} + +#=============================================================================== +# Wrapper function for htmlspecialchars() +#=============================================================================== +function escapeHTML($string): string { + return htmlspecialchars($string); +} + +#=============================================================================== +# Wrapper function for strip_tags() +#=============================================================================== +function removeHTML($string): string { + return strip_tags($string); +} + +#=============================================================================== +# Remove all double line breaks from string +#=============================================================================== +function removeDoubleLineBreaks($string): string { + return preg_replace('#(\r?\n){2,}#', "\n\n", $string); +} + +#=============================================================================== +# Remove line breaks and tabs from a string +#=============================================================================== +function removeLineBreaksAndTabs($string, $replace = ''): string { + return str_replace(["\r\n", "\r", "\n", "\t"], $replace, $string); +} + +#=============================================================================== +# Return pseudo-random (hex converted) string +#=============================================================================== +function getRandomValue($length = 40): string { + return strtoupper(bin2hex(random_bytes(ceil($length / 2)))); +} + +#=============================================================================== +# Return cutted string +#=============================================================================== +function cut($string, $length, $replace = ' […]') { + if(mb_strlen($string) > $length) { + return preg_replace("/^(.{1,{$length}}\\b).*/su", "\\1{$replace}", $string); + } + + return $string; +} + +#=============================================================================== +# Return excerpt content +#=============================================================================== +function excerpt($string, $length = 500, $replace = ' […]') { + $string = removeHTML($string); + $string = removeDoubleLineBreaks($string); + $string = cut($string, $length, $replace); + $string = nl2br($string); + + return $string; +} + +#==================================================================================================== +# Generate a valid slug URL part from a string +#==================================================================================================== +function makeSlugURL($string) { + $string = strtolower($string); + $string = str_replace(['ä', 'ö', 'ü', 'ß'], ['ae', 'oe', 'ue', 'ss'], $string); + $string = preg_replace('/[^a-zA-Z0-9\-]/', '-', $string); + $string = preg_replace('/-+/', '-', $string); + + return trim($string, '-'); +} +?> diff --git a/core/language/de.php b/core/language/de.php new file mode 100644 index 0000000..dc6e924 --- /dev/null +++ b/core/language/de.php @@ -0,0 +1,194 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# This file contains core internationalization strings for the DE language. If # +# you are a translator, please only use the original EN language file for your # +# translation and open a pull request on GitHub or send your language file via # +# email back to . # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +#=============================================================================== +# Date element names +#=============================================================================== +$LANGUAGE['date_d'] = 'Tag'; +$LANGUAGE['date_m'] = 'Monat'; +$LANGUAGE['date_y'] = 'Jahr'; + +#=============================================================================== +# Time element names +#=============================================================================== +$LANGUAGE['time_h'] = 'Stunde'; +$LANGUAGE['time_m'] = 'Minute'; +$LANGUAGE['time_s'] = 'Sekunde'; + +#=============================================================================== +# Day names +#=============================================================================== +$LANGUAGE['day_0'] = 'Montag'; +$LANGUAGE['day_1'] = 'Dienstag'; +$LANGUAGE['day_2'] = 'Mittwoch'; +$LANGUAGE['day_3'] = 'Donnerstag'; +$LANGUAGE['day_4'] = 'Freitag'; +$LANGUAGE['day_5'] = 'Samstag'; +$LANGUAGE['day_6'] = 'Sonntag'; + +#=============================================================================== +# Month names +#=============================================================================== +$LANGUAGE['month_01'] = 'Januar'; +$LANGUAGE['month_02'] = 'Februar'; +$LANGUAGE['month_03'] = 'März'; +$LANGUAGE['month_04'] = 'April'; +$LANGUAGE['month_05'] = 'Mai'; +$LANGUAGE['month_06'] = 'Juni'; +$LANGUAGE['month_07'] = 'Juli'; +$LANGUAGE['month_08'] = 'August'; +$LANGUAGE['month_09'] = 'September'; +$LANGUAGE['month_10'] = 'Oktober'; +$LANGUAGE['month_11'] = 'November'; +$LANGUAGE['month_12'] = 'Dezember'; + +#=============================================================================== +# Emoticon explanations +#=============================================================================== +$LANGUAGE['emoticon_1F60A'] = 'Lächelndes Gesicht mit lächelnden Augen'; +$LANGUAGE['emoticon_1F61E'] = 'Enttäuschtes Gesicht'; +$LANGUAGE['emoticon_1F603'] = 'Lächelndes Gesicht mit offenem Mund'; +$LANGUAGE['emoticon_1F61B'] = 'Gesicht mit herausgestreckter Zunge'; +$LANGUAGE['emoticon_1F632'] = 'Erstauntes Gesicht'; +$LANGUAGE['emoticon_1F609'] = 'Zwinkerndes Gesicht'; +$LANGUAGE['emoticon_1F622'] = 'Weinendes Gesicht'; +$LANGUAGE['emoticon_1F610'] = 'Neutrales Gesicht'; +$LANGUAGE['emoticon_1F635'] = 'Schwindeliges Gesicht'; +$LANGUAGE['emoticon_1F612'] = 'Frustriertes Gesicht'; +$LANGUAGE['emoticon_1F60E'] = 'Lächelndes Gesicht mit Sonnenbrille'; +$LANGUAGE['emoticon_1F61F'] = 'Besorgtes Gesicht'; +$LANGUAGE['emoticon_1F602'] = 'Gesicht mit Freudentränen'; +$LANGUAGE['emoticon_1F604'] = 'Lächelndes Gesicht mit offenem Mund und lachenden Augen'; + +#=============================================================================== +# Error messages +#=============================================================================== +$LANGUAGE['error_security_csrf'] = 'Der Sicherheitstoken stimmt nicht mit dem Sicherheitstoken des Servers überein.'; +$LANGUAGE['error_database_exec'] = 'Es ist ein unerwarteter Fehler bei der Kommunikation mit der Datenbank aufgetreten.'; + +#=============================================================================== +# Fulltext search +#=============================================================================== +$LANGUAGE['search_no_results'] = 'Entschuldigung, es wurden keine Ergebnisse für "%s" gefunden.'; + +#=============================================================================== +# Authentication +#=============================================================================== +$LANGUAGE['authentication_failure'] = 'Der Benutzername oder das Passwort ist nicht korrekt.'; + +#=============================================================================== +# Items [singular] +#=============================================================================== +$LANGUAGE['page'] = 'Seite'; +$LANGUAGE['post'] = 'Beitrag'; +$LANGUAGE['user'] = 'Benutzer'; + +#=============================================================================== +# Items [plural] +#=============================================================================== +$LANGUAGE['pages'] = 'Seiten'; +$LANGUAGE['posts'] = 'Beiträge'; +$LANGUAGE['users'] = 'Benutzer'; + +#=============================================================================== +# Actions +#=============================================================================== +$LANGUAGE['select'] = 'Anzeigen'; +$LANGUAGE['insert'] = 'Erstellen'; +$LANGUAGE['update'] = 'Bearbeiten'; +$LANGUAGE['delete'] = 'Löschen'; +$LANGUAGE['search'] = 'Suchen'; +$LANGUAGE['remove'] = 'Entfernen'; + +#=============================================================================== +# Previous items +#=============================================================================== +$LANGUAGE['prev_page'] = 'Vorherige Seite'; +$LANGUAGE['prev_post'] = 'Vorheriger Beitrag'; +$LANGUAGE['prev_user'] = 'Vorheriger Benutzer'; + +#=============================================================================== +# Next items +#=============================================================================== +$LANGUAGE['next_page'] = 'Nächste Seite'; +$LANGUAGE['next_post'] = 'Nächster Beitrag'; +$LANGUAGE['next_user'] = 'Nächster Benutzer'; + +#=============================================================================== +# Item overview +#=============================================================================== +$LANGUAGE['page_overview'] = 'Seitenübersicht'; +$LANGUAGE['post_overview'] = 'Beitragübersicht'; +$LANGUAGE['user_overview'] = 'Benutzerübersicht'; + +#=============================================================================== +# Items select +#=============================================================================== +$LANGUAGE['select_page'] = 'Seite anzeigen'; +$LANGUAGE['select_post'] = 'Beitrag anzeigen'; +$LANGUAGE['select_user'] = 'Benutzer anzeigen'; + +#=============================================================================== +# Items insert +#=============================================================================== +$LANGUAGE['insert_page'] = 'Seite erstellen'; +$LANGUAGE['insert_post'] = 'Beitrag erstellen'; +$LANGUAGE['insert_user'] = 'Benutzer erstellen'; + +#=============================================================================== +# Items update +#=============================================================================== +$LANGUAGE['update_page'] = 'Seite bearbeiten'; +$LANGUAGE['update_post'] = 'Beitrag bearbeiten'; +$LANGUAGE['update_user'] = 'Benutzer bearbeiten'; + +#=============================================================================== +# Items delete +#=============================================================================== +$LANGUAGE['delete_page'] = 'Seite löschen'; +$LANGUAGE['delete_post'] = 'Beitrag löschen'; +$LANGUAGE['delete_user'] = 'Benutzer löschen'; + +#=============================================================================== +# Item insert titles +#=============================================================================== +$LANGUAGE['title_page_insert'] = $LANGUAGE['insert_page']; +$LANGUAGE['title_post_insert'] = $LANGUAGE['insert_post']; +$LANGUAGE['title_user_insert'] = $LANGUAGE['insert_user']; + +#=============================================================================== +# Item update titles +#=============================================================================== +$LANGUAGE['title_page_update'] = $LANGUAGE['update_page']; +$LANGUAGE['title_post_update'] = $LANGUAGE['update_post']; +$LANGUAGE['title_user_update'] = $LANGUAGE['update_user']; + +#=============================================================================== +# Item delete titles +#=============================================================================== +$LANGUAGE['title_page_delete'] = $LANGUAGE['delete_page']; +$LANGUAGE['title_post_delete'] = $LANGUAGE['delete_post']; +$LANGUAGE['title_user_delete'] = $LANGUAGE['delete_user']; + +#=============================================================================== +# Item overview titles +#=============================================================================== +$LANGUAGE['title_page_overview'] = "{$LANGUAGE['page_overview']} [%d]"; +$LANGUAGE['title_post_overview'] = "{$LANGUAGE['post_overview']} [%d]"; +$LANGUAGE['title_user_overview'] = "{$LANGUAGE['user_overview']} [%d]"; + +#=============================================================================== +# Search titles +#=============================================================================== +$LANGUAGE['title_search_request'] = 'Volltextsuche'; +$LANGUAGE['title_search_results'] = 'Ergebnisse für "%s"'; +?> \ No newline at end of file diff --git a/core/language/en.php b/core/language/en.php new file mode 100644 index 0000000..3e48191 --- /dev/null +++ b/core/language/en.php @@ -0,0 +1,194 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# This file contains core internationalization strings for the EN language. If # +# you are a translator, please only use the original EN language file for your # +# translation and open a pull request on GitHub or send your language file via # +# email back to . # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +#=============================================================================== +# Date element names +#=============================================================================== +$LANGUAGE['date_d'] = 'Day'; +$LANGUAGE['date_m'] = 'Month'; +$LANGUAGE['date_y'] = 'Year'; + +#=============================================================================== +# Time element names +#=============================================================================== +$LANGUAGE['time_h'] = 'Hour'; +$LANGUAGE['time_m'] = 'Minute'; +$LANGUAGE['time_s'] = 'Second'; + +#=============================================================================== +# Day names +#=============================================================================== +$LANGUAGE['day_0'] = 'Monday'; +$LANGUAGE['day_1'] = 'Tuesday'; +$LANGUAGE['day_2'] = 'Wednesday'; +$LANGUAGE['day_3'] = 'Thursday'; +$LANGUAGE['day_4'] = 'Friday'; +$LANGUAGE['day_5'] = 'Saturday'; +$LANGUAGE['day_6'] = 'Sunday'; + +#=============================================================================== +# Month names +#=============================================================================== +$LANGUAGE['month_01'] = 'January'; +$LANGUAGE['month_02'] = 'February'; +$LANGUAGE['month_03'] = 'March'; +$LANGUAGE['month_04'] = 'April'; +$LANGUAGE['month_05'] = 'May'; +$LANGUAGE['month_06'] = 'June'; +$LANGUAGE['month_07'] = 'July'; +$LANGUAGE['month_08'] = 'August'; +$LANGUAGE['month_09'] = 'September'; +$LANGUAGE['month_10'] = 'October'; +$LANGUAGE['month_11'] = 'November'; +$LANGUAGE['month_12'] = 'December'; + +#=============================================================================== +# Emoticon explanations +#=============================================================================== +$LANGUAGE['emoticon_1F60A'] = 'Smiling face with smiling eyes'; +$LANGUAGE['emoticon_1F61E'] = 'Disappointed face'; +$LANGUAGE['emoticon_1F603'] = 'Smiling face with open mouth'; +$LANGUAGE['emoticon_1F61B'] = 'Face with stuck-out tongue'; +$LANGUAGE['emoticon_1F632'] = 'Astonished face'; +$LANGUAGE['emoticon_1F609'] = 'Winking face'; +$LANGUAGE['emoticon_1F622'] = 'Crying face'; +$LANGUAGE['emoticon_1F610'] = 'Neutral face'; +$LANGUAGE['emoticon_1F635'] = 'Dizzy face'; +$LANGUAGE['emoticon_1F612'] = 'Unamused face'; +$LANGUAGE['emoticon_1F60E'] = 'Smiling face with sunglasses'; +$LANGUAGE['emoticon_1F61F'] = 'Worried face'; +$LANGUAGE['emoticon_1F602'] = 'Face with tears of joy'; +$LANGUAGE['emoticon_1F604'] = 'Smiling face with open mouth and smiling eyes'; + +#=============================================================================== +# Error messages +#=============================================================================== +$LANGUAGE['error_security_csrf'] = 'The security token does not matches the security token at server.'; +$LANGUAGE['error_database_exec'] = 'An unexpected error occurred while communicating with the database.'; + +#=============================================================================== +# Fulltext search +#=============================================================================== +$LANGUAGE['search_no_results'] = 'Sorry, there are no search results for "%s".'; + +#=============================================================================== +# Authentication +#=============================================================================== +$LANGUAGE['authentication_failure'] = 'The username or password is incorrect.'; + +#=============================================================================== +# Items [singular] +#=============================================================================== +$LANGUAGE['page'] = 'Page'; +$LANGUAGE['post'] = 'Post'; +$LANGUAGE['user'] = 'User'; + +#=============================================================================== +# Items [plural] +#=============================================================================== +$LANGUAGE['pages'] = 'Pages'; +$LANGUAGE['posts'] = 'Posts'; +$LANGUAGE['users'] = 'Users'; + +#=============================================================================== +# Actions +#=============================================================================== +$LANGUAGE['select'] = 'Show'; +$LANGUAGE['insert'] = 'Create'; +$LANGUAGE['update'] = 'Edit'; +$LANGUAGE['delete'] = 'Delete'; +$LANGUAGE['search'] = 'Search'; +$LANGUAGE['remove'] = 'Remove'; + +#=============================================================================== +# Previous items +#=============================================================================== +$LANGUAGE['prev_page'] = 'Previous page'; +$LANGUAGE['prev_post'] = 'Previous post'; +$LANGUAGE['prev_user'] = 'Previous user'; + +#=============================================================================== +# Next items +#=============================================================================== +$LANGUAGE['next_page'] = 'Next page'; +$LANGUAGE['next_post'] = 'Next post'; +$LANGUAGE['next_user'] = 'Next user'; + +#=============================================================================== +# Item overview +#=============================================================================== +$LANGUAGE['page_overview'] = 'Page overview'; +$LANGUAGE['post_overview'] = 'Post overview'; +$LANGUAGE['user_overview'] = 'User overview'; + +#=============================================================================== +# Items select +#=============================================================================== +$LANGUAGE['select_page'] = 'Show page'; +$LANGUAGE['select_post'] = 'Show post'; +$LANGUAGE['select_user'] = 'Show user'; + +#=============================================================================== +# Items insert +#=============================================================================== +$LANGUAGE['insert_page'] = 'Create page'; +$LANGUAGE['insert_post'] = 'Create post'; +$LANGUAGE['insert_user'] = 'Create user'; + +#=============================================================================== +# Items update +#=============================================================================== +$LANGUAGE['update_page'] = 'Edit page'; +$LANGUAGE['update_post'] = 'Edit post'; +$LANGUAGE['update_user'] = 'Edit user'; + +#=============================================================================== +# Items delete +#=============================================================================== +$LANGUAGE['delete_page'] = 'Delete page'; +$LANGUAGE['delete_post'] = 'Delete post'; +$LANGUAGE['delete_user'] = 'Delete user'; + +#=============================================================================== +# Item insert titles +#=============================================================================== +$LANGUAGE['title_page_insert'] = $LANGUAGE['insert_page']; +$LANGUAGE['title_post_insert'] = $LANGUAGE['insert_post']; +$LANGUAGE['title_user_insert'] = $LANGUAGE['insert_user']; + +#=============================================================================== +# Item update titles +#=============================================================================== +$LANGUAGE['title_page_update'] = $LANGUAGE['update_page']; +$LANGUAGE['title_post_update'] = $LANGUAGE['update_post']; +$LANGUAGE['title_user_update'] = $LANGUAGE['update_user']; + +#=============================================================================== +# Item delete titles +#=============================================================================== +$LANGUAGE['title_page_delete'] = $LANGUAGE['delete_page']; +$LANGUAGE['title_post_delete'] = $LANGUAGE['delete_post']; +$LANGUAGE['title_user_delete'] = $LANGUAGE['delete_user']; + +#=============================================================================== +# Item overview titles +#=============================================================================== +$LANGUAGE['title_page_overview'] = "{$LANGUAGE['page_overview']} [%d]"; +$LANGUAGE['title_post_overview'] = "{$LANGUAGE['post_overview']} [%d]"; +$LANGUAGE['title_user_overview'] = "{$LANGUAGE['user_overview']} [%d]"; + +#=============================================================================== +# Search titles +#=============================================================================== +$LANGUAGE['title_search_request'] = 'Fulltext search'; +$LANGUAGE['title_search_results'] = 'Results for "%s"'; +?> \ No newline at end of file diff --git a/core/namespace/Application.php b/core/namespace/Application.php new file mode 100644 index 0000000..39cb522 --- /dev/null +++ b/core/namespace/Application.php @@ -0,0 +1,148 @@ +loadLanguage(ROOT.'template/'.self::get('TEMPLATE.NAME').'/lang/'.self::get('TEMPLATE.LANG').'.php'); + self::$Language = $Language; + } + + return self::$Language; + } + + #=============================================================================== + # Return unique CSRF token for the current session + #=============================================================================== + public static function getSecurityToken(): string { + if(!isset($_SESSION['token'])) { + $_SESSION['token'] = getRandomValue(); + } + + return $_SESSION['token']; + } + + #=============================================================================== + # Return boolean if successfully authenticated + #=============================================================================== + public static function isAuthenticated(): bool { + return isset($_SESSION['auth']); + } + + #=============================================================================== + # Return absolute base URL + #=============================================================================== + public static function getURL($more = ''): string { + $prot = self::get('PATHINFO.PROT'); + $host = self::get('PATHINFO.HOST'); + $base = self::get('PATHINFO.BASE'); + + return "{$prot}://{$host}/{$base}{$more}"; + } + + #=============================================================================== + # Return absolute root URL + #=============================================================================== + public static function getAdminURL($more = ''): string { + return self::getURL("admin/{$more}"); + } + + #=============================================================================== + # Return absolute post URL + #=============================================================================== + public static function getPostURL($more = ''): string { + return self::getURL(self::get('POST.DIRECTORY')."/{$more}"); + } + + #=============================================================================== + # Return absolute page URL + #=============================================================================== + public static function getPageURL($more = ''): string { + return self::getURL(self::get('PAGE.DIRECTORY')."/{$more}"); + } + + #=============================================================================== + # Return absolute user URL + #=============================================================================== + public static function getUserURL($more = ''): string { + return self::getURL(self::get('USER.DIRECTORY')."/{$more}"); + } + + #=============================================================================== + # Return absolute file URL + #=============================================================================== + public static function getFileURL($more = ''): string { + return self::getURL("rsrc/{$more}"); + } + + #=============================================================================== + # Return absolute template URL + #=============================================================================== + public static function getTemplateURL($more = ''): string { + $template = self::get('TEMPLATE.NAME'); + return Application::getURL("template/{$template}/{$more}"); + } + + #=============================================================================== + # Exit application with + #=============================================================================== + public static function exit($code = 500) { + http_response_code($code); + $code === 404 AND require_once(ROOT."system/404.php"); + exit(); + } +} +?> \ No newline at end of file diff --git a/core/namespace/Attribute.php b/core/namespace/Attribute.php new file mode 100644 index 0000000..69998e0 --- /dev/null +++ b/core/namespace/Attribute.php @@ -0,0 +1,74 @@ +{$attribute} = $value; + } + + #=============================================================================== + # Get attribute + #=============================================================================== + public function get($attribute) { + return $this->{$attribute} ?? NULL; + } + + #=============================================================================== + # Get array with not FALSE attributes + #=============================================================================== + protected function getFilteredAttributes(): array { + return array_filter(get_object_vars($this), function($value) { + return $value !== FALSE; + }); + } + + #=============================================================================== + # Insert database item + #=============================================================================== + public function databaseINSERT(\Database $Database): bool { + $part[0] = ''; + $part[1] = ''; + + $attributes = $this->getFilteredAttributes(); + + foreach($attributes as $column => $value) { + $part[0] .= "{$column},"; + $part[1] .= '?,'; + } + + $part[0] = rtrim($part[0], ','); + $part[1] = rtrim($part[1], ','); + + $Statement = $Database->prepare('INSERT INTO '.static::TABLE." ({$part[0]}) VALUES ({$part[1]})"); + return $Statement->execute(array_values($attributes)); + } + + #=============================================================================== + # Update database item + #=============================================================================== + public function databaseUPDATE(\Database $Database): bool { + $part = ''; + + $attributes = $this->getFilteredAttributes(); + + foreach($attributes as $column => $value) { + $part .= "{$column} = ?,"; + } + + $part = rtrim($part, ','); + + $Statement = $Database->prepare('UPDATE '.static::TABLE.' SET '.$part.' WHERE id = '.(int) $this->get('id')); + return $Statement->execute(array_values($attributes)); + } + + #=============================================================================== + # Delete database item + #=============================================================================== + public function databaseDELETE(\Database $Database): bool { + $Statement = $Database->prepare('DELETE FROM '.static::TABLE.' WHERE id = ?'); + return $Statement->execute([$this->get('id')]); + } +} +?> \ No newline at end of file diff --git a/core/namespace/AttributeInterface.php b/core/namespace/AttributeInterface.php new file mode 100644 index 0000000..74cd1f1 --- /dev/null +++ b/core/namespace/AttributeInterface.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/core/namespace/Database.php b/core/namespace/Database.php new file mode 100644 index 0000000..ae233f4 --- /dev/null +++ b/core/namespace/Database.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/core/namespace/ExceptionHandler.php b/core/namespace/ExceptionHandler.php new file mode 100644 index 0000000..d64d74c --- /dev/null +++ b/core/namespace/ExceptionHandler.php @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/core/namespace/Factory.php b/core/namespace/Factory.php new file mode 100644 index 0000000..38be666 --- /dev/null +++ b/core/namespace/Factory.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/core/namespace/FactoryInterface.php b/core/namespace/FactoryInterface.php new file mode 100644 index 0000000..54a115b --- /dev/null +++ b/core/namespace/FactoryInterface.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/core/namespace/HTTP.php b/core/namespace/HTTP.php new file mode 100644 index 0000000..f9fbf01 --- /dev/null +++ b/core/namespace/HTTP.php @@ -0,0 +1,234 @@ + 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out' + ]; + + #=============================================================================== + # Initialize $GET, $POST and $FILE + #=============================================================================== + public static function init($GET, $POST, $FILE, $removeArrays = FALSE, $trimValues = TRUE) { + self::$GET = $GET; + self::$POST = $POST; + self::$FILE = $FILE; + + $removeArrays AND self::removeArrays(); + + self::$GET = ($trimValues === TRUE ? self::trim(self::$GET) : self::$GET ); + self::$POST = ($trimValues === TRUE ? self::trim(self::$POST) : self::$POST); + } + + #=============================================================================== + # Remove all arrays from $_GET and $_POST + #=============================================================================== + private static function removeArrays() { + foreach(['GET', 'POST'] as $HTTP) { + foreach(self::$$HTTP as $name => $value) { + if(is_array(self::$$HTTP[$name])) { + unset(self::$$HTTP[$name]); + } + } + } + } + + #=============================================================================== + # Trim all strings in argument + #=============================================================================== + private static function trim($mixed) { + if(is_array($mixed)) { + return array_map('self::trim', $mixed); + } + + return trim($mixed); + } + + #=============================================================================== + # Checks if all elements of $arguments are set as key of $data + #=============================================================================== + private static function issetData($data, $arguments) { + foreach($arguments as $key) { + if(is_array($key)) { + if(!isset($data[key($key)]) OR $data[key($key)] !== $key[key($key)]) { + return FALSE; + } + } + + else if(!isset($data[$key]) OR !is_string($data[$key])) { + return FALSE; + } + } + + return TRUE; + } + + #=============================================================================== + # Return null or the value (if set) from one of the three requests attributes + #=============================================================================== + public static function returnKey($data, array $paths) { + $current = &$data; + + foreach($paths as $path) { + if(!isset($current[$path])) { + return NULL; + } + $current = &$current[$path]; + } + + return $current; + } + + #=============================================================================== + # Return GET value + #=============================================================================== + public static function GET() { + return self::returnKey(self::$GET, func_get_args()); + } + + #=============================================================================== + # Return POST value + #=============================================================================== + public static function POST() { + return self::returnKey(self::$POST, func_get_args()); + } + + #=============================================================================== + # Return FILE value + #=============================================================================== + public static function FILE() { + return self::returnKey(self::$FILE, func_get_args()); + } + + #=============================================================================== + # Checks if all elements of func_get_args() are set key of self::$POST + #=============================================================================== + public static function issetPOST() { + return self::issetData(self::$POST, func_get_args()); + } + + #=============================================================================== + # Checks if all elements of func_get_args() are set key of self::$GET + #=============================================================================== + public static function issetGET() { + return self::issetData(self::$GET, func_get_args()); + } + + #=============================================================================== + # Checks if all elements of func_get_args() are set key of self::$FILE + #=============================================================================== + public static function issetFILE() { + return self::issetData(self::$FILE, func_get_args()); + } + + #=============================================================================== + # Return HTTP request method or check if request method equals with $method + #=============================================================================== + public static function requestMethod($method = NULL) { + if(!empty($method)) { + return ($_SERVER['REQUEST_METHOD'] === $method); + } + + return $_SERVER['REQUEST_METHOD']; + } + + #=============================================================================== + # Return REQUEST_URL + #=============================================================================== + public static function requestURI() { + return $_SERVER['REQUEST_URL'] ?? FALSE; + } + + #=============================================================================== + # Sends a HTTP header line to the client + #=============================================================================== + public static function responseHeader($field, $value) { + self::sendHeader("{$field}: {$value}"); + } + + #=============================================================================== + # Sends a HTTP redirect to the client + #=============================================================================== + public static function redirect($location, $code = 303, $exit = TRUE) { + http_response_code($code); + self::sendHeader("Location: {$location}"); + $exit AND exit(); + } + + #=============================================================================== + # Sends a new HTTP header line to the client if headers are not already sent + #=============================================================================== + private static function sendHeader($header) { + if(!headers_sent()) { + header($header); + } + } +} +?> \ No newline at end of file diff --git a/core/namespace/Item.php b/core/namespace/Item.php new file mode 100644 index 0000000..6d17011 --- /dev/null +++ b/core/namespace/Item.php @@ -0,0 +1,150 @@ +Database = $Database; + + $this->Reflection = new ReflectionObject($this); + + $attribute = "{$this->Reflection->getNamespaceName()}\\Attribute"; + $exception = "{$this->Reflection->getNamespaceName()}\\Exception"; + + #=============================================================================== + # Checking if item in database exists + #=============================================================================== + $Statement = $Database->prepare(sprintf('SELECT * FROM %s WHERE id = ?', $attribute::TABLE)); + $Statement->execute([$itemID]); + + #=============================================================================== + # Checking if retrieving data failed + #=============================================================================== + if(!$this->Attribute = $Statement->fetchObject($attribute)) { + throw new $exception(sprintf('%s\\Item with ID %s does not exists', $this->Reflection->getNamespaceName(), (int) $itemID)); + } + } + + #=============================================================================== + # Return attribute by name (short hand wrapper) + #=============================================================================== + public function attr($attribute) { + return $this->Attribute->get($attribute); + } + + #=============================================================================== + # Return Attribute object + #=============================================================================== + public final function getAttribute(): Attribute { + return $this->Attribute; + } + + #=============================================================================== + # Return unique ID + #=============================================================================== + public final function getID(): int { + return $this->Attribute->get('id'); + } + + #=============================================================================== + # Return pre-parsed content + #=============================================================================== + public function getBody(): string { + $content = preg_replace_callback('#\{(POST|PAGE|USER)\[([0-9]+)\]\}#', function($matches) { + $namespace = ucfirst(strtolower($matches[1])).'\\Factory'; + + try { + $Item = $namespace::build($matches[2]); + return $Item->getURL(); + } catch(Exception $Exception) { + return '{undefined}'; + } + }, $this->Attribute->get('body')); + + $content = preg_replace('#\{BASE\[\"([^"]+)\"\]\}#', \Application::getURL('$1'), $content); + $content = preg_replace('#\{FILE\[\"([^"]+)\"\]\}#', \Application::getFileURL('$1'), $content); + + return $content; + } + + #=============================================================================== + # Return parsed content + #=============================================================================== + public function getHTML(): string { + $item = "{$this->Reflection->getNamespaceName()}\\Item"; + + $Parsedown = new Parsedown(); + $content = $this->getBody(); + + if(\Application::get($item::CONFIGURATION.'.EMOTICONS') === TRUE) { + $content = parseEmoticons($content); + } + + return $Parsedown->text($content); + } + + #=============================================================================== + # Return attached files + #=============================================================================== + public function getFiles(): array { + if(preg_match_all('#\!\[(.*)\][ ]?(?:\n[ ]*)?\((.*)(\s[\'"](.*)[\'"])?\)#U', $this->getBody(), $matches)) { + return array_map('htmlentities', $matches[2]); + } + + return []; + } + + #=============================================================================== + # Return previous item ID + #=============================================================================== + public function getPrevID(): int { + $execute = 'SELECT id FROM %s WHERE DATE(time_insert) <= DATE(?) AND id < ? ORDER BY time_insert DESC, id DESC LIMIT 1'; + + $attribute = "{$this->Reflection->getNamespaceName()}\\Attribute"; + $Statement = $this->Database->prepare(sprintf($execute, $attribute::TABLE)); + + if($Statement->execute([$this->Attribute->get('time_insert'), $this->Attribute->get('id')])) { + return $Statement->fetchColumn(); + } + + return 0; + } + + #=============================================================================== + # Return next item ID + #=============================================================================== + public function getNextID(): int { + $execute = 'SELECT id FROM %s WHERE DATE(time_insert) >= DATE(?) AND id > ? ORDER BY time_insert ASC, id DESC LIMIT 1'; + + $attribute = "{$this->Reflection->getNamespaceName()}\\Attribute"; + $Statement = $this->Database->prepare(sprintf($execute, $attribute::TABLE)); + + if($Statement->execute([$this->Attribute->get('time_insert'), $this->Attribute->get('id')])) { + return $Statement->fetchColumn(); + } + + return 0; + } + + #=============================================================================== + # Return unique ID based on specific field comparison with value + #=============================================================================== + public static function getIDByField($field, $value, \Database $Database): int { + $attribute = (new ReflectionClass(get_called_class()))->getNamespaceName().'\\Attribute'; + $Statement = $Database->prepare('SELECT id FROM '.$attribute::TABLE." WHERE {$field} = ?"); + + if($Statement->execute([$value])) { + return $Statement->fetchColumn(); + } + + return 0; + } +} +?> \ No newline at end of file diff --git a/core/namespace/ItemFactory.php b/core/namespace/ItemFactory.php new file mode 100644 index 0000000..e5793d4 --- /dev/null +++ b/core/namespace/ItemFactory.php @@ -0,0 +1,12 @@ +getNamespaceName().'\\Item'; + $Instance = parent::storeInstance($itemID, new $Item($itemID, \Application::getDatabase())); + } + + return $Instance; + } +} +?> \ No newline at end of file diff --git a/core/namespace/ItemInterface.php b/core/namespace/ItemInterface.php new file mode 100644 index 0000000..e7ccb6a --- /dev/null +++ b/core/namespace/ItemInterface.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/core/namespace/Language.php b/core/namespace/Language.php new file mode 100644 index 0000000..c8a018e --- /dev/null +++ b/core/namespace/Language.php @@ -0,0 +1,48 @@ +language = $LANGUAGE; + } + + public function loadLanguage($filename) { + require $filename; + $this->template = $LANGUAGE; + } + + public function template($name, $params = FALSE) { + if(isset($this->template[$name])) { + if($params) { + return vsprintf($this->template[$name], $params); + } + + return $this->template[$name]; + } + + return "{{$name}}"; + } + + private function get($name, $params = FALSE) { + if(isset($this->language[$name])) { + if($params) { + return vsprintf($this->language[$name], $params); + } + + return $this->language[$name]; + } + + return "{{$name}}"; + } + + public function text($name, $params = FALSE) { + return $this->get($name, $params); + } + + public function set($name, $value) { + return $this->language[$name] = $value; + } +} +?> \ No newline at end of file diff --git a/core/namespace/Page/Attribute.php b/core/namespace/Page/Attribute.php new file mode 100644 index 0000000..c12a2c8 --- /dev/null +++ b/core/namespace/Page/Attribute.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/core/namespace/Page/Exception.php b/core/namespace/Page/Exception.php new file mode 100644 index 0000000..d4794b7 --- /dev/null +++ b/core/namespace/Page/Exception.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/core/namespace/Page/Factory.php b/core/namespace/Page/Factory.php new file mode 100644 index 0000000..f53b35f --- /dev/null +++ b/core/namespace/Page/Factory.php @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/core/namespace/Page/Item.php b/core/namespace/Page/Item.php new file mode 100644 index 0000000..c6cece7 --- /dev/null +++ b/core/namespace/Page/Item.php @@ -0,0 +1,29 @@ +Attribute->get('slug')}/"); + } + + return \Application::getPageURL("{$this->Attribute->get('id')}/"); + } + + #=============================================================================== + # Return unique pseudo GUID + #=============================================================================== + public function getGUID(): string { + foreach(\Application::get('PAGE.FEED_GUID') as $attribute) { + $attributes[] = $this->Attribute->get($attribute); + } + + return sha1(implode(NULL, $attributes)); + } +} +?> \ No newline at end of file diff --git a/core/namespace/Parsedown.php b/core/namespace/Parsedown.php new file mode 100644 index 0000000..610658b --- /dev/null +++ b/core/namespace/Parsedown.php @@ -0,0 +1,1548 @@ +DefinitionData = array(); + + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # remove surrounding line breaks + $text = trim($text, "\n"); + + # split text into lines + $lines = explode("\n", $text); + + # iterate through lines to identify blocks + $markup = $this->lines($lines); + + # trim line breaks + $markup = trim($markup, "\n"); + + return $markup; + } + + # + # Setters + # + + function setBreaksEnabled($breaksEnabled) + { + $this->breaksEnabled = $breaksEnabled; + + return $this; + } + + protected $breaksEnabled; + + function setMarkupEscaped($markupEscaped) + { + $this->markupEscaped = $markupEscaped; + + return $this; + } + + protected $markupEscaped; + + function setUrlsLinked($urlsLinked) + { + $this->urlsLinked = $urlsLinked; + + return $this; + } + + protected $urlsLinked = true; + + # + # Lines + # + + protected $BlockTypes = array( + '#' => array('Header'), + '*' => array('Rule', 'List'), + '+' => array('List'), + '-' => array('SetextHeader', 'Table', 'Rule', 'List'), + '0' => array('List'), + '1' => array('List'), + '2' => array('List'), + '3' => array('List'), + '4' => array('List'), + '5' => array('List'), + '6' => array('List'), + '7' => array('List'), + '8' => array('List'), + '9' => array('List'), + ':' => array('Table'), + '<' => array('Comment', 'Markup'), + '=' => array('SetextHeader'), + '>' => array('Quote'), + '[' => array('Reference'), + '_' => array('Rule'), + '`' => array('FencedCode'), + '|' => array('Table'), + '~' => array('FencedCode'), + ); + + # ~ + + protected $unmarkedBlockTypes = array( + 'Code', + ); + + # + # Blocks + # + + protected function lines(array $lines) + { + $CurrentBlock = null; + + foreach ($lines as $line) + { + if (chop($line) === '') + { + if (isset($CurrentBlock)) + { + $CurrentBlock['interrupted'] = true; + } + + continue; + } + + if (strpos($line, "\t") !== false) + { + $parts = explode("\t", $line); + + $line = $parts[0]; + + unset($parts[0]); + + foreach ($parts as $part) + { + $shortage = 4 - mb_strlen($line, 'utf-8') % 4; + + $line .= str_repeat(' ', $shortage); + $line .= $part; + } + } + + $indent = 0; + + while (isset($line[$indent]) and $line[$indent] === ' ') + { + $indent ++; + } + + $text = $indent > 0 ? substr($line, $indent) : $line; + + # ~ + + $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); + + # ~ + + if (isset($CurrentBlock['continuable'])) + { + $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); + + if (isset($Block)) + { + $CurrentBlock = $Block; + + continue; + } + else + { + if ($this->isBlockCompletable($CurrentBlock['type'])) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + } + } + + # ~ + + $marker = $text[0]; + + # ~ + + $blockTypes = $this->unmarkedBlockTypes; + + if (isset($this->BlockTypes[$marker])) + { + foreach ($this->BlockTypes[$marker] as $blockType) + { + $blockTypes []= $blockType; + } + } + + # + # ~ + + foreach ($blockTypes as $blockType) + { + $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); + + if (isset($Block)) + { + $Block['type'] = $blockType; + + if ( ! isset($Block['identified'])) + { + $Blocks []= $CurrentBlock; + + $Block['identified'] = true; + } + + if ($this->isBlockContinuable($blockType)) + { + $Block['continuable'] = true; + } + + $CurrentBlock = $Block; + + continue 2; + } + } + + # ~ + + if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) + { + $CurrentBlock['element']['text'] .= "\n".$text; + } + else + { + $Blocks []= $CurrentBlock; + + $CurrentBlock = $this->paragraph($Line); + + $CurrentBlock['identified'] = true; + } + } + + # ~ + + if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + + # ~ + + $Blocks []= $CurrentBlock; + + unset($Blocks[0]); + + # ~ + + $markup = ''; + + foreach ($Blocks as $Block) + { + if (isset($Block['hidden'])) + { + continue; + } + + $markup .= "\n"; + $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); + } + + $markup .= "\n"; + + # ~ + + return $markup; + } + + protected function isBlockContinuable($Type) + { + return method_exists($this, 'block'.$Type.'Continue'); + } + + protected function isBlockCompletable($Type) + { + return method_exists($this, 'block'.$Type.'Complete'); + } + + # + # Code + + protected function blockCode($Line, $Block = null) + { + if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted'])) + { + return; + } + + if ($Line['indent'] >= 4) + { + $text = substr($Line['body'], 4); + + $Block = array( + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => array( + 'name' => 'code', + 'text' => $text, + ), + ), + ); + + return $Block; + } + } + + protected function blockCodeContinue($Line, $Block) + { + if ($Line['indent'] >= 4) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['element']['text']['text'] .= "\n"; + + $text = substr($Line['body'], 4); + + $Block['element']['text']['text'] .= $text; + + return $Block; + } + } + + protected function blockCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Comment + + protected function blockComment($Line) + { + if ($this->markupEscaped) + { + return; + } + + if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') + { + $Block = array( + 'markup' => $Line['body'], + ); + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + } + + protected function blockCommentContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + $Block['markup'] .= "\n" . $Line['body']; + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + + # + # Fenced Code + + protected function blockFencedCode($Line) + { + if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches)) + { + $Element = array( + 'name' => 'code', + 'text' => '', + ); + + if (isset($matches[1])) + { + $class = 'language-'.$matches[1]; + + $Element['attributes'] = array( + 'class' => $class, + ); + } + + $Block = array( + 'char' => $Line['text'][0], + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => $Element, + ), + ); + + return $Block; + } + } + + protected function blockFencedCodeContinue($Line, $Block) + { + if (isset($Block['complete'])) + { + return; + } + + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text'])) + { + $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1); + + $Block['complete'] = true; + + return $Block; + } + + $Block['element']['text']['text'] .= "\n".$Line['body'];; + + return $Block; + } + + protected function blockFencedCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Header + + protected function blockHeader($Line) + { + if (isset($Line['text'][1])) + { + $level = 1; + + while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') + { + $level ++; + } + + if ($level > 6) + { + return; + } + + $text = trim($Line['text'], '# '); + + $Block = array( + 'element' => array( + 'name' => 'h' . min(6, $level), + 'text' => $text, + 'handler' => 'line', + ), + ); + + return $Block; + } + } + + # + # List + + protected function blockList($Line) + { + list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]'); + + if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'indent' => $Line['indent'], + 'pattern' => $pattern, + 'element' => array( + 'name' => $name, + 'handler' => 'elements', + ), + ); + + if($name === 'ol') + { + $listStart = stristr($matches[0], '.', true); + + if($listStart !== '1') + { + $Block['element']['attributes'] = array('start' => $listStart); + } + } + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $matches[2], + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + } + + protected function blockListContinue($Line, array $Block) + { + if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['li']['text'] []= ''; + + unset($Block['interrupted']); + } + + unset($Block['li']); + + $text = isset($matches[1]) ? $matches[1] : ''; + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $text, + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + + if ($Line['text'][0] === '[' and $this->blockReference($Line)) + { + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + return $Block; + } + + if ($Line['indent'] > 0) + { + $Block['li']['text'] []= ''; + + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + unset($Block['interrupted']); + + return $Block; + } + } + + # + # Quote + + protected function blockQuote($Line) + { + if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'element' => array( + 'name' => 'blockquote', + 'handler' => 'lines', + 'text' => (array) $matches[1], + ), + ); + + return $Block; + } + } + + protected function blockQuoteContinue($Line, array $Block) + { + if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text'] []= ''; + + unset($Block['interrupted']); + } + + $Block['element']['text'] []= $matches[1]; + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $Block['element']['text'] []= $Line['text']; + + return $Block; + } + } + + # + # Rule + + protected function blockRule($Line) + { + if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) + { + $Block = array( + 'element' => array( + 'name' => 'hr' + ), + ); + + return $Block; + } + } + + # + # Setext + + protected function blockSetextHeader($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (chop($Line['text'], $Line['text'][0]) === '') + { + $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; + + return $Block; + } + } + + # + # Markup + + protected function blockMarkup($Line) + { + if ($this->markupEscaped) + { + return; + } + + if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) + { + $element = strtolower($matches[1]); + + if (in_array($element, $this->textLevelElements)) + { + return; + } + + $Block = array( + 'name' => $matches[1], + 'depth' => 0, + 'markup' => $Line['text'], + ); + + $length = strlen($matches[0]); + + $remainder = substr($Line['text'], $length); + + if (trim($remainder) === '') + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + $Block['closed'] = true; + + $Block['void'] = true; + } + } + else + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + return; + } + + if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) + { + $Block['closed'] = true; + } + } + + return $Block; + } + } + + protected function blockMarkupContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open + { + $Block['depth'] ++; + } + + if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close + { + if ($Block['depth'] > 0) + { + $Block['depth'] --; + } + else + { + $Block['closed'] = true; + } + } + + if (isset($Block['interrupted'])) + { + $Block['markup'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['markup'] .= "\n".$Line['body']; + + return $Block; + } + + # + # Reference + + protected function blockReference($Line) + { + if (preg_match('/^\[(.+?)\]:[ ]*?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) + { + $id = strtolower($matches[1]); + + $Data = array( + 'url' => $matches[2], + 'title' => null, + ); + + if (isset($matches[3])) + { + $Data['title'] = $matches[3]; + } + + $this->DefinitionData['Reference'][$id] = $Data; + + $Block = array( + 'hidden' => true, + ); + + return $Block; + } + } + + # + # Table + + protected function blockTable($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') + { + $alignments = array(); + + $divider = $Line['text']; + + $divider = trim($divider); + $divider = trim($divider, '|'); + + $dividerCells = explode('|', $divider); + + foreach ($dividerCells as $dividerCell) + { + $dividerCell = trim($dividerCell); + + if ($dividerCell === '') + { + continue; + } + + $alignment = null; + + if ($dividerCell[0] === ':') + { + $alignment = 'left'; + } + + if (substr($dividerCell, - 1) === ':') + { + $alignment = $alignment === 'left' ? 'center' : 'right'; + } + + $alignments []= $alignment; + } + + # ~ + + $HeaderElements = array(); + + $header = $Block['element']['text']; + + $header = trim($header); + $header = trim($header, '|'); + + $headerCells = explode('|', $header); + + foreach ($headerCells as $index => $headerCell) + { + $headerCell = trim($headerCell); + + $HeaderElement = array( + 'name' => 'th', + 'text' => $headerCell, + 'handler' => 'line', + ); + + if (isset($alignments[$index])) + { + $alignment = $alignments[$index]; + + $HeaderElement['attributes'] = array( + 'style' => 'text-align: '.$alignment.';', + ); + } + + $HeaderElements []= $HeaderElement; + } + + # ~ + + $Block = array( + 'alignments' => $alignments, + 'identified' => true, + 'element' => array( + 'name' => 'table', + 'handler' => 'elements', + ), + ); + + $Block['element']['text'] []= array( + 'name' => 'thead', + 'handler' => 'elements', + ); + + $Block['element']['text'] []= array( + 'name' => 'tbody', + 'handler' => 'elements', + 'text' => array(), + ); + + $Block['element']['text'][0]['text'] []= array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $HeaderElements, + ); + + return $Block; + } + } + + protected function blockTableContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) + { + $Elements = array(); + + $row = $Line['text']; + + $row = trim($row); + $row = trim($row, '|'); + + preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); + + foreach ($matches[0] as $index => $cell) + { + $cell = trim($cell); + + $Element = array( + 'name' => 'td', + 'handler' => 'line', + 'text' => $cell, + ); + + if (isset($Block['alignments'][$index])) + { + $Element['attributes'] = array( + 'style' => 'text-align: '.$Block['alignments'][$index].';', + ); + } + + $Elements []= $Element; + } + + $Element = array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $Elements, + ); + + $Block['element']['text'][1]['text'] []= $Element; + + return $Block; + } + } + + # + # ~ + # + + protected function paragraph($Line) + { + $Block = array( + 'element' => array( + 'name' => 'p', + 'text' => $Line['text'], + 'handler' => 'line', + ), + ); + + return $Block; + } + + # + # Inline Elements + # + + protected $InlineTypes = array( + '"' => array('SpecialCharacter'), + '!' => array('Image'), + '&' => array('SpecialCharacter'), + '*' => array('Emphasis'), + ':' => array('Url'), + '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'), + '>' => array('SpecialCharacter'), + '[' => array('Link'), + '_' => array('Emphasis'), + '`' => array('Code'), + '~' => array('Strikethrough'), + '\\' => array('EscapeSequence'), + ); + + # ~ + + protected $inlineMarkerList = '!"*_&[:<>`~\\'; + + # + # ~ + # + + public function line($text) + { + $markup = ''; + + # $excerpt is based on the first occurrence of a marker + + while ($excerpt = strpbrk($text, $this->inlineMarkerList)) + { + $marker = $excerpt[0]; + + $markerPosition = strpos($text, $marker); + + $Excerpt = array('text' => $excerpt, 'context' => $text); + + foreach ($this->InlineTypes[$marker] as $inlineType) + { + $Inline = $this->{'inline'.$inlineType}($Excerpt); + + if ( ! isset($Inline)) + { + continue; + } + + # makes sure that the inline belongs to "our" marker + + if (isset($Inline['position']) and $Inline['position'] > $markerPosition) + { + continue; + } + + # sets a default inline position + + if ( ! isset($Inline['position'])) + { + $Inline['position'] = $markerPosition; + } + + # the text that comes before the inline + $unmarkedText = substr($text, 0, $Inline['position']); + + # compile the unmarked text + $markup .= $this->unmarkedText($unmarkedText); + + # compile the inline + $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']); + + # remove the examined text + $text = substr($text, $Inline['position'] + $Inline['extent']); + + continue 2; + } + + # the marker does not belong to an inline + + $unmarkedText = substr($text, 0, $markerPosition + 1); + + $markup .= $this->unmarkedText($unmarkedText); + + $text = substr($text, $markerPosition + 1); + } + + $markup .= $this->unmarkedText($text); + + return $markup; + } + + # + # ~ + # + + protected function inlineCode($Excerpt) + { + $marker = $Excerpt['text'][0]; + + if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(? strlen($matches[0]), + 'element' => array( + 'name' => 'code', + 'text' => $text, + ), + ); + } + } + + protected function inlineEmailTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) + { + $url = $matches[1]; + + if ( ! isset($matches[2])) + { + $url = 'mailto:' . $url; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $matches[1], + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + protected function inlineEmphasis($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + $marker = $Excerpt['text'][0]; + + if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'strong'; + } + elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'em'; + } + else + { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => $emphasis, + 'handler' => 'line', + 'text' => $matches[1], + ), + ); + } + + protected function inlineEscapeSequence($Excerpt) + { + if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) + { + return array( + 'markup' => $Excerpt['text'][1], + 'extent' => 2, + ); + } + } + + protected function inlineImage($Excerpt) + { + if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') + { + return; + } + + $Excerpt['text']= substr($Excerpt['text'], 1); + + $Link = $this->inlineLink($Excerpt); + + if ($Link === null) + { + return; + } + + $Inline = array( + 'extent' => $Link['extent'] + 1, + 'element' => array( + 'name' => 'img', + 'attributes' => array( + 'src' => $Link['element']['attributes']['href'], + 'alt' => $Link['element']['text'], + ), + ), + ); + + $Inline['element']['attributes'] += $Link['element']['attributes']; + + unset($Inline['element']['attributes']['href']); + + return $Inline; + } + + protected function inlineLink($Excerpt) + { + $Element = array( + 'name' => 'a', + 'handler' => 'line', + 'text' => null, + 'attributes' => array( + 'href' => null, + 'title' => null, + ), + ); + + $extent = 0; + + $remainder = $Excerpt['text']; + + if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches)) + { + $Element['text'] = $matches[1]; + + $extent += strlen($matches[0]); + + $remainder = substr($remainder, $extent); + } + else + { + return; + } + + if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches)) + { + $Element['attributes']['href'] = $matches[1]; + + if (isset($matches[2])) + { + $Element['attributes']['title'] = substr($matches[2], 1, - 1); + } + + $extent += strlen($matches[0]); + } + else + { + if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) + { + $definition = strlen($matches[1]) ? $matches[1] : $Element['text']; + $definition = strtolower($definition); + + $extent += strlen($matches[0]); + } + else + { + $definition = strtolower($Element['text']); + } + + if ( ! isset($this->DefinitionData['Reference'][$definition])) + { + return; + } + + $Definition = $this->DefinitionData['Reference'][$definition]; + + $Element['attributes']['href'] = $Definition['url']; + $Element['attributes']['title'] = $Definition['title']; + } + + $Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']); + + return array( + 'extent' => $extent, + 'element' => $Element, + ); + } + + protected function inlineMarkup($Excerpt) + { + if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false) + { + return; + } + + if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + } + + protected function inlineSpecialCharacter($Excerpt) + { + if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text'])) + { + return array( + 'markup' => '&', + 'extent' => 1, + ); + } + + $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); + + if (isset($SpecialCharacter[$Excerpt['text'][0]])) + { + return array( + 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', + 'extent' => 1, + ); + } + } + + protected function inlineStrikethrough($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) + { + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'del', + 'text' => $matches[1], + 'handler' => 'line', + ), + ); + } + } + + protected function inlineUrl($Excerpt) + { + if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') + { + return; + } + + if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) + { + $Inline = array( + 'extent' => strlen($matches[0][0]), + 'position' => $matches[0][1], + 'element' => array( + 'name' => 'a', + 'text' => $matches[0][0], + 'attributes' => array( + 'href' => $matches[0][0], + ), + ), + ); + + return $Inline; + } + } + + protected function inlineUrlTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) + { + $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]); + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + # ~ + + protected function unmarkedText($text) + { + if ($this->breaksEnabled) + { + $text = preg_replace('/[ ]*\n/', "
\n", $text); + } + else + { + $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $text); + $text = str_replace(" \n", "\n", $text); + } + + return $text; + } + + # + # Handlers + # + + protected function element(array $Element) + { + $markup = '<'.$Element['name']; + + if (isset($Element['attributes'])) + { + foreach ($Element['attributes'] as $name => $value) + { + if ($value === null) + { + continue; + } + + $markup .= ' '.$name.'="'.$value.'"'; + } + } + + if (isset($Element['text'])) + { + $markup .= '>'; + + if (isset($Element['handler'])) + { + $markup .= $this->{$Element['handler']}($Element['text']); + } + else + { + $markup .= $Element['text']; + } + + $markup .= ''; + } + else + { + $markup .= ' />'; + } + + return $markup; + } + + protected function elements(array $Elements) + { + $markup = ''; + + foreach ($Elements as $Element) + { + $markup .= "\n" . $this->element($Element); + } + + $markup .= "\n"; + + return $markup; + } + + # ~ + + protected function li($lines) + { + $markup = $this->lines($lines); + + $trimmedMarkup = trim($markup); + + if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '

') + { + $markup = $trimmedMarkup; + $markup = substr($markup, 3); + + $position = strpos($markup, "

"); + + $markup = substr_replace($markup, '', $position, 4); + } + + return $markup; + } + + # + # Deprecated Methods + # + + function parse($text) + { + $markup = $this->text($text); + + return $markup; + } + + # + # Static Methods + # + + static function instance($name = 'default') + { + if (isset(self::$instances[$name])) + { + return self::$instances[$name]; + } + + $instance = new static(); + + self::$instances[$name] = $instance; + + return $instance; + } + + private static $instances = array(); + + # + # Fields + # + + protected $DefinitionData; + + # + # Read-Only + + protected $specialCharacters = array( + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', + ); + + protected $StrongRegex = array( + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', + ); + + protected $EmRegex = array( + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ); + + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; + + protected $voidElements = array( + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', + ); + + protected $textLevelElements = array( + 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', + 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', + 'i', 'rp', 'del', 'code', 'strike', 'marquee', + 'q', 'rt', 'ins', 'font', 'strong', + 's', 'tt', 'sub', 'mark', + 'u', 'xm', 'sup', 'nobr', + 'var', 'ruby', + 'wbr', 'span', + 'time', + ); +} diff --git a/core/namespace/Post/Attribute.php b/core/namespace/Post/Attribute.php new file mode 100644 index 0000000..6f20183 --- /dev/null +++ b/core/namespace/Post/Attribute.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/core/namespace/Post/Exception.php b/core/namespace/Post/Exception.php new file mode 100644 index 0000000..516ddbe --- /dev/null +++ b/core/namespace/Post/Exception.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/core/namespace/Post/Factory.php b/core/namespace/Post/Factory.php new file mode 100644 index 0000000..34aa5e4 --- /dev/null +++ b/core/namespace/Post/Factory.php @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/core/namespace/Post/Item.php b/core/namespace/Post/Item.php new file mode 100644 index 0000000..a269ce4 --- /dev/null +++ b/core/namespace/Post/Item.php @@ -0,0 +1,50 @@ +Attribute->get('slug')}/"); + } + + return \Application::getPostURL("{$this->Attribute->get('id')}/"); + } + + #=============================================================================== + # Return unique pseudo GUID + #=============================================================================== + public function getGUID(): string { + foreach(\Application::get('POST.FEED_GUID') as $attribute) { + $attributes[] = $this->Attribute->get($attribute); + } + + return sha1(implode(NULL, $attributes)); + } + + #=============================================================================== + # Return unique post IDs for search results + #=============================================================================== + public static function getSearchResultIDs($search, array $date, \Database $Database): array { + $D = ($D = intval($date[0])) !== 0 ? $D : 'NULL'; + $M = ($M = intval($date[1])) !== 0 ? $M : 'NULL'; + $Y = ($Y = intval($date[2])) !== 0 ? $Y : 'NULL'; + + $Statement = $Database->prepare(sprintf("SELECT id FROM %s WHERE + ({$Y} IS NULL OR YEAR(time_insert) = {$Y}) AND + ({$M} IS NULL OR MONTH(time_insert) = {$M}) AND + ({$D} IS NULL OR DAY(time_insert) = {$D}) AND + MATCH(name, body) AGAINST(? IN BOOLEAN MODE) LIMIT 20", Attribute::TABLE)); + + if($Statement->execute([$search])) { + return $Statement->fetchAll($Database::FETCH_COLUMN); + } + + return []; + } +} +?> \ No newline at end of file diff --git a/core/namespace/Template/Exception.php b/core/namespace/Template/Exception.php new file mode 100644 index 0000000..52c522e --- /dev/null +++ b/core/namespace/Template/Exception.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/core/namespace/Template/Factory.php b/core/namespace/Template/Factory.php new file mode 100644 index 0000000..a90c61e --- /dev/null +++ b/core/namespace/Template/Factory.php @@ -0,0 +1,18 @@ +set('Language', \Application::getLanguage()); + $Template->set('BLOGMETA', [ + 'NAME' => \Application::get('BLOGMETA.NAME'), + 'DESC' => \Application::get('BLOGMETA.DESC'), + 'MAIL' => \Application::get('BLOGMETA.MAIL'), + 'LANG' => \Application::get('BLOGMETA.LANG'), + ]); + + return $Template; + } +} +?> \ No newline at end of file diff --git a/core/namespace/Template/Template.php b/core/namespace/Template/Template.php new file mode 100644 index 0000000..0c68f92 --- /dev/null +++ b/core/namespace/Template/Template.php @@ -0,0 +1,72 @@ +filename = $filename; + + if(!file_exists($filename)) { + throw new Exception("Template {$filename} does not exists."); + } + } + + #=============================================================================== + # Set value to array path + #=============================================================================== + public function set($name, $value) { + if(!is_array($name)) { + return $this->parameters[$name] = $value; + } + + $current = &$this->parameters; + + foreach($name as $path) { + if(!isset($current[$path])) { + $current[$path] = []; + } + $current = &$current[$path]; + } + + return $current = $value; + } + + #=============================================================================== + # Add value as item to array path + #=============================================================================== + public function add($paths, $value) { + if(!is_array($paths)) { + return $this->parameters[$paths][] = $value; + } + + $current = &$this->parameters; + + foreach($paths as $path) { + if(!isset($current[$path])) { + $current[$path] = []; + } + $current = &$current[$path]; + } + + return $current[] = $value; + } + + #=============================================================================== + # Return parsed template content + #=============================================================================== + public function __toString() { + foreach($this->parameters as $name => $value) { + ${$name} = $value; + } + + ob_start(); + require $this->filename; + return ob_get_clean(); + } +} +?> \ No newline at end of file diff --git a/core/namespace/User/Attribute.php b/core/namespace/User/Attribute.php new file mode 100644 index 0000000..b161fa9 --- /dev/null +++ b/core/namespace/User/Attribute.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/core/namespace/User/Exception.php b/core/namespace/User/Exception.php new file mode 100644 index 0000000..b5bcad0 --- /dev/null +++ b/core/namespace/User/Exception.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/core/namespace/User/Factory.php b/core/namespace/User/Factory.php new file mode 100644 index 0000000..80e6b49 --- /dev/null +++ b/core/namespace/User/Factory.php @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/core/namespace/User/Item.php b/core/namespace/User/Item.php new file mode 100644 index 0000000..129d8f9 --- /dev/null +++ b/core/namespace/User/Item.php @@ -0,0 +1,36 @@ +Attribute->get('slug')}/"); + } + + return \Application::getUserURL("{$this->Attribute->get('id')}/"); + } + + #=============================================================================== + # Return unique pseudo GUID + #=============================================================================== + public function getGUID(): string { + foreach(['id', 'time_insert'] as $attribute) { + $attributes[] = $this->Attribute->get($attribute); + } + + return sha1(implode(NULL, $attributes)); + } + + #=============================================================================== + # Compare plaintext password with hashed password from database + #=============================================================================== + public function comparePassword($password): bool { + return password_verify($password, $this->Attribute->get('password')); + } +} +?> \ No newline at end of file diff --git a/database.sql b/database.sql new file mode 100644 index 0000000..efcd044 --- /dev/null +++ b/database.sql @@ -0,0 +1,66 @@ +-- ============================================================================= +-- Table structure for page items +-- ============================================================================= +CREATE TABLE `page` ( + `id` smallint(6) NOT NULL, + `time_insert` datetime NOT NULL, + `time_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `user` tinyint(4) NOT NULL, + `slug` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `name` varchar(100) NOT NULL, + `body` text NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ============================================================================= +-- Table structure for post items +-- ============================================================================= +CREATE TABLE `post` ( + `id` smallint(6) NOT NULL, + `time_insert` datetime NOT NULL, + `time_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `user` tinyint(4) NOT NULL, + `slug` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `name` varchar(100) NOT NULL, + `body` text NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; + +-- ============================================================================= +-- Table structure for user items +-- ============================================================================= +CREATE TABLE `user` ( + `id` tinyint(4) NOT NULL, + `time_insert` datetime NOT NULL, + `time_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `slug` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `password` char(64) CHARACTER SET latin1 DEFAULT NULL, + `fullname` varchar(40) NOT NULL, + `mailaddr` varchar(60) NOT NULL, + `body` text NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ============================================================================= +-- Insert demo page, post and user +-- ============================================================================= +INSERT INTO `page` (`id`, `time_insert`, `time_update`, `user`, `slug`, `name`, `body`) VALUES +(1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 'example-page', 'Example Page', 'OK. You discovered that there is also a page functionality. But what is the difference between a **page** and a **post**? This is simple: There is not really much difference. But you can style posts and pages within the templates CSS completely independent from each other. For example, use **pages** for things like your imprint, your terms of use, your FAQ or other stuff. And **posts** for your main blog posts. A **page** (and also a **user**) has exactly the same functionality as already described within the [first post]({POST[1]})! 8)'); +INSERT INTO `post` (`id`, `time_insert`, `time_update`, `user`, `slug`, `name`, `body`) VALUES +(1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, 'hello-world', 'Hello World!', 'Hello! This is the automatically generated first post on your new blog installation. You can type [Markdown](https://daringfireball.net/projects/markdown/) plaintext into the editor to format your content as you want. In this post you can see several examples to [format your content with Markdown](https://daringfireball.net/projects/markdown/syntax) and with the special features provided by this blog application. After you are familiar with the text formatting and done with the exploration of your new blog application, you can delete this post and create your own. Have fun! :)\r\n\r\n![Demo image: Computer Guy (Public Domain)]({FILE[\"image/content/computer-guy-public-domain.svg\"]})\r\n\r\n## Parsing emoticons (if `POST.EMOTICONS` is `TRUE` within your `configuration.php`)\r\n> You can insert one or more of the following emoticons into your posts by typing the emoticon as simple ASCII text. The emoticon parser will convert your ASCII emoticon to the HTML multibyte unicode equivalent. Each emoticon comes with an further explanation if you just hold your mouse over a emoticons face: \r\n> :) :( :D :P :O ;) ;( :| :X :/ 8) :S xD ^^\r\n\r\n## Dynamic internal URLs for items\r\nIf you want to link an item, please don\'t put the URL to the item hardcoded into your content! What if you want to change your site address (or the base directory) in the future? Then you have to change all links in your content. This is not cool! Thus, you can use the following code **without spaces between the braces** by knowing the ID of an item to link it dynamically:\r\n\r\n1. Example: `{ POST[1] }` \r\n{POST[1]}\r\n\r\n2. Example: `{ PAGE[1] }` \r\n{PAGE[1]}\r\n\r\n3. Example: `{ USER[1] }` \r\n{USER[1]}\r\n\r\n## Dynamic internal URLs for other resources\r\nThis also applies to any other resource that exists in the blog system and that you want to link to! You can link any other resource dynamically either relative to your base directory or relative to your resource directory for static content:\r\n\r\n* Example: `{ BASE[\"foo/bar/\"] }` \r\n{BASE[\"foo/bar/\"]}\r\n\r\n* Example: `{ FILE[\"foo/bar/\"] }` \r\n{FILE[\"foo/bar/\"]}\r\n\r\n### Anywhere …\r\nYou can use these codes anywhere in your markdown plaintext. This codes will be pre-parsed before the markdown parser gets the content. If the markdown parser begins then all codes already have been converted into the URLs.'); +INSERT INTO `user` (`id`, `time_insert`, `time_update`, `slug`, `username`, `password`, `fullname`, `mailaddr`, `body`) VALUES +(1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'change-me', 'ChangeMe', '$2y$10$jH48L1K1y9dB303aI2biN.ob0biZDuUbMxPKadi3wDqOIxj6yNT6K', 'John Doe', 'mail@example.org', 'Describe yourself.'); + +-- ============================================================================= +-- Add keys for tables +-- ============================================================================= +ALTER TABLE `page` ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `slug` (`slug`), ADD KEY `page_user` (`user`); +ALTER TABLE `post` ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `slug` (`slug`), ADD KEY `post_user` (`user`); +ALTER TABLE `post` ADD FULLTEXT KEY `body` (`body`); +ALTER TABLE `user` ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `username` (`username`), ADD UNIQUE KEY `slug` (`slug`); + +-- ============================================================================= +-- Add AUTO_INCREMENT for tables +-- ============================================================================= +ALTER TABLE `page` MODIFY `id` smallint(6) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; +ALTER TABLE `post` MODIFY `id` smallint(6) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; +ALTER TABLE `user` MODIFY `id` tinyint(4) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; +ALTER TABLE `page` ADD CONSTRAINT `page_user` FOREIGN KEY (`user`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..b07fee6 --- /dev/null +++ b/index.php @@ -0,0 +1,54 @@ +query(sprintf($execSQL, Post\Attribute::TABLE)); + + $postIDs = $Statement->fetchAll($Database::FETCH_COLUMN); + + foreach($postIDs as $postID) { + try { + $Post = Post\Factory::build($postID); + $User = User\Factory::build($Post->attr('user')); + + $ItemTemplate = generatePostItemTemplate($Post, $User); + + $posts[] = $ItemTemplate; + } + catch(Post\Exception $Exception){} + catch(User\Exception $Exception){} + } + + $HomeTemplate = Template\Factory::build('home'); + $HomeTemplate->set('PAGINATION', [ + 'HTML' => generatePostNaviTemplate(1) + ]); + $HomeTemplate->set('LIST', [ + 'POSTS' => $posts ?? [] + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('HTML', $HomeTemplate); + $MainTemplate->set('HEAD', [ + 'NAME' => Application::get('BLOGMETA.HOME'), + 'DESC' => Application::get('BLOGMETA.NAME').' – '.Application::get('BLOGMETA.DESC'), + 'PERM' => Application::getURL() + ]); + + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..c0bc0eb --- /dev/null +++ b/readme.md @@ -0,0 +1,20 @@ +# PHP7 blogging application +Easy blogging application written with pure PHP7! The application comes with a fulltext search functionality for posts and with customizable templates and languages. You can build your own template if the standard template does not satisfy you! You can see the application in action with a custom template on my private blog at [blog.nerdmind.de](https://blog.nerdmind.de/)! + +## Minimalistic standard template +![Standard template](https://nmnd.de/file/p/github-blog/standard-template.png) + +## Clean administration interface … +![Administration interface](https://nmnd.de/file/p/github-blog/admin-template-2.png) + +## … with a nice content editor +![Content editor](https://nmnd.de/file/p/github-blog/admin-template-1.png) + +## Installation +1. Download the repository and extract it to the target directory where it should be installed. +2. Create your MySQL database and import the `database.sql` file. +3. Rename `core/configuration-example.php` to `core/configuration.php` and customize the configuration and set in any case the settings for the database connection. +4. Navigate your browser to `/admin/auth.php` and authenticate with the default username `ChangeMe` and the password `changeme` (please note that the username is case-sensitive). + +## Wiki +More information about the configuration and customization on the **wiki** of this repository! \ No newline at end of file diff --git a/rsrc/image/content/computer-guy-public-domain.svg b/rsrc/image/content/computer-guy-public-domain.svg new file mode 100644 index 0000000..44b6eef --- /dev/null +++ b/rsrc/image/content/computer-guy-public-domain.svg @@ -0,0 +1,242 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/system/403.php b/system/403.php new file mode 100644 index 0000000..08ef44d --- /dev/null +++ b/system/403.php @@ -0,0 +1,29 @@ +set('HEAD', [ + 'NAME' => '403 Forbidden', + 'DESC' => "You don't have permission to access {$_SERVER['REQUEST_URI']} on this server." + ]); + + $MainTemplate->set('HTML', Template\Factory::build('403')); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/system/404.php b/system/404.php new file mode 100644 index 0000000..a92e9d6 --- /dev/null +++ b/system/404.php @@ -0,0 +1,29 @@ +set('HEAD', [ + 'NAME' => '404 Not Found', + 'DESC' => "The requested URL {$_SERVER['REQUEST_URI']} was not found on this server." + ]); + + $MainTemplate->set('HTML', Template\Factory::build('404')); + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/system/feed/main.php b/system/feed/main.php new file mode 100644 index 0000000..00ac4b8 --- /dev/null +++ b/system/feed/main.php @@ -0,0 +1,76 @@ +query(sprintf($execSQL, Post\Attribute::TABLE))->fetchAll($Database::FETCH_COLUMN); + + foreach($postIDs as $postID) { + try { + $Post = Post\Factory::build($postID); + $User = User\Factory::build($Post->attr('user')); + + $ItemTemplate = Template\Factory::build('feed/item_post'); + $ItemTemplate->set('POST', generatePostItemData($Post)); + $ItemTemplate->set('USER', generateUserItemData($User)); + + $posts[] = $ItemTemplate; + } + + catch(Post\Exception $Exception){} + catch(User\Exception $Exception){} + } + } + + if(HTTP::GET('item') !== 'post') { + $execSQL = 'SELECT id FROM %s ORDER BY '.Application::get('PAGE.FEED_SORT').' LIMIT '.Application::get('PAGE.FEED_SIZE'); + $pageIDs = $Database->query(sprintf($execSQL, Page\Attribute::TABLE))->fetchAll($Database::FETCH_COLUMN); + + foreach($pageIDs as $pageID) { + try { + $Page = Page\Factory::build($pageID); + $User = User\Factory::build($Page->attr('user')); + + $ItemTemplate = Template\Factory::build('feed/item_page'); + $ItemTemplate->set('PAGE', generatePageItemData($Page)); + $ItemTemplate->set('USER', generateUserItemData($User)); + + $pages[] = $ItemTemplate; + } + + catch(Page\Exception $Exception){} + catch(User\Exception $Exception){} + } + } + + $FeedTemplate = Template\Factory::build('feed/main'); + $FeedTemplate->set('FEED', [ + 'TYPE' => HTTP::GET('item'), + 'LIST' => [ + 'POSTS' => $posts ?? [], + 'PAGES' => $pages ?? [], + ] + ]); + + echo $FeedTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/system/page/list.php b/system/page/list.php new file mode 100644 index 0000000..5d07d30 --- /dev/null +++ b/system/page/list.php @@ -0,0 +1,64 @@ +query(sprintf('SELECT COUNT(id) FROM %s', Page\Attribute::TABLE))->fetchColumn() / $site_size); + +$currentSite = HTTP::GET('site') ?? 1; +$currentSite = abs(intval($currentSite)); + +if($currentSite < 1 OR ($currentSite > $lastSite AND $lastSite > 0)) { + Application::exit(404); +} + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + $execSQL = "SELECT id FROM %s ORDER BY {$site_sort} LIMIT ".(($currentSite-1) * $site_size).", {$site_size}"; + $pageIDs = $Database->query(sprintf($execSQL, Page\Attribute::TABLE))->fetchAll($Database::FETCH_COLUMN); + + foreach($pageIDs as $pageID) { + try { + $Page = Page\Factory::build($pageID); + $User = User\Factory::build($Page->attr('user')); + + $ItemTemplate = generatePageItemTemplate($Page, $User); + + $pages[] = $ItemTemplate; + } + catch(Page\Exception $Exception){} + catch(User\Exception $Exception){} + } + + $ListTemplate = Template\Factory::build('page/list'); + $ListTemplate->set('PAGINATION', [ + 'THIS' => $currentSite, + 'LAST' => $lastSite, + 'HTML' => generatePageNaviTemplate($currentSite) + ]); + $ListTemplate->set('LIST', [ + 'PAGES' => $pages ?? [] + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('HTML', $ListTemplate); + $MainTemplate->set('HEAD', [ + 'NAME' => $Language->text('title_page_overview', $currentSite) + ]); + + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/system/page/main.php b/system/page/main.php new file mode 100644 index 0000000..fdd5399 --- /dev/null +++ b/system/page/main.php @@ -0,0 +1,89 @@ +attr('user')); + + $page_data = generatePageItemData($Page); + $user_data = generateUserItemData($User); + + #=============================================================================== + # Add post data for previous and next post if exists + #=============================================================================== + try { + $PrevPage = Page\Factory::build($Page->getPrevID()); + $page_data['PREV'] = generatePageItemData($PrevPage); + } catch(Page\Exception $Exception){} + + try { + $NextPage = Page\Factory::build($Page->getNextID()); + $page_data['NEXT'] = generatePageItemData($NextPage); + } catch(Page\Exception $Exception){} + + #=============================================================================== + # TRY: Template\Exception + #=============================================================================== + try { + $PageTemplate = Template\Factory::build('page/main'); + $PageTemplate->set('PAGE', $page_data); + $PageTemplate->set('USER', $user_data); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('HTML', $PageTemplate); + $MainTemplate->set('HEAD', [ + 'NAME' => $page_data['ATTR']['NAME'], + 'DESC' => cut(removeLineBreaksAndTabs(removeHTML($Page->getHTML()), ' '), Application::get('PAGE.DESCRIPTION_SIZE')), + 'PERM' => $page_data['URL'], + 'OG_IMAGES' => $page_data['FILE']['LIST'] + ]); + + echo $MainTemplate; + } + + #=============================================================================== + # CATCH: Template\Exception + #=============================================================================== + catch(Template\Exception $Exception) { + $Exception->defaultHandler(); + } +} + +#=============================================================================== +# CATCH: Page\Exception +#=============================================================================== +catch(Page\Exception $Exception) { + try { + if(Application::get('PAGE.SLUG_URLS') === FALSE) { + $Page = Page\Factory::buildBySlug(HTTP::GET('param')); + } else { + $Page = Page\Factory::build(HTTP::GET('param')); + } + + HTTP::redirect($Page->getURL()); + } + + catch(Page\Exception $Exception) { + Application::exit(404); + } +} + +#=============================================================================== +# CATCH: User\Exception +#=============================================================================== +catch(User\Exception $Exception) { + exit($Exception->getMessage()); +} \ No newline at end of file diff --git a/system/post/list.php b/system/post/list.php new file mode 100644 index 0000000..a7d6ce7 --- /dev/null +++ b/system/post/list.php @@ -0,0 +1,64 @@ +query(sprintf('SELECT COUNT(id) FROM %s', Post\Attribute::TABLE))->fetchColumn() / $site_size); + +$currentSite = HTTP::GET('site') ?? 1; +$currentSite = abs(intval($currentSite)); + +if($currentSite < 1 OR ($currentSite > $lastSite AND $lastSite > 0)) { + Application::exit(404); +} + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + $execSQL = "SELECT id FROM %s ORDER BY {$site_sort} LIMIT ".(($currentSite-1) * $site_size).", {$site_size}"; + $postIDs = $Database->query(sprintf($execSQL, Post\Attribute::TABLE))->fetchAll($Database::FETCH_COLUMN); + + foreach($postIDs as $postID) { + try { + $Post = Post\Factory::build($postID); + $User = User\Factory::build($Post->attr('user')); + + $ItemTemplate = generatePostItemTemplate($Post, $User); + + $posts[] = $ItemTemplate; + } + catch(Post\Exception $Exception){} + catch(User\Exception $Exception){} + } + + $ListTemplate = Template\Factory::build('post/list'); + $ListTemplate->set('PAGINATION', [ + 'THIS' => $currentSite, + 'LAST' => $lastSite, + 'HTML' => generatePostNaviTemplate($currentSite) + ]); + $ListTemplate->set('LIST', [ + 'POSTS' => $posts ?? [] + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('HTML', $ListTemplate); + $MainTemplate->set('HEAD', [ + 'NAME' => $Language->text('title_post_overview', $currentSite) + ]); + + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/system/post/main.php b/system/post/main.php new file mode 100644 index 0000000..4322f53 --- /dev/null +++ b/system/post/main.php @@ -0,0 +1,89 @@ +attr('user')); + + $post_data = generatePostItemData($Post); + $user_data = generateUserItemData($User); + + #=============================================================================== + # Add post data for previous and next post if exists + #=============================================================================== + try { + $PrevPost = Post\Factory::build($Post->getPrevID()); + $post_data['PREV'] = generatePostItemData($PrevPost); + } catch(Post\Exception $Exception){} + + try { + $NextPost = Post\Factory::build($Post->getNextID()); + $post_data['NEXT'] = generatePostItemData($NextPost); + } catch(Post\Exception $Exception){} + + #=============================================================================== + # TRY: Template\Exception + #=============================================================================== + try { + $PostTemplate = Template\Factory::build('post/main'); + $PostTemplate->set('POST', $post_data); + $PostTemplate->set('USER', $user_data); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('HTML', $PostTemplate); + $MainTemplate->set('HEAD', [ + 'NAME' => $post_data['ATTR']['NAME'], + 'DESC' => cut(removeLineBreaksAndTabs(removeHTML($post_data['BODY']['HTML']), ' '), Application::get('POST.DESCRIPTION_SIZE')), + 'PERM' => $post_data['URL'], + 'OG_IMAGES' => $post_data['FILE']['LIST'] + ]); + + echo $MainTemplate; + } + + #=============================================================================== + # CATCH: Template\Exception + #=============================================================================== + catch(Template\Exception $Exception) { + $Exception->defaultHandler(); + } +} + +#=============================================================================== +# CATCH: Post\Exception +#=============================================================================== +catch(Post\Exception $Exception) { + try { + if(Application::get('POST.SLUG_URLS') === FALSE) { + $Post = Post\Factory::buildBySlug(HTTP::GET('param')); + } else { + $Post = Post\Factory::build(HTTP::GET('param')); + } + + HTTP::redirect($Post->getURL()); + } + + catch(Post\Exception $Exception) { + Application::exit(404); + } +} + +#=============================================================================== +# CATCH: User\Exception +#=============================================================================== +catch(User\Exception $Exception) { + exit($Exception->getMessage()); +} \ No newline at end of file diff --git a/system/search/main.php b/system/search/main.php new file mode 100644 index 0000000..1c9921a --- /dev/null +++ b/system/search/main.php @@ -0,0 +1,89 @@ +query(sprintf('SELECT DISTINCT DAY(time_insert) AS temp FROM %s ORDER BY temp', Post\Attribute::TABLE)); +$M_LIST = $Database->query(sprintf('SELECT DISTINCT MONTH(time_insert) AS temp FROM %s ORDER BY temp', Post\Attribute::TABLE)); +$Y_LIST = $Database->query(sprintf('SELECT DISTINCT YEAR(time_insert) AS temp FROM %s ORDER BY temp', Post\Attribute::TABLE)); + +if($search = HTTP::GET('q')) { + if(!$postIDs = Post\Item::getSearchResultIDs($search, [HTTP::GET('d'), HTTP::GET('m'), HTTP::GET('y')], $Database)) { + $message = $Language->text('search_no_results', escapeHTML($search)); + } +} + +$form_data = [ + 'SELECT' => [ + 'D' => HTTP::GET('d'), + 'M' => HTTP::GET('m'), + 'Y' => HTTP::GET('y'), + ], + 'OPTIONS' => [ + 'D' => $D_LIST->fetchAll(PDO::FETCH_COLUMN), + 'M' => $M_LIST->fetchAll(PDO::FETCH_COLUMN), + 'Y' => $Y_LIST->fetchAll(PDO::FETCH_COLUMN), + ] +]; + +$search_data = [ + 'TEXT' => $search, + 'INFO' => isset($message) ? $message : FALSE, +]; + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + if(isset($postIDs) AND !empty($postIDs)) { + foreach($postIDs as $postID) { + try { + $Post = Post\Factory::build($postID); + $User = User\Factory::build($Post->attr('user')); + + $posts[] = generatePostItemTemplate($Post, $User); + } + catch(Post\Exception $Exception){} + catch(User\Exception $Exception){} + } + + $ResultTemplate = Template\Factory::build('search/result'); + $ResultTemplate->set('FORM', $form_data); + $ResultTemplate->set('SEARCH', $search_data); + $ResultTemplate->set('RESULT', [ + 'LIST' => $posts ?? [] + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('HTML', $ResultTemplate); + $MainTemplate->set('HEAD', [ + 'NAME' => $Language->text('title_search_results', escapeHTML($search)), + 'PERM' => Application::getURL('search/') + ]); + } + + else { + $SearchTemplate = Template\Factory::build('search/main'); + $SearchTemplate->set('FORM', $form_data); + $SearchTemplate->set('SEARCH', $search_data); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('HTML', $SearchTemplate); + $MainTemplate->set('HEAD', [ + 'NAME' => $Language->text('title_search_request'), + 'DESC' => 'Wenn du einen bestimmten Beitrag suchst, aber ihn nicht finden kannst, dann kann dir die Suchfunktion bestimmt weiterhelfen.', + 'PERM' => Application::getURL('search/') + ]); + } + + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/system/user/list.php b/system/user/list.php new file mode 100644 index 0000000..1f29473 --- /dev/null +++ b/system/user/list.php @@ -0,0 +1,60 @@ +query(sprintf('SELECT COUNT(id) FROM %s', User\Attribute::TABLE))->fetchColumn() / $site_size); + +$currentSite = HTTP::GET('site') ?? 1; +$currentSite = abs(intval($currentSite)); + +if($currentSite < 1 OR ($currentSite > $lastSite AND $lastSite > 0)) { + Application::exit(404); +} + +#=============================================================================== +# TRY: Template\Exception +#=============================================================================== +try { + $execSQL = "SELECT id FROM %s ORDER BY {$site_sort} LIMIT ".(($currentSite-1) * $site_size).", {$site_size}"; + $userIDs = $Database->query(sprintf($execSQL, User\Attribute::TABLE))->fetchAll($Database::FETCH_COLUMN); + + foreach($userIDs as $userID) { + try { + $User = User\Factory::build($userID); + $ItemTemplate = generateUserItemTemplate($User); + + $users[] = $ItemTemplate; + } catch(User\Exception $Exception){} + } + + $ListTemplate = Template\Factory::build('user/list'); + $ListTemplate->set('PAGINATION', [ + 'THIS' => $currentSite, + 'LAST' => $lastSite, + 'HTML' => generateUserNaviTemplate($currentSite) + ]); + $ListTemplate->set('LIST', [ + 'USERS' => $users ?? [] + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('HTML', $ListTemplate); + $MainTemplate->set('HEAD', [ + 'NAME' => $Language->text('title_user_overview', $currentSite) + ]); + + echo $MainTemplate; +} + +#=============================================================================== +# CATCH: Template\Exception +#=============================================================================== +catch(Template\Exception $Exception) { + $Exception->defaultHandler(); +} +?> \ No newline at end of file diff --git a/system/user/main.php b/system/user/main.php new file mode 100644 index 0000000..d236b94 --- /dev/null +++ b/system/user/main.php @@ -0,0 +1,97 @@ +getPrevID()); + $user_data['PREV'] = generateUserItemData($PrevUser); + } catch(User\Exception $Exception){} + + try { + $NextUser = User\Factory::build($User->getNextID()); + $user_data['NEXT'] = generateUserItemData($NextUser); + } catch(User\Exception $Exception){} + + #=============================================================================== + # TRY: Template\Exception + #=============================================================================== + try { + #=============================================================================== + # TRY: PDOException + #=============================================================================== + try { + $PostCountStatement = $Database->query(sprintf('SELECT COUNT(*) FROM %s WHERE user = %d', Post\Attribute::TABLE, $User->getID())); + $PageCountStatement = $Database->query(sprintf('SELECT COUNT(*) FROM %s WHERE user = %d', Page\Attribute::TABLE, $User->getID())); + } + + #=============================================================================== + # CATCH: PDOException + #=============================================================================== + catch(PDOException $Exception) { + exit($Exception->getMessage()); + } + + $UserTemplate = Template\Factory::build('user/main'); + $UserTemplate->set('USER', $user_data); + $UserTemplate->set('COUNT', [ + 'POST' => $PostCountStatement->fetchColumn(), + 'PAGE' => $PageCountStatement->fetchColumn() + ]); + + $MainTemplate = Template\Factory::build('main'); + $MainTemplate->set('HTML', $UserTemplate); + $MainTemplate->set('HEAD', [ + 'NAME' => $user_data['ATTR']['FULLNAME'], + 'DESC' => cut(removeLineBreaksAndTabs(removeHTML($User->getHTML()), ' '), Application::get('USER.DESCRIPTION_SIZE')), + 'PERM' => $User->getURL(), + 'OG_IMAGES' => $User->getFiles() + ]); + + echo $MainTemplate; + } + + #=============================================================================== + # CATCH: Template\Exception + #=============================================================================== + catch(Template\Exception $Exception) { + $Exception->defaultHandler(); + } +} + +#=============================================================================== +# CATCH: User\Exception +#=============================================================================== +catch(User\Exception $Exception) { + try { + if(Application::get('USER.SLUG_URLS') === FALSE) { + $User = User\Factory::buildBySlug(HTTP::GET('param')); + } else { + $User = User\Factory::build(HTTP::GET('param')); + } + + HTTP::redirect($User->getURL()); + } + + catch(User\Exception $Exception) { + Application::exit(404); + } +} \ No newline at end of file diff --git a/template/admin/html/403.php b/template/admin/html/403.php new file mode 100644 index 0000000..aebddb7 --- /dev/null +++ b/template/admin/html/403.php @@ -0,0 +1,2 @@ +

template('403_heading_text')?>

+

template('403_heading_desc')?>

diff --git a/template/admin/html/404.php b/template/admin/html/404.php new file mode 100644 index 0000000..4f841b6 --- /dev/null +++ b/template/admin/html/404.php @@ -0,0 +1,2 @@ +

template('404_heading_text')?>

+

template('404_heading_desc')?>

\ No newline at end of file diff --git a/template/admin/html/auth.php b/template/admin/html/auth.php new file mode 100644 index 0000000..a33be5d --- /dev/null +++ b/template/admin/html/auth.php @@ -0,0 +1,30 @@ +

template('authentication_text')?>

+

template('authentication_desc')?>

+ + + +
+ + + +
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
\ No newline at end of file diff --git a/template/admin/html/database.php b/template/admin/html/database.php new file mode 100644 index 0000000..92c5103 --- /dev/null +++ b/template/admin/html/database.php @@ -0,0 +1,26 @@ +

template('overview_database_text')?>

+

template('overview_database_desc')?>

+ + + +
+ + + +
+ + +
+ +
+ + +
+
+
+ + +
+ +
+
\ No newline at end of file diff --git a/template/admin/html/home.php b/template/admin/html/home.php new file mode 100644 index 0000000..6701f35 --- /dev/null +++ b/template/admin/html/home.php @@ -0,0 +1,34 @@ +

template('overview_dashboard_text')?>

+

template('overview_dashboard_desc')?>

+ +

template('last_post')?>

+

text('posts')?>: | text('post_overview')?> | text('insert')?>

+ +
    + +
+ +

template('home_no_posts')?>

+ + +

template('last_page')?>

+

text('pages')?>: | text('page_overview')?> | text('insert')?>

+ + +
    + +
+ +

template('home_no_pages')?>

+ + +

template('last_user')?>

+

text('users')?>: | text('user_overview')?> | text('insert')?>

+ + +
    + +
+ +

template('home_no_users')?>

+ diff --git a/template/admin/html/main.php b/template/admin/html/main.php new file mode 100644 index 0000000..d2fba38 --- /dev/null +++ b/template/admin/html/main.php @@ -0,0 +1,52 @@ + + + + + + + + + <?=escapeHTML($NAME)?> | Administration + + +
+
+
+ +
Administration
+
PHP7 blogging application by Nerdmind!
+
+
+
+ +
+
+
+
+ +
+ +
+ + \ No newline at end of file diff --git a/template/admin/html/page/delete.php b/template/admin/html/page/delete.php new file mode 100644 index 0000000..95451d8 --- /dev/null +++ b/template/admin/html/page/delete.php @@ -0,0 +1,4 @@ +

text('delete_page')?>

+

template('delete_page_desc')?>

+ + \ No newline at end of file diff --git a/template/admin/html/page/form.php b/template/admin/html/page/form.php new file mode 100644 index 0000000..cf87f82 --- /dev/null +++ b/template/admin/html/page/form.php @@ -0,0 +1,91 @@ + + +
+ + + +
+ + + +
+
+
+
+
id="L_ID" name="id" placeholder="[AUTO_INCREMENT]" value="" />
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
    + $data):?> +
  • + +
+
+
+
    +
  • +
  • +
  • + +
  • +
  • +
  • +
  • +
  • +
+
+ +
+ +
+ + +
+ + + + + + + +
+
\ No newline at end of file diff --git a/template/admin/html/page/index.php b/template/admin/html/page/index.php new file mode 100644 index 0000000..4d090ca --- /dev/null +++ b/template/admin/html/page/index.php @@ -0,0 +1,10 @@ +

text('page_overview')?>">text('insert')?>

+

template('overview_page_desc')?>

+ +
    + + + +
+ + \ No newline at end of file diff --git a/template/admin/html/page/insert.php b/template/admin/html/page/insert.php new file mode 100644 index 0000000..d45d79c --- /dev/null +++ b/template/admin/html/page/insert.php @@ -0,0 +1,4 @@ +

text('insert_page')?>

+

template('insert_page_desc')?>

+ + \ No newline at end of file diff --git a/template/admin/html/page/item.php b/template/admin/html/page/item.php new file mode 100644 index 0000000..e1e67b7 --- /dev/null +++ b/template/admin/html/page/item.php @@ -0,0 +1,16 @@ +
  • +
    +

    #

    + +
    +
    +

    +
    + +
  • \ No newline at end of file diff --git a/template/admin/html/page/update.php b/template/admin/html/page/update.php new file mode 100644 index 0000000..d15d784 --- /dev/null +++ b/template/admin/html/page/update.php @@ -0,0 +1,4 @@ +

    text('update_page')?>

    +

    template('update_page_desc')?>

    + + \ No newline at end of file diff --git a/template/admin/html/pagination.php b/template/admin/html/pagination.php new file mode 100644 index 0000000..31b5378 --- /dev/null +++ b/template/admin/html/pagination.php @@ -0,0 +1,45 @@ + + + \ No newline at end of file diff --git a/template/admin/html/post/delete.php b/template/admin/html/post/delete.php new file mode 100644 index 0000000..9be4566 --- /dev/null +++ b/template/admin/html/post/delete.php @@ -0,0 +1,4 @@ +

    text('delete_post')?>

    +

    template('delete_post_desc')?>

    + + \ No newline at end of file diff --git a/template/admin/html/post/form.php b/template/admin/html/post/form.php new file mode 100644 index 0000000..cba4aa8 --- /dev/null +++ b/template/admin/html/post/form.php @@ -0,0 +1,91 @@ + + +
    + + + +
    + + + +
    +
    +
    +
    +
    id="L_ID" name="id" placeholder="[AUTO_INCREMENT]" value="" />
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
      + $data):?> +
    • + +
    +
    +
    +
      +
    • +
    • +
    • + +
    • +
    • +
    • +
    • +
    • +
    +
    + +
    + +
    + + +
    + + + + + + + +
    +
    \ No newline at end of file diff --git a/template/admin/html/post/index.php b/template/admin/html/post/index.php new file mode 100644 index 0000000..481cafe --- /dev/null +++ b/template/admin/html/post/index.php @@ -0,0 +1,10 @@ +

    text('post_overview')?>">text('insert')?>

    +

    template('overview_post_desc')?>

    + +
      + + + +
    + + \ No newline at end of file diff --git a/template/admin/html/post/insert.php b/template/admin/html/post/insert.php new file mode 100644 index 0000000..df4b26d --- /dev/null +++ b/template/admin/html/post/insert.php @@ -0,0 +1,4 @@ +

    text('insert_post')?>

    +

    template('insert_post_desc')?>

    + + \ No newline at end of file diff --git a/template/admin/html/post/item.php b/template/admin/html/post/item.php new file mode 100644 index 0000000..1b942bd --- /dev/null +++ b/template/admin/html/post/item.php @@ -0,0 +1,16 @@ +
  • +
    +

    #

    + +
    +
    +

    +
    + +
  • \ No newline at end of file diff --git a/template/admin/html/post/update.php b/template/admin/html/post/update.php new file mode 100644 index 0000000..baa119e --- /dev/null +++ b/template/admin/html/post/update.php @@ -0,0 +1,4 @@ +

    text('update_post')?>

    +

    template('update_post_desc')?>

    + + \ No newline at end of file diff --git a/template/admin/html/user/delete.php b/template/admin/html/user/delete.php new file mode 100644 index 0000000..dac4185 --- /dev/null +++ b/template/admin/html/user/delete.php @@ -0,0 +1,6 @@ +

    text('delete_user')?>

    +

    template('delete_user_desc')?>

    + +

    template('delete_user_warning')?>

    + + \ No newline at end of file diff --git a/template/admin/html/user/form.php b/template/admin/html/user/form.php new file mode 100644 index 0000000..c284187 --- /dev/null +++ b/template/admin/html/user/form.php @@ -0,0 +1,97 @@ + + +
    + + + +
    + + + +
    +
    +
    +
    +
    id="L_ID" name="id" placeholder="[AUTO_INCREMENT]" value="" />
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
      + $data):?> +
    • + +
    +
    +
    +
      +
    • +
    • +
    • + +
    • +
    • +
    • +
    • +
    • +
    +
    + +
    + +
    + + +
    + + + + + + + +
    +
    \ No newline at end of file diff --git a/template/admin/html/user/index.php b/template/admin/html/user/index.php new file mode 100644 index 0000000..6b23536 --- /dev/null +++ b/template/admin/html/user/index.php @@ -0,0 +1,10 @@ +

    text('user_overview')?>">text('insert')?>

    +

    template('overview_user_desc')?>

    + +
      + + + +
    + + \ No newline at end of file diff --git a/template/admin/html/user/insert.php b/template/admin/html/user/insert.php new file mode 100644 index 0000000..54598ee --- /dev/null +++ b/template/admin/html/user/insert.php @@ -0,0 +1,4 @@ +

    text('insert_user')?>

    +

    template('insert_user_desc')?>

    + + \ No newline at end of file diff --git a/template/admin/html/user/item.php b/template/admin/html/user/item.php new file mode 100644 index 0000000..df8e5b1 --- /dev/null +++ b/template/admin/html/user/item.php @@ -0,0 +1,15 @@ +
  • +
    +

    #

    +
    +
    +

    +
    + +
  • \ No newline at end of file diff --git a/template/admin/html/user/update.php b/template/admin/html/user/update.php new file mode 100644 index 0000000..6f7ddae --- /dev/null +++ b/template/admin/html/user/update.php @@ -0,0 +1,4 @@ +

    text('update_user')?>

    +

    template('update_user_desc')?>

    + + \ No newline at end of file diff --git a/template/admin/lang/de.php b/template/admin/lang/de.php new file mode 100644 index 0000000..d9966ae --- /dev/null +++ b/template/admin/lang/de.php @@ -0,0 +1,118 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# This file contains template internationalization strings for the DE language # +# and is completely independend from the core internationalization strings. # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +#=============================================================================== +# Item last text +#=============================================================================== +$LANGUAGE['last_post'] = 'Letzter Post'; +$LANGUAGE['last_page'] = 'Letzte Seite'; +$LANGUAGE['last_user'] = 'Letzter Benutzer'; + +#=============================================================================== +# Insert item description +#=============================================================================== +$LANGUAGE['insert_page_desc'] = 'Hier kannst du eine neue Seite erstellen und veröffentlichen.'; +$LANGUAGE['insert_post_desc'] = 'Hier kannst du einen neuen Beitrag erstellen und veröffentlichen.'; +$LANGUAGE['insert_user_desc'] = 'Hier kannst du einen neuen Benutzer erstellen und veröffentlichen.'; + +#=============================================================================== +# Update item description +#=============================================================================== +$LANGUAGE['update_page_desc'] = 'Hier kannst du eine vorhandene Seite bearbeiten und die Änderungen abspeichern.'; +$LANGUAGE['update_post_desc'] = 'Hier kannst du einen vorhandenen Beitrag bearbeiten und die Änderungen abspeichern.'; +$LANGUAGE['update_user_desc'] = 'Hier kannst du deinen vorhandenen Benutzer bearbeiten und die Änderungen abspeichern.'; + +#=============================================================================== +# Delete item description +#=============================================================================== +$LANGUAGE['delete_page_desc'] = 'Falls du diese Seite nicht mehr benötigst kannst du sie über den folgenden Button permanent löschen.'; +$LANGUAGE['delete_post_desc'] = 'Falls du diesen Beitrag nicht mehr benötigst kannst du ihn über den folgenden Button permanent löschen.'; +$LANGUAGE['delete_user_desc'] = 'Falls du diesen Benutzer nicht mehr benötigst kannst du ihn über den folgenden Button permanent löschen.'; + +#=============================================================================== +# Item overview description +#=============================================================================== +$LANGUAGE['overview_page_desc'] = 'Hier siehst du alle vorhandenen Seiten.'; +$LANGUAGE['overview_post_desc'] = 'Hier siehst du alle vorhandenen Beiträge.'; +$LANGUAGE['overview_user_desc'] = 'Hier siehst du alle vorhandenen Benutzer.'; + +#=============================================================================== +# Dashboard overview text +#=============================================================================== +$LANGUAGE['overview_dashboard_text'] = 'Dashboard'; + +#=============================================================================== +# Dashboard overview text +#=============================================================================== +$LANGUAGE['overview_dashboard_desc'] = 'Willkommen im Administrationsbereich. Hier kannst du deine Inhalte verwalten.'; + +#=============================================================================== +# Database overview text +#=============================================================================== +$LANGUAGE['overview_database_text'] = 'Datenbank'; + +#=============================================================================== +# Database overview text +#=============================================================================== +$LANGUAGE['overview_database_desc'] = 'Datenbankoperationen mit SQL-Befehlen durchführen.'; + +#=============================================================================== +# Authentication +#=============================================================================== +$LANGUAGE['authentication_text'] = 'Authentifizierung'; +$LANGUAGE['authentication_desc'] = 'Um deine Inhalte zu verwalten musst du dich zuerst authentifizieren.'; + +#=============================================================================== +# No items exists +#=============================================================================== +$LANGUAGE['home_no_pages'] = 'Es gibt keine letzte Seite zum anzeigen hier. Du musst zuerst eine erstellen.'; +$LANGUAGE['home_no_posts'] = 'Es gibt keinen letzten Beitrag zum anzeigen hier. Du musst zuerst einen erstellen.'; +$LANGUAGE['home_no_users'] = 'Es gibt keinen letzten Benutzer zum anzeigen hier. Du musst zuerst einen erstellen.'; + +#=============================================================================== +# Delete user warning +#=============================================================================== +$LANGUAGE['delete_user_warning'] = 'WARNUNG: Wenn du diesen Benutzer löschst werden alle ihm zugehörigen Beiträge und Seiten ebenfalls gelöscht!'; + +#=============================================================================== +# Database warning +#=============================================================================== +$LANGUAGE['database_warning'] = 'Manche Befehle können gefährliche Auswirkungen haben, wenn du nicht weißt, was du tust!'; + +#=============================================================================== +# Error 403 +#=============================================================================== +$LANGUAGE['403_heading_text'] = 'Zugriff verweigert'; +$LANGUAGE['403_heading_desc'] = 'Der Zugriff auf diese Ressource des Servers wurde dir verweigert, da du die dafür notwendigen Berechtigungen nicht besitzt.'; + +#=============================================================================== +# Error 404 +#=============================================================================== +$LANGUAGE['404_heading_text'] = 'Nicht gefunden'; +$LANGUAGE['404_heading_desc'] = 'Die angeforderte Ressource konnte auf diesem Server nicht gefunden werden.'; + +#=============================================================================== +# "Are you sure?" question +#=============================================================================== +$LANGUAGE['sure'] = 'Bist du sicher?'; + +#=============================================================================== +# Labels +#=============================================================================== +$LANGUAGE['LABEL_SLUG'] = 'Slug'; +$LANGUAGE['LABEL_USER'] = 'Benutzer'; +$LANGUAGE['LABEL_NAME'] = 'Titel'; +$LANGUAGE['LABEL_INSERT'] = 'Erstellt'; +$LANGUAGE['LABEL_UPDATE'] = 'Bearbeitet'; +$LANGUAGE['LABEL_FULLNAME'] = 'Name'; +$LANGUAGE['LABEL_MAILADDR'] = 'E-Mail'; +$LANGUAGE['LABEL_USERNAME'] = 'Username'; +$LANGUAGE['LABEL_PASSWORD'] = 'Passwort'; +?> \ No newline at end of file diff --git a/template/admin/lang/en.php b/template/admin/lang/en.php new file mode 100644 index 0000000..d98ea44 --- /dev/null +++ b/template/admin/lang/en.php @@ -0,0 +1,118 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# This file contains template internationalization strings for the EN language # +# and is completely independend from the core internationalization strings. # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +#=============================================================================== +# Item last text +#=============================================================================== +$LANGUAGE['last_post'] = 'Last post'; +$LANGUAGE['last_page'] = 'Last page'; +$LANGUAGE['last_user'] = 'Last user'; + +#=============================================================================== +# Insert item description +#=============================================================================== +$LANGUAGE['insert_page_desc'] = 'Here you can create and publish a new page.'; +$LANGUAGE['insert_post_desc'] = 'Here you can create and publish a new post.'; +$LANGUAGE['insert_user_desc'] = 'Here you can create and publish a new user.'; + +#=============================================================================== +# Update item description +#=============================================================================== +$LANGUAGE['update_page_desc'] = 'Here you can edit an existing page and save the changes.'; +$LANGUAGE['update_post_desc'] = 'Here you can edit an existing post and save the changes.'; +$LANGUAGE['update_user_desc'] = 'Here you can edit an existing user and save the changes.'; + +#=============================================================================== +# Delete item description +#=============================================================================== +$LANGUAGE['delete_page_desc'] = 'If you do not need this page anymore, you can permanently delete it by clicking the following button.'; +$LANGUAGE['delete_post_desc'] = 'If you do not need this post anymore, you can permanently delete it by clicking the following button.'; +$LANGUAGE['delete_user_desc'] = 'If you do not need this user anymore, you can permanently delete it by clicking the following button.'; + +#=============================================================================== +# Item overview description +#=============================================================================== +$LANGUAGE['overview_page_desc'] = 'Here you can see all existing pages.'; +$LANGUAGE['overview_post_desc'] = 'Here you can see all existing posts.'; +$LANGUAGE['overview_user_desc'] = 'Here you can see all existing users.'; + +#=============================================================================== +# Dashboard overview text +#=============================================================================== +$LANGUAGE['overview_dashboard_text'] = 'Dashboard'; + +#=============================================================================== +# Dashboard overview text +#=============================================================================== +$LANGUAGE['overview_dashboard_desc'] = 'Welcome to the administration area. Here you can manage your content.'; + +#=============================================================================== +# Database overview text +#=============================================================================== +$LANGUAGE['overview_database_text'] = 'Database'; + +#=============================================================================== +# Database overview text +#=============================================================================== +$LANGUAGE['overview_database_desc'] = 'Perform database operations with SQL commands.'; + +#=============================================================================== +# Authentication +#=============================================================================== +$LANGUAGE['authentication_text'] = 'Authentication'; +$LANGUAGE['authentication_desc'] = 'To manage your content, you have to authenticate yourself first.'; + +#=============================================================================== +# No items exists +#=============================================================================== +$LANGUAGE['home_no_pages'] = 'There is no last page to display here. You have to insert a new page first.'; +$LANGUAGE['home_no_posts'] = 'There is no last post to display here. You have to insert a new post first.'; +$LANGUAGE['home_no_users'] = 'There is no last user to display here. You have to insert a new user first.'; + +#=============================================================================== +# Delete user warning +#=============================================================================== +$LANGUAGE['delete_user_warning'] = 'WARNING: If you delete this user, all posts and pages belonging to this user will also be deleted!'; + +#=============================================================================== +# Database warning +#=============================================================================== +$LANGUAGE['database_warning'] = 'Some commands can have dangerous effects if you do not know what you are doing!'; + +#=============================================================================== +# Error 403 +#=============================================================================== +$LANGUAGE['403_heading_text'] = 'Access denied'; +$LANGUAGE['403_heading_desc'] = 'You are denied to access this resource because you do not have the necessary permissions.'; + +#=============================================================================== +# Error 404 +#=============================================================================== +$LANGUAGE['404_heading_text'] = 'Not found'; +$LANGUAGE['404_heading_desc'] = 'The requested resource could not be found on this server.'; + +#=============================================================================== +# "Are you sure?" question +#=============================================================================== +$LANGUAGE['sure'] = 'Are you sure?'; + +#=============================================================================== +# Labels +#=============================================================================== +$LANGUAGE['LABEL_SLUG'] = 'Slug'; +$LANGUAGE['LABEL_USER'] = 'User'; +$LANGUAGE['LABEL_NAME'] = 'Title'; +$LANGUAGE['LABEL_INSERT'] = 'Created'; +$LANGUAGE['LABEL_UPDATE'] = 'Updated'; +$LANGUAGE['LABEL_FULLNAME'] = 'Name'; +$LANGUAGE['LABEL_MAILADDR'] = 'Email'; +$LANGUAGE['LABEL_USERNAME'] = 'Username'; +$LANGUAGE['LABEL_PASSWORD'] = 'Password'; +?> \ No newline at end of file diff --git a/template/admin/rsrc/background.png b/template/admin/rsrc/background.png new file mode 100644 index 0000000..f018e81 Binary files /dev/null and b/template/admin/rsrc/background.png differ diff --git a/template/admin/rsrc/font/font-awesome.woff2 b/template/admin/rsrc/font/font-awesome.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/template/admin/rsrc/font/font-awesome.woff2 differ diff --git a/template/admin/rsrc/font/kadwa-n-400.woff2 b/template/admin/rsrc/font/kadwa-n-400.woff2 new file mode 100644 index 0000000..1566426 Binary files /dev/null and b/template/admin/rsrc/font/kadwa-n-400.woff2 differ diff --git a/template/admin/rsrc/font/ruda-n-400.woff2 b/template/admin/rsrc/font/ruda-n-400.woff2 new file mode 100644 index 0000000..6435a0d Binary files /dev/null and b/template/admin/rsrc/font/ruda-n-400.woff2 differ diff --git a/template/admin/rsrc/font/ruda-n-700.woff2 b/template/admin/rsrc/font/ruda-n-700.woff2 new file mode 100644 index 0000000..0066431 Binary files /dev/null and b/template/admin/rsrc/font/ruda-n-700.woff2 differ diff --git a/template/admin/rsrc/icon-public-domain.svg b/template/admin/rsrc/icon-public-domain.svg new file mode 100644 index 0000000..e0f0b0f --- /dev/null +++ b/template/admin/rsrc/icon-public-domain.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/admin/rsrc/main.css b/template/admin/rsrc/main.css new file mode 100644 index 0000000..2466b31 --- /dev/null +++ b/template/admin/rsrc/main.css @@ -0,0 +1,275 @@ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Selection +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +::-moz-selection{background:#BBB;color:#000;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Hyperlinks +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +a{color:#0060A0;text-decoration:none;}a:focus{background:#CCC;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Paragraphs +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:0;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Icons +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +h1 .fa, h2 .fa, h3 .fa, h4 .fa, h5 .fa, h6 .fa{margin-right:0.25rem;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Headings +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +h1 > a{float:right;font-size:0.7rem;font-weight:normal;} +h1,h2,h3,h4,h5,h6{margin:0;text-transform:uppercase;} +h1{font-size:0.80rem;}h2{font-size:0.70rem;} +h3{font-size:0.65rem;}h4{font-size:0.60rem;} +h5{font-size:0.55rem;}h6{font-size:0.50rem;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Document +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +html,body{margin:0;padding:0;} +main{padding:1rem;} +html{font-size:1.25rem;color:#333;background:url(background.png) fixed center #CCC;-webkit-hyphens:auto;hyphens:auto;} +body{font-family:Ruda,sans-serif;font-size:0.7rem;line-height:1.2rem;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Main content +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#main-content{background:#FFF;border:0.05rem solid #AAA;border-top:none;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Width +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#main-content,.header-content{max-width:50rem;margin:0 auto;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Header +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#main-header{font-size:0.6rem;} +#header-text,#header-desc{text-shadow: 0 -1px #4E718F, 1px 0 #4E718F, 0 1px #4E718F, -1px 0 #4E718F;} +#header-text{font-size:0.8rem;font-weight:700;text-transform:uppercase;} +#header-desc{font-size:0.6rem;font-weight:700;line-height:1rem;} +#header-desc span{color:#CCC;} +#header-logo{display:block;max-height:5rem;float:left;margin-right:0.5rem;} +.header-line{padding:0.5rem 1rem;overflow:hidden;} +.header-line:first-child{background:#5E819F;} +.header-line:last-child{background:#EEE;border:0.05rem solid #AAA;border-left:none;border-right:none;padding:0.25rem 1rem;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Main Navigation +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#main-navi ul{list-style:none;margin:0;padding:0;float:left;} +#main-navi li{display:inline;} +#main-navi ul+ul{float:right;} +#main-navi li .fa{margin-right:0.25rem;} +#main-navi a{padding:0.1rem 0.3rem;background:#DDD;border:0.05rem solid #AAA;color:inherit;text-decoration:none;text-align:center;display:inline-block;} +#main-navi a:hover, #main-navi a:focus{text-decoration:none;background:#CCC;} +#main-navi a:focus{background:#CCC;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Footer +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#main-footer{font-size:0.6rem;background:#EEE;border-top:0.05rem solid #AAA;padding:0.75rem;text-align:center;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Content containers +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +.content{border:0.05rem solid #AAA;margin-bottom:1rem;clear:both;} +.content{background:#FFF;border:0.05rem solid #AAA;} + +.content > header{padding:0.5rem 1rem;} + +.content > header, +.content > footer{background:#EEE;overflow:hidden;} + +.content > article{padding:1rem;} +.content > header{border-bottom:0.05rem solid #AAA;} +.content > footer{border-top:0.05rem solid #AAA;} +.content > footer > ul{margin:0;padding:0;list-style:none;} +.content > footer > ul > li{display:inline-block;float:left;} +.content > footer > ul > li:last-child{float:right;} +.content > footer > ul > li > a{color:inherit;display:inline-block;padding:0.25rem 2rem;} +.content > footer > ul > li > a:hover, +.content > footer > ul > li > a:active{background:#DDD;} + +.content h2 > span{float:right;} + +.item-list{margin:0;padding:0;list-style:none;} +.item-list > li{display:block;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Site Navigation +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#site-navi{clear:both;display:flex;box-sizing:border-box;justify-content:space-between;} +#site-navi > div{display:flex;align-items:center;border:0.05rem solid #AAA;background:#EEE;} +#site-navi > div > a{display:block;} +#site-navi > section{display:flex;overflow:hidden;align-items:center;} +#site-navi > section > div{border:0.05rem solid #AAA;background:#EEE;} + +#site-navi .disabled{pointer-events:none;color:#AAA;} +#site-navi .active a{background:#CCC !important;font-weight:600;pointer-events: none;} + +#site-navi ol{list-style:none;margin:0;padding:0;} +#site-navi li{float:left;display:inline-block;} +#site-navi li+li{border-left:0.05rem solid #AAA;} +#site-navi a{padding:0 0.5rem;text-decoration:none;color:inherit;display:inline-block;} +#site-navi a:hover,#site-navi a:focus{background:#CCC;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Elements +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +pre{margin-bottom:1rem;overflow:auto;-moz-tab-size:4;tab-size:4;} +code,pre{font-family:monospace;color:#B03060;} +p{margin-top:0;} +img{border:none;max-width:100%;} +.red{color:#B03060;} +.blue{color:#40779A;} +.green{color:#008B45;} +.right{float:right;} +#database-result{color:inherit;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Table elements +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +table{width:100%;margin:0 0 1rem;}td{vertical-align:middle;} +table,td{border-spacing:0;border-collapse:collapse;padding:0.5rem;border:0.05rem solid #000;} +thead,tr:nth-child(even){background:#EEE;} +thead > tr, th{font-weight:700;font-style:italic;} +thead > tr > td, th > td{text-align:center;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Brackets +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +.brackets a,a.brackets{text-decoration:none;} +.brackets:after{content:"]"} +.brackets:before{content:"["} +a.brackets:before,a.brackets:after{color:#222;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Form flex-box +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +form{border:0.05rem solid #AAA;} + +.flex{display:flex;width:100%;justify-content:center;overflow:hidden;box-sizing:border-box;} +.flex + section{border-top:0.05rem solid #AAA;} +.flex > section {display:flex;box-sizing:padding-box;width:100%;} +.flex > section > div{display:flex;align-items:center;padding:0.5rem;box-sizing:border-box;} +.flex > section > div + div{border-left:0.05rem solid #AAA;} +.flex.flex-responsive > section{width:50%;} + +.form-icon-flex{background:#DDD;width:10%;justify-content:center;} +.form-label-flex{background:#DDD;width:30%;border-left:none !important;} +.form-field-flex{width:60%;} + +.fa + label{margin-left:0.25rem;} +.background{background:#DDD;} +.flex-padding{padding:0.5rem;} +.flex-direction-column{flex-direction:column;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Form buttons +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +.button-list {margin:0;padding:0;list-style:none;} +.button-list > li{display:inline-block;background:#EEE;padding:0.5rem;border:0.05rem solid #AAA;cursor:pointer;} +.button-list.emoticons > li {padding:0.25rem 0.5rem;} +.button-list > li:hover, +.button-list > li:active{background:#CCC;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Form elements +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +input,select,textarea{width:100%;box-sizing:padding-box;background:#EEE;color:#404040;font-family:inherit;font-size:0.7rem;padding:0.25rem;border:0.05rem solid #AAA;} +textarea{font-family:Kadwa,sans-serif;box-sizing:border-box;display:inline-block;resize:vertical;min-height:15rem;line-height:1.4rem;padding:0.75rem;} +input[type="submit"]{text-transform:uppercase;} +input:disabled{background:#DDD;color:#888;} +input:disabled:hover{cursor:not-allowed;} +label{text-transform:uppercase;font-weight:normal;} +label:after{content:":";} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* FontAwesome Main +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +.fa{display:inline-block;font:normal normal normal 14px/1 "FontAwesome";font-size:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* FontAwesome Icons +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +.fa-rss:before{content:"\f09e"} +.fa-bug:before{content:"\f188"} +.fa-key:before{content:"\f084"} +.fa-link:before{content:"\f0c1"} +.fa-bold:before{content:"\f032"} +.fa-code:before{content:"\f121"} +.fa-bars:before{content:"\f0c9"} +.fa-user:before{content:"\f007"} +.fa-home:before{content:"\f015"} +.fa-plus:before{content:"\f067"} +.fa-italic:before{content:"\f033"} +.fa-header:before{content:"\f1dc"} +.fa-search:before{content:"\f002"} +.fa-trash-o:before{content:"\f014"} +.fa-sign-in:before{content:"\f090"} +.fa-clock-o:before{content:"\f017"} +.fa-list-ul:before{content:"\f0ca"} +.fa-list-ol:before{content:"\f0cb"} +.fa-smile-o:before{content:"\f118"} +.fa-database:before{content:"\f1c0"} +.fa-sign-out:before{content:"\f08b"} +.fa-dashboard:before{content:"\f0e4"} +.fa-picture-o:before{content:"\f03e"} +.fa-envelope-o:before{content:"\f003"} +.fa-eyedropper:before{content:"\f1fb"} +.fa-rss-square:before{content:"\f143"} +.fa-arrow-left:before{content:"\f060"} +.fa-quote-right:before{content:"\f10e"} +.fa-user-secret:before{content:"\f21b"} +.fa-file-text-o:before{content:"\f0f6"} +.fa-newspaper-o:before{content:"\f1ea"} +.fa-arrow-right:before{content:"\f061"} +.fa-external-link:before{content:"\f08e"} +.fa-pencil-square-o:before{content:"\f044"} +.fa-question-circle:before{content:"\f059"} +.fa-pencil-square-o:before{content:"\f044"} +.fa-exclamation-triangle:before{content:"\f071"} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Responsive Level #1 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +@media only screen and (max-width:50rem) { + html{font-size:1.125rem;/*18px*/background-image:none !important;} +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Responsive Level #2 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +@media only screen and (max-width:37.5rem) { + .flex.flex-responsive{display:block;width:auto;} + .flex.flex-responsive > section {width:100%;} + .flex.flex-responsive > section + section{border-top:0.05rem solid #AAA;} + + #main-navi{font-size:1rem;} + #main-navi li span{display:none;} + #main-navi li .fa{margin-right:0;} + #main-navi a{padding:0.5rem;} + + .flex-emoticons{display:none !important;} +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Font "Font Awesome" [4.7.0]: SIL Open Font License (OFL) +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +@font-face{font-family:"FontAwesome";font-weight:400;src:url("font/font-awesome.woff2") format("woff2");} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Font "Kadwa": SIL Open Font License (OFL) +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +@font-face{font-family:Kadwa;font-weight:400;src:url("font/kadwa-n-400.woff2") format("woff2");} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Font "Ruda": SIL Open Font License (OFL) +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +@font-face{font-family:Ruda;font-weight:400;src:url("font/ruda-n-400.woff2") format("woff2");} +@font-face{font-family:Ruda;font-weight:700;src:url("font/ruda-n-700.woff2") format("woff2");} \ No newline at end of file diff --git a/template/admin/rsrc/main.js b/template/admin/rsrc/main.js new file mode 100644 index 0000000..3d5546f --- /dev/null +++ b/template/admin/rsrc/main.js @@ -0,0 +1,94 @@ +//============================================================================== +// Markdown tags to replace +//============================================================================== +var markdownTags = { + "bold": ["**", "**"], + "italic": ["*", "*"], + "header": ["## ", "\n"], + "link": ["[", "](href)"], + "image": ["![", "](href)"], + "code": ["\n~~~\n", "\n~~~\n"], + "quote": ["\n> ", ""], + "list_ul": ["* ", ""], + "list_ol": ["1. ", ""] +}; + +//============================================================================== +// Set caret position in editor +//============================================================================== +function setCaretPosition(position) { + window.setTimeout(function() { + document.getElementById("content-editor").focus(); + document.getElementById("content-editor").setSelectionRange(position, position); + }, 50); + +} + +//============================================================================== +// Insert markdown around text in editor +//============================================================================== +function markdownReplace(tagname) { + var element = document.activeElement; + + if(element.nodeName === 'TEXTAREA') { + var selectionStart = element.selectionStart; + var selectionEnd = element.selectionEnd; + + var selectedText = element.value.substring(selectionStart, selectionEnd); + + var content = element.value; + element.value = content.slice(0, selectionStart) + markdownTags[tagname][0] + selectedText + markdownTags[tagname][1] + content.slice(selectionEnd); + + setCaretPosition(selectionStart + markdownTags[tagname][0].length + selectedText.length + markdownTags[tagname][1].length); + } +} + +//============================================================================== +// Insert emoticon after cursor in editor +//============================================================================== +function emoticonReplace(emoticon) { + var element = document.activeElement; + + if(element.nodeName === 'TEXTAREA') { + var selectionStart = element.selectionStart; + var selectionEnd = element.selectionEnd; + + var content = element.value; + element.value = content.slice(0, selectionStart) + emoticon + content.slice(selectionEnd); + + setCaretPosition(selectionStart + emoticon.length); + } +} + +//============================================================================== +// Keep server-side session active if the user is writing a long text +//============================================================================== +addEventListener("DOMContentLoaded", function() { + setInterval(function() { + var Request = new XMLHttpRequest(); + Request.open("HEAD", "", true); + Request.send(); + }, 300000); +}, false); + +//============================================================================== +// Insert tab indent into editor if is pressed +//============================================================================== +addEventListener("DOMContentLoaded", function() { + if(document.getElementById("content-editor")) { + var element = document.getElementById("content-editor"); + element.addEventListener('keydown', function(e) { + if(e.keyCode === 9 && !e.ctrlKey && !e.shiftKey) { + var selectionStart = element.selectionStart; + var selectionEnd = element.selectionEnd; + + var content = element.value; + + element.value = content.substring(0, selectionStart) + "\t" + content.substring(selectionEnd); + + setCaretPosition(selectionStart + 1); + e.preventDefault(); + } + }, false); + } +}, false); \ No newline at end of file diff --git a/template/standard/html/403.php b/template/standard/html/403.php new file mode 100644 index 0000000..5039678 --- /dev/null +++ b/template/standard/html/403.php @@ -0,0 +1,11 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> +

    template('403_heading_text')?>

    +

    template('403_heading_desc')?>

    \ No newline at end of file diff --git a/template/standard/html/404.php b/template/standard/html/404.php new file mode 100644 index 0000000..acb7ab9 --- /dev/null +++ b/template/standard/html/404.php @@ -0,0 +1,11 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> +

    template('404_heading_text')?>

    +

    template('404_heading_desc')?>

    \ No newline at end of file diff --git a/template/standard/html/feed/item_page.php b/template/standard/html/feed/item_page.php new file mode 100644 index 0000000..9defc25 --- /dev/null +++ b/template/standard/html/feed/item_page.php @@ -0,0 +1,25 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> + + <?=escapeHTML($PAGE['ATTR']['NAME'])?> + + + + + + + + ]]> + + + + + \ No newline at end of file diff --git a/template/standard/html/feed/item_post.php b/template/standard/html/feed/item_post.php new file mode 100644 index 0000000..f004316 --- /dev/null +++ b/template/standard/html/feed/item_post.php @@ -0,0 +1,26 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> + + <?=escapeHTML($POST['ATTR']['NAME'])?> + + + + + + + +

    Kommentare: [0x33EB32A2] blog@nerdmind.de

    + ]]> +
    + + + +
    \ No newline at end of file diff --git a/template/standard/html/feed/main.php b/template/standard/html/feed/main.php new file mode 100644 index 0000000..174a841 --- /dev/null +++ b/template/standard/html/feed/main.php @@ -0,0 +1,50 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +switch($FEED['TYPE']) { + case 'post': + $title = escapeHTML($BLOGMETA['NAME']).' ['.$Language->template('feed_only_posts').']'; + $self = Application::getURL('feed/post/'); + break; + case 'page': + $title = escapeHTML($BLOGMETA['NAME']).' ['.$Language->template('feed_only_pages').']'; + $self = Application::getURL('feed/page/'); + break; + default: + $title = escapeHTML($BLOGMETA['NAME']); + $self = Application::getURL('feed/'); +} +?> +'?> + + + <?=$title?> + + + + + + + + <?=escapeHTML($BLOGMETA['NAME'])?> + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/template/standard/html/home.php b/template/standard/html/home.php new file mode 100644 index 0000000..42fc169 --- /dev/null +++ b/template/standard/html/home.php @@ -0,0 +1,19 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> +

    template('home_heading_text', escapeHTML(Application::get('BLOGMETA.NAME')))?>Feed

    +

    template('home_heading_desc', Application::get('POST.LIST_SIZE'))?>

    + +
      + + + +
    + + \ No newline at end of file diff --git a/template/standard/html/main.php b/template/standard/html/main.php new file mode 100644 index 0000000..19d3b29 --- /dev/null +++ b/template/standard/html/main.php @@ -0,0 +1,92 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <?=escapeHTML("{$HEAD['NAME']} | {$BLOGMETA['NAME']} {$BLOGMETA['DESC']}")?> + + +
    +
    +
    + "> + + +
    + +
    +
    + +
    +
    + © +
    +
    + + \ No newline at end of file diff --git a/template/standard/html/page/item.php b/template/standard/html/page/item.php new file mode 100644 index 0000000..29ab9cf --- /dev/null +++ b/template/standard/html/page/item.php @@ -0,0 +1,20 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> +
  • +
    +

    + + +

    +
    +
    +

    +
    +
  • \ No newline at end of file diff --git a/template/standard/html/page/list.php b/template/standard/html/page/list.php new file mode 100644 index 0000000..b6a112d --- /dev/null +++ b/template/standard/html/page/list.php @@ -0,0 +1,19 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> +

    text('page_overview')?>Feed

    +

    template('page_base_heading_desc', $PAGINATION['THIS'])?>

    + +
      + + + +
    + + \ No newline at end of file diff --git a/template/standard/html/page/main.php b/template/standard/html/page/main.php new file mode 100644 index 0000000..b5e6a6d --- /dev/null +++ b/template/standard/html/page/main.php @@ -0,0 +1,46 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +$user = "{$USER['ATTR']['FULLNAME']}"; +$time = ""; +?> +

    template('page_main_heading_text', $PAGE['ATTR']['NAME']))?>

    +

    template('page_main_heading_desc', [$user, $time])?>

    + +
    + +
    + + + + \ No newline at end of file diff --git a/template/standard/html/pagination.php b/template/standard/html/pagination.php new file mode 100644 index 0000000..221530e --- /dev/null +++ b/template/standard/html/pagination.php @@ -0,0 +1,54 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> + + + \ No newline at end of file diff --git a/template/standard/html/post/item.php b/template/standard/html/post/item.php new file mode 100644 index 0000000..ab00624 --- /dev/null +++ b/template/standard/html/post/item.php @@ -0,0 +1,20 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> +
  • +
    +

    + + +

    +
    +
    + +
    +
  • \ No newline at end of file diff --git a/template/standard/html/post/list.php b/template/standard/html/post/list.php new file mode 100644 index 0000000..457c1df --- /dev/null +++ b/template/standard/html/post/list.php @@ -0,0 +1,19 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> +

    text('post_overview')?>Feed

    +

    template('post_base_heading_desc', $PAGINATION['THIS'])?>

    + +
      + + + +
    + + \ No newline at end of file diff --git a/template/standard/html/post/main.php b/template/standard/html/post/main.php new file mode 100644 index 0000000..31d7102 --- /dev/null +++ b/template/standard/html/post/main.php @@ -0,0 +1,46 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +$user = "{$USER['ATTR']['FULLNAME']}"; +$time = ""; +?> +

    template('post_main_heading_text', $POST['ATTR']['NAME']))?>

    +

    template('post_main_heading_desc', [$user, $time])?>

    + +
    + +
    + + + + \ No newline at end of file diff --git a/template/standard/html/search/main.php b/template/standard/html/search/main.php new file mode 100644 index 0000000..b749517 --- /dev/null +++ b/template/standard/html/search/main.php @@ -0,0 +1,34 @@ +

    template('search_base_heading_text')?>

    +

    template('search_base_heading_desc')?>

    + + +
    + + +
    + + + + + + + +
    \ No newline at end of file diff --git a/template/standard/html/search/result.php b/template/standard/html/search/result.php new file mode 100644 index 0000000..e79eb98 --- /dev/null +++ b/template/standard/html/search/result.php @@ -0,0 +1,36 @@ +

    template('search_result_heading_text', escapeHTML($SEARCH['TEXT']))?>

    +

    template('search_result_heading_desc')?>

    + +
    + + + + + + + +
    + +
      + + + +
    \ No newline at end of file diff --git a/template/standard/html/user/item.php b/template/standard/html/user/item.php new file mode 100644 index 0000000..78a757d --- /dev/null +++ b/template/standard/html/user/item.php @@ -0,0 +1,20 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> +
  • +
    +

    + + +

    +
    +
    + +
    +
  • \ No newline at end of file diff --git a/template/standard/html/user/list.php b/template/standard/html/user/list.php new file mode 100644 index 0000000..922a3a4 --- /dev/null +++ b/template/standard/html/user/list.php @@ -0,0 +1,19 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> +

    text('user_overview')?>

    +

    template('user_base_heading_desc', $PAGINATION['THIS'])?>

    + +
      + + + +
    + + \ No newline at end of file diff --git a/template/standard/html/user/main.php b/template/standard/html/user/main.php new file mode 100644 index 0000000..adc0534 --- /dev/null +++ b/template/standard/html/user/main.php @@ -0,0 +1,43 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# [see documentation] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +?> +

    template('user_heading_text', [escapeHTML($USER['ATTR']['FULLNAME']), $USER['ATTR']['USERNAME']])?>

    +

    template('user_heading_desc', [escapeHTML($USER['ATTR']['FULLNAME']), $COUNT['POST'], $COUNT['PAGE']])?>

    + +
    + +
    + + + + \ No newline at end of file diff --git a/template/standard/lang/de.php b/template/standard/lang/de.php new file mode 100644 index 0000000..333aab2 --- /dev/null +++ b/template/standard/lang/de.php @@ -0,0 +1,51 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# This file contains template internationalization strings for the DE language # +# and is completely independend from the core internationalization strings. # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +$LANGUAGE['date_format'] = '[D].[M].[Y] [H]:[I]'; + +$LANGUAGE['feed_only_posts'] = 'nur Beiträge'; +$LANGUAGE['feed_only_pages'] = 'nur Seiten'; + +$LANGUAGE['navigation_home_text'] = 'Home'; +$LANGUAGE['navigation_home_desc'] = '%s'; + +$LANGUAGE['navigation_search_text'] = 'Suche'; +$LANGUAGE['navigation_search_desc'] = 'Volltextsuche'; + +$LANGUAGE['home_heading_text'] = 'Willkommen bei %s'; +$LANGUAGE['home_heading_desc'] = 'Hallo! Hier siehst du erst einmal die letzten %d veröffentlichten Beiträge. Viel Spaß!'; + +$LANGUAGE['user_heading_text'] = '%s [%s]'; +$LANGUAGE['user_heading_desc'] = 'Bisher wurden von %s insgesamt %d Beiträge und %d Seiten veröffentlicht.'; + +$LANGUAGE['post_base_heading_desc'] = '[Seite: %d] Hier siehst du alle veröffentlichten Beiträge nach dem Zeitpunkt der Veröffentlchung sortiert.'; +$LANGUAGE['page_base_heading_desc'] = '[Seite: %d] Hier siehst du alle veröffentlichten Seiten nach dem Zeitpunkt der Veröffentlchung sortiert.'; +$LANGUAGE['user_base_heading_desc'] = '[Seite: %d] Hier siehst du alle vorhandenen Benutzer nach dem Zeitpunkt der Erstellung sortiert.'; + +$LANGUAGE['post_main_heading_text'] = '%s'; +$LANGUAGE['post_main_heading_desc'] = 'Von: %s (veröffentlicht am: %s)'; + +$LANGUAGE['page_main_heading_text'] = '%s'; +$LANGUAGE['page_main_heading_desc'] = 'Von: %s (veröffentlicht am: %s)'; + +$LANGUAGE['403_heading_text'] = 'Zugriff verweigert'; +$LANGUAGE['403_heading_desc'] = 'Der Zugriff auf diese Ressource des Servers wurde dir verweigert, da du die dafür notwendigen Berechtigungen nicht besitzt.'; + +$LANGUAGE['404_heading_text'] = 'Nicht gefunden'; +$LANGUAGE['404_heading_desc'] = 'Die angeforderte Ressource konnte auf diesem Server nicht gefunden werden.'; + +$LANGUAGE['search_base_heading_text'] = 'Volltextsuche'; +$LANGUAGE['search_base_heading_desc'] = 'Wenn du einen bestimmten Beitrag suchst, dann kann dir die Volltext-Suchfunktion der MySQL-Datenbank bestimmt weiterhelfen.'; + +$LANGUAGE['search_result_heading_text'] = 'Suchergebnisse für %s'; +$LANGUAGE['search_result_heading_desc'] = 'Herzlichen Glückwunsch, deine Suchanfrage scheint erfolgreich gewesen zu sein!'; + +$LANGUAGE['search_form_placeholder'] = 'Suchbegriff eingeben …'; +?> \ No newline at end of file diff --git a/template/standard/lang/en.php b/template/standard/lang/en.php new file mode 100644 index 0000000..66e2a67 --- /dev/null +++ b/template/standard/lang/en.php @@ -0,0 +1,51 @@ +] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# This file contains template internationalization strings for the EN language # +# and is completely independend from the core internationalization strings. # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +$LANGUAGE['date_format'] = '[Y]-[M]-[D] [H]:[I]'; + +$LANGUAGE['feed_only_posts'] = 'only posts'; +$LANGUAGE['feed_only_pages'] = 'only pages'; + +$LANGUAGE['navigation_home_text'] = 'Home'; +$LANGUAGE['navigation_home_desc'] = '%s'; + +$LANGUAGE['navigation_search_text'] = 'Search'; +$LANGUAGE['navigation_search_desc'] = 'Fulltext search'; + +$LANGUAGE['home_heading_text'] = 'Welcome to %s'; +$LANGUAGE['home_heading_desc'] = 'Here you can see the last 10 published posts. Have fun!'; + +$LANGUAGE['user_heading_text'] = '%s [%s]'; +$LANGUAGE['user_heading_desc'] = '%s has published a total count of %d posts and %d pages.'; + +$LANGUAGE['post_base_heading_desc'] = '[Page: %d] Here you can see all published posts ordered by the date of publication.'; +$LANGUAGE['page_base_heading_desc'] = '[Page: %d] Here you can see all published pages ordered by the date of publication.'; +$LANGUAGE['user_base_heading_desc'] = '[Page: %d] Here you can see all existing users ordered by the date of creation.'; + +$LANGUAGE['post_main_heading_text'] = '%s'; +$LANGUAGE['post_main_heading_desc'] = 'By: %s (published on: %s)'; + +$LANGUAGE['page_main_heading_text'] = '%s'; +$LANGUAGE['page_main_heading_desc'] = 'By: %s (published on: %s)'; + +$LANGUAGE['403_heading_text'] = 'Access denied'; +$LANGUAGE['403_heading_desc'] = 'You are denied to access this resource because you do not have the necessary permissions.'; + +$LANGUAGE['404_heading_text'] = 'Not found'; +$LANGUAGE['404_heading_desc'] = 'The requested resource could not be found on this server.'; + +$LANGUAGE['search_base_heading_text'] = 'Fulltext search'; +$LANGUAGE['search_base_heading_desc'] = 'If you are looking for a specific post, then the full-text search function of the MySQL database could help you.'; + +$LANGUAGE['search_result_heading_text'] = 'Search results for %s'; +$LANGUAGE['search_result_heading_desc'] = 'Congratulations, your search request seems to have been successful!'; + +$LANGUAGE['search_form_placeholder'] = 'Enter search term …'; +?> \ No newline at end of file diff --git a/template/standard/rsrc/font-awesome.min.css b/template/standard/rsrc/font-awesome.min.css new file mode 100644 index 0000000..540440c --- /dev/null +++ b/template/standard/rsrc/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/template/standard/rsrc/font/font-awesome.woff2 b/template/standard/rsrc/font/font-awesome.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/template/standard/rsrc/font/font-awesome.woff2 differ diff --git a/template/standard/rsrc/font/ruda-n-400.woff2 b/template/standard/rsrc/font/ruda-n-400.woff2 new file mode 100644 index 0000000..6435a0d Binary files /dev/null and b/template/standard/rsrc/font/ruda-n-400.woff2 differ diff --git a/template/standard/rsrc/font/ruda-n-700.woff2 b/template/standard/rsrc/font/ruda-n-700.woff2 new file mode 100644 index 0000000..0066431 Binary files /dev/null and b/template/standard/rsrc/font/ruda-n-700.woff2 differ diff --git a/template/standard/rsrc/logo.png b/template/standard/rsrc/logo.png new file mode 100644 index 0000000..7a62549 Binary files /dev/null and b/template/standard/rsrc/logo.png differ diff --git a/template/standard/rsrc/main.css b/template/standard/rsrc/main.css new file mode 100644 index 0000000..44547ec --- /dev/null +++ b/template/standard/rsrc/main.css @@ -0,0 +1,202 @@ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Hyperlinks +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +a{color:#0060A0;text-decoration:none;}a:focus{background:#CCC;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Paragraphs +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:0;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Headings +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +h1,h2,h3,h4,h5,h6{margin:0;font-weight:600;} +h1{font-size:0.80rem;}h2{font-size:0.70rem;} +h3{font-size:0.65rem;}h4{font-size:0.60rem;} +h5{font-size:0.55rem;}h6{font-size:0.50rem;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Document +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +html,body{margin:0;padding:0;} +html{font-size:1.25rem;color:#333;background:#CCC;-webkit-hyphens:auto;hyphens:auto;} +body{font-family:Ruda,sans-serif;font-size:0.7rem;line-height:1.2rem;} + +#container{max-width:45rem;margin:1rem auto;border:0.05rem solid #AAA;background:#FFF;} + +main,#main-header > section{padding:1rem;box-sizing:border-box;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Header +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#main-header > section{overflow:hidden;} +#main-logo{height:1.75rem;display:block;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Footer +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#main-footer{background:#EEE;border-top:0.05rem solid #AAA;padding:0.25rem 1rem;text-align:center;font-size:0.6rem;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Main Navigation +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#main-navi{font-size:0.6rem;background:#EEE;border:0.05rem solid #AAA;border-left:none;border-right:none;padding:0 1rem;} +#main-navi ul{list-style:none;margin:0;padding:0;} +#main-navi li{display:inline;} +#main-navi li .fa, h1 > .fa, h2 > .fa{margin-right:0.25rem;} +#main-navi a{padding:0.25rem 0.3rem;color:inherit;text-decoration:none;text-align:center;display:inline-block;border:0.05rem solid transparent;border-top:none;border-bottom:none;} +#main-navi a:hover, #main-navi a:focus{text-decoration:none;background:#DDD;border:0.05rem solid #AAA;border-top:none;border-bottom:none;} +#main-navi li:last-child{float:right;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Site Navigation +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#site-navi{clear:both;display:flex;box-sizing:border-box;justify-content:space-between;} +#site-navi > div{display:flex;align-items:center;border:0.05rem solid #AAA;background:#EEE;} +#site-navi > div > a{display:block;} +#site-navi > section{display:flex;overflow:hidden;align-items:center;} +#site-navi > section > div{border:0.05rem solid #AAA;background:#EEE;} + +#site-navi .disabled{pointer-events:none;color:#AAA;} +#site-navi .active a{background:#CCC !important;font-weight:600;pointer-events: none;} + +#site-navi ol{list-style:none;margin:0;padding:0;} +#site-navi li{float:left;display:inline-block;} +#site-navi li+li{border-left:0.05rem solid #AAA;} +#site-navi a{padding:0 0.5rem;text-decoration:none;color:inherit;display:inline-block;} +#site-navi a:hover,#site-navi a:focus{background:#CCC;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Elements +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +pre{font-family:monospace;margin-bottom:1rem;overflow:auto;-moz-tab-size:4;tab-size:4;} +code,pre{font-family:monospace;color:#008B45;} +strong,label{font-weight:600;} +img{border:none;max-width:100%;} +main img{border:0.05rem solid #000;border-radius:0.15rem;} +table img{border:none;border-radius:0;} +.red{color:#B03060;} +.head-link{font-size:0.6rem;float:right;} +.head-link .fa{margin-right:0.125rem;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Brackets +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +.brackets a,a.brackets{text-decoration:none;} +.brackets:after{content:"]"} +.brackets:before{content:"["} +a.brackets:before,a.brackets:after{color:#222;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Item List
      +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +.item-list{list-style:none;padding:0;} +.item-list.page{} +.item-list.post{} +.item-list.user{} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Item List
    • +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +.item-list-li{background:#EEE;border:0.05rem solid #AAA;overflow:hidden;margin:0.5rem 0;} +.item-list-li > header{padding:0.25rem 1rem;border-bottom:0.05rem solid #AAA;overflow:hidden;} +.item-list-li > header h2{border:none !important;font-size:inherit;text-transform:uppercase;} +.item-list-li > header h2 .info{float:right;font-size:0.7rem;font-weight:400;} +.item-list-li > header a{color:inherit;} +.item-list-li > article{padding:0 1rem;} +.item-list-li > article img{display:block;} +.item-list-li.page{} +.item-list-li.post{} +.item-list-li.user{} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Item content on main sites +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#content{background:#EEE;border:0.05rem solid #AAA;margin:0.5rem 0;padding:0 1rem;} +#content img{display:block;} +#content.page{} /* different rules for page item */ +#content.post{} /* different rules for page item */ +#content.user{} /* different rules for page item */ + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Responsive +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#toogle-nav{display:none;clear:both;} +#toogle-nav-label{display:none;cursor:pointer;font-size:1.25rem;text-align:center;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Form elements +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +input,select{background:#EEE;color:inherit;padding:0.2rem;border:0.1rem solid #AAA;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Table elements +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +table{width:100%;margin:0 0 1rem;}td{vertical-align:middle;} +table,td{border-spacing:0;border-collapse:collapse;padding:0.5rem;border:0.05rem solid #000;} +thead,tr:nth-child(even){background:#EEE;} +thead > tr, th{font-weight:600;font-style:italic;} +thead > tr > td, th > td{text-align:center;} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Responsive Level #1 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +@media only screen and (max-width:50rem) { + html{font-size:1.125rem;/*18px*/background-image:none !important;} + body{line-height:1.2rem;} + #container{margin:0;border-right:none;border-left:none;} +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Responsive Level #2 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +@media only screen and (max-width:37.5rem) { + main,#main-header > section{padding:1rem;} + #main-navi{padding:0.25rem 1rem;} + #main-navi a{border:0.05rem solid transparent;} + #main-navi a:hover, #main-navi a:focus{border:0.05rem solid #BBB;} + #main-navi > ul{display:none;float:none;} + #main-navi > ul > li a{display:block;text-align:left;padding:0 0.25rem;} + #main-navi{overflow:hidden;} + h2{border-bottom:0.05rem solid #888;} + #toogle-nav-label{display:block;} + #toogle-nav:checked + ul{display:block;} + #main-navi li:last-child{float:none;} + + .item-list-li > header > h2{text-align:center;} + .item-list-li > header > h2 > .info{float:none;display:block;} +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* FontAwesome Main +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +.fa{display:inline-block;font:normal normal normal 14px/1 "FontAwesome";font-size:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* FontAwesome Icons +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +.fa-rss:before{content:"\f09e"} +.fa-key:before{content:"\f084"} +.fa-bars:before{content:"\f0c9"} +.fa-user:before{content:"\f007"} +.fa-home:before{content:"\f015"} +.fa-search:before{content:"\f002"} +.fa-rss-square:before{content:"\f143"} +.fa-arrow-left:before{content:"\f060"} +.fa-user-secret:before{content:"\f21b"} +.fa-file-text-o:before{content:"\f0f6"} +.fa-newspaper-o:before{content:"\f1ea"} +.fa-arrow-right:before{content:"\f061"} +.fa-exclamation-triangle:before{content:"\f071"} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Font "Font Awesome" [4.7.0]: SIL Open Font License (OFL) +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +@font-face{font-family:"FontAwesome";font-weight:400;src:url("font/font-awesome.woff2") format("woff2");} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* Font "Ruda": SIL Open Font License (OFL) +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +@font-face{font-family:Ruda;font-weight:400;src:url("font/ruda-n-400.woff2") format("woff2");} +@font-face{font-family:Ruda;font-weight:700;src:url("font/ruda-n-700.woff2") format("woff2");} \ No newline at end of file -- cgit v1.2.3