mirror of
https://github.com/typemill/typemill.git
synced 2025-07-16 12:06:21 +02:00
RC2: Fix frontend navigation, darkmode, and more
This commit is contained in:
10
composer.lock
generated
10
composer.lock
generated
@ -227,12 +227,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Gregwar/Captcha.git",
|
||||
"reference": "5b8323637b502d3dbcbe28a74633dad8aed63452"
|
||||
"reference": "229d3cdfe33d6f1349e0aec94a26e9205a6db08e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Gregwar/Captcha/zipball/5b8323637b502d3dbcbe28a74633dad8aed63452",
|
||||
"reference": "5b8323637b502d3dbcbe28a74633dad8aed63452",
|
||||
"url": "https://api.github.com/repos/Gregwar/Captcha/zipball/229d3cdfe33d6f1349e0aec94a26e9205a6db08e",
|
||||
"reference": "229d3cdfe33d6f1349e0aec94a26e9205a6db08e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -275,9 +275,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Gregwar/Captcha/issues",
|
||||
"source": "https://github.com/Gregwar/Captcha/tree/master"
|
||||
"source": "https://github.com/Gregwar/Captcha/tree/v1.2.1"
|
||||
},
|
||||
"time": "2023-05-08T15:17:31+00:00"
|
||||
"time": "2023-09-26T13:45:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jbroadway/urlify",
|
||||
|
@ -14,5 +14,5 @@ meta:
|
||||
time: 22-09-48
|
||||
reference: null
|
||||
referencetype: null
|
||||
hide: false
|
||||
hide: true
|
||||
noindex: false
|
||||
|
@ -6,9 +6,5 @@ If you found a bug or if you have a question, then please open a new issue on [G
|
||||
|
||||
Do you need professional help, an individual theme or a special plugin? You can hire us at [Trendschau Digital](https://trendschau.net/typemill-development).
|
||||
|
||||
[recording 2023 05 11 at 235818 (GIF, 1.30 MB)](media/files/recording-2023-05-11-at-235818.gif){.tm-download file-gif}
|
||||
|
||||
[Contributions](https://github.com/typemill/typemill#contributors--supporters), [donations](https://www.paypal.me/typemill) and [feedback](https://github.com/typemill/typemill/issues) for this open source project are always welcome.
|
||||
|
||||
{.center loading="lazy"}
|
||||
|
||||
|
166
content/00-welcome/05-todos.md
Normal file
166
content/00-welcome/05-todos.md
Normal file
@ -0,0 +1,166 @@
|
||||
# ToDos Version 2
|
||||
|
||||
[TOC]
|
||||
|
||||
## System settings
|
||||
|
||||
* DONE: Migrate from backend to frontend with vue and api
|
||||
* DONE: Redesign
|
||||
* DONE: License feature
|
||||
* DONE: Enhance with plugins
|
||||
|
||||
## Visual Editor
|
||||
|
||||
* DONE: Refactor and redesign
|
||||
* DONE: Fix toc component in new block
|
||||
* DONE: Fix hr component in new block
|
||||
* DONE: finish shortcode component
|
||||
* DONE: Fix inline formats
|
||||
* DONE: fix lenght of page
|
||||
* DONE: Fix design of new block at the end (background color)
|
||||
* DONE: Move Block
|
||||
* DONE: Fix headline design
|
||||
* DONE: Fix save on two enter
|
||||
* DONE: fix quote design
|
||||
* DONE: Fix toc preview
|
||||
* DONE: disable enable
|
||||
* DONE: Add load sign (from navigation)
|
||||
* DONE: File is not published from tmp to media/files if you save the block.
|
||||
|
||||
## Raw Editor
|
||||
|
||||
* DONE: Refactor and redesign
|
||||
* DONE: Integrate highlighting
|
||||
|
||||
## Navigation
|
||||
|
||||
* DONE: Refactor and redesign
|
||||
* DONE: fix status in navigation
|
||||
* DONE: refresh navigation after changes
|
||||
|
||||
## Publish Controller
|
||||
|
||||
* DONE: Refactor and redesign
|
||||
* DONE: Create
|
||||
* DONE: publish
|
||||
* DONE: unpublish
|
||||
* DONE: discard
|
||||
* DONE: delete
|
||||
* DONE: save draft
|
||||
* DONE: switch to raw
|
||||
|
||||
## Meta Tabs
|
||||
|
||||
* DONE: Refactor and redesign
|
||||
* DONE: Enhance with plugins
|
||||
|
||||
## Medialib
|
||||
|
||||
* DONE: Refactor and redesign
|
||||
|
||||
## Posts
|
||||
|
||||
* DONE: Refactor and redesign
|
||||
|
||||
## Plugins
|
||||
|
||||
* Asset Class in progress
|
||||
|
||||
## Frontend
|
||||
|
||||
* DONE: Refactor
|
||||
* DONE: Test restrictions
|
||||
|
||||
## Other big tasks
|
||||
|
||||
* DONE: System setup
|
||||
* DONE: Recover Password
|
||||
|
||||
## Medium tasks
|
||||
|
||||
* DONE: Merge processAssets modell
|
||||
* DONE: Table of content duplicated for published pages
|
||||
* DONE: Session handling: csrf fail and session start error if restrictions are active
|
||||
* DONE: Image and files for meta
|
||||
|
||||
## Open tasks
|
||||
|
||||
* DONE: Sitemap and ping
|
||||
* DONE: Version check
|
||||
* DONE: Proxy support
|
||||
* DONE: SVG checker: https://github.com/TribalSystems/SVG-Sanitizer
|
||||
* DONE: Backend form builder
|
||||
* DONE: Image generation on the fly
|
||||
* DONE: Delete folder in base level
|
||||
* DONE: Make folder delete easier with glob or scandir
|
||||
* DONE: fix error messages (check models)
|
||||
* DONE: error status codes (check middleware)
|
||||
* DONE: Warn if open another block
|
||||
* DONE: Customfields not styled yet
|
||||
* DOING: Fix error api systemnavi + validate
|
||||
* FIXED: System stores html or sends wrong error messsages
|
||||
* FIXED: Wrong frontend navigation if unpublished pages
|
||||
* DONE: Icon for hidden pages
|
||||
* DOING: Responsive design
|
||||
* DONE: Captcha integration
|
||||
* DONE: Solution for logo and favicon
|
||||
* FIXED: Raw editor jumps if you edit long text at the end
|
||||
* Reference feature
|
||||
* Typemill Utilities
|
||||
* Markdown secure rendering
|
||||
* finish youtube component
|
||||
* Handle formdata centrally ???
|
||||
* BUG: Error fields in account form not styled correctly
|
||||
|
||||
## later
|
||||
|
||||
* Clear cache
|
||||
* Show security Log
|
||||
* User search only for +10 users
|
||||
* For api translations should be done completely in backoffice
|
||||
* Change translation files so they are loaded in settings instead of adding them manually to settings-defaults.yaml
|
||||
|
||||
## Cleanups:
|
||||
|
||||
* DONE: Events
|
||||
* DONE: Error messages
|
||||
* DONE: Translations
|
||||
|
||||
## Info: Select userroles
|
||||
|
||||
* Userroles for file restriction: in vue-blox-components loaded via api
|
||||
* Userroles for userfields: in php model user getUserFields()
|
||||
* Userroles for meta: in php controller apiAuthorMeta getMeta()
|
||||
* Plugins and themes: in php model extension getThemeDefinitions()
|
||||
|
||||
## Info: License Check
|
||||
|
||||
* On activation in apiControllerExtension. It checks the license in yaml.
|
||||
* In plugin php code with setPremiumLicense
|
||||
* In static plugins, it checks manual premium list and method setPremiumLicense and more
|
||||
|
||||
## Plugins
|
||||
|
||||
* MAKER: Rebuild search
|
||||
* MAKER: Rebuild contactform with shortcode
|
||||
|
||||
## Status codes
|
||||
|
||||
| Status code | Description |
|
||||
|---|---|
|
||||
| 200 ok | cell |
|
||||
| 400 bad request | The request was unacceptable due to missing or invalid parameter. |
|
||||
| 401 unauthorized | The request requires an authorization. |
|
||||
| (402 request failed) | The parameters where there but the request failed for other reasons. |
|
||||
| 403 forbidden | The user is authenticated but he has not enough rights. |
|
||||
| 404 not found | new |
|
||||
| 500 internal server error | new |
|
||||
|
||||
## Upgrade
|
||||
|
||||
* Delete content of system folder
|
||||
* Upload new content of system folder with folders typemill and vendor
|
||||
* Delete settings file
|
||||
* upload new index.php file
|
||||
* Upload new htaccess file
|
||||
|
@ -1 +1 @@
|
||||
["# ToDos Version 2","[TOC]","## System settings","* DONE: Migrate from backend to frontend with vue and api\n* DONE: Redesign\n* DONE: License feature\n* DONE: Enhance with plugins","## Visual Editor","* DONE: Refactor and redesign\n* DONE: Fix toc component in new block\n* DONE: Fix hr component in new block\n* DONE: finish shortcode component\n* DONE: Fix inline formats\n* DONE: fix lenght of page\n* DONE: Fix design of new block at the end (background color)\n* DONE: Move Block\n* DONE: Fix headline design\n* DONE: Fix save on two enter\n* DONE: fix quote design\n* DONE: Fix toc preview\n* DONE: disable enable \n* DONE: Add load sign (from navigation)\n* DONE: File is not published from tmp to media\/files if you save the block.","## Raw Editor","* DONE: Refactor and redesign\n* DONE: Integrate highlighting","## Navigation","* DONE: Refactor and redesign\n* DONE: fix status in navigation\n* DONE: refresh navigation after changes","## Publish Controller","* DONE: Refactor and redesign\n* DONE: Create \n* DONE: publish\n* DONE: unpublish\n* DONE: discard\n* DONE: delete\n* DONE: save draft\n* DONE: switch to raw","## Meta Tabs","* DONE: Refactor and redesign\n* DONE: Enhance with plugins","## Medialib","* DONE: Refactor and redesign","## Posts","* DONE: Refactor and redesign","## Plugins","* Asset Class in progress","## Frontend","* DONE: Refactor\n* DONE: Test restrictions","## Other big tasks","* DONE: System setup\n* DONE: Recover Password","## Medium tasks","* DONE: Merge processAssets modell\n* DONE: Table of content duplicated for published pages\n* DONE: Session handling: csrf fail and session start error if restrictions are active\n* DONE: Image and files for meta","## Open tasks","* DONE: Sitemap and ping\n* DONE: Version check\n* DONE: Proxy support\n* DONE: SVG checker: https:\/\/github.com\/TribalSystems\/SVG-Sanitizer\n* DONE: Backend form builder\n* DONE: Image generation on the fly\n* DONE: Delete folder in base level\n* DONE: Make folder delete easier with glob or scandir\n* DONE: fix error messages (check models)\n* DONE: error status codes (check middleware)\n* DONE: Warn if open another block\n* DONE: Customfields not styled yet\n* DOING: Fix error api systemnavi + validate\n* FIXED: System stores html or sends wrong error messsages\n* FIXED: Wrong frontend navigation if unpublished pages\n* DONE: Icon for hidden pages\n* DOING: Responsive design\n* DONE: Captcha integration\n* DONE: Solution for logo and favicon\n* Reference feature\n* Typemill Utilities\n* Markdown secure rendering\n* finish youtube component\n* Handle formdata centrally ???\n* BUG: Raw editor jumps if you edit long text at the end\n* BUG: Error fields in account form not styled correctly","## later","* Clear cache\n* Show security Log\n* User search only for +10 users\n* For api translations should be done completely in backoffice\n* Change translation files so they are loaded in settings instead of adding them manually to settings-defaults.yaml","## Cleanups:","* DONE: Events\n* DONE: Error messages\n* DONE: Translations","## Info: Select userroles","* Userroles for file restriction: in vue-blox-components loaded via api\n* Userroles for userfields: in php model user getUserFields()\n* Userroles for meta: in php controller apiAuthorMeta getMeta()\n* Plugins and themes: in php model extension getThemeDefinitions()","## Info: License Check","* On activation in apiControllerExtension. It checks the license in yaml.\n* In plugin php code with setPremiumLicense\n* In static plugins, it checks manual premium list and method setPremiumLicense and more ","## Plugins","* MAKER: Rebuild search\n* MAKER: Rebuild contactform with shortcode","## Status codes","| Status code | Description | \n|---|---|\n| 200 ok | cell | \n| 400 bad request | The request was unacceptable due to missing or invalid parameter. | \n| 401 unauthorized | The request requires an authorization. | \n| (402 request failed) | The parameters where there but the request failed for other reasons. | \n| 403 forbidden | The user is authenticated but he has not enough rights. | \n| 404 not found | new | \n| 500 internal server error | new |"]
|
||||
["# ToDos Version 2","[TOC]","## System settings","* DONE: Migrate from backend to frontend with vue and api\n* DONE: Redesign\n* DONE: License feature\n* DONE: Enhance with plugins","## Visual Editor","* DONE: Refactor and redesign\n* DONE: Fix toc component in new block\n* DONE: Fix hr component in new block\n* DONE: finish shortcode component\n* DONE: Fix inline formats\n* DONE: fix lenght of page\n* DONE: Fix design of new block at the end (background color)\n* DONE: Move Block\n* DONE: Fix headline design\n* DONE: Fix save on two enter\n* DONE: fix quote design\n* DONE: Fix toc preview\n* DONE: disable enable \n* DONE: Add load sign (from navigation)\n* DONE: File is not published from tmp to media\/files if you save the block.","## Raw Editor","* DONE: Refactor and redesign\n* DONE: Integrate highlighting","## Navigation","* DONE: Refactor and redesign\n* DONE: fix status in navigation\n* DONE: refresh navigation after changes","## Publish Controller","* DONE: Refactor and redesign\n* DONE: Create \n* DONE: publish\n* DONE: unpublish\n* DONE: discard\n* DONE: delete\n* DONE: save draft\n* DONE: switch to raw","## Meta Tabs","* DONE: Refactor and redesign\n* DONE: Enhance with plugins","## Medialib","* DONE: Refactor and redesign","## Posts","* DONE: Refactor and redesign","## Plugins","* Asset Class in progress","## Frontend","* DONE: Refactor\n* DONE: Test restrictions","## Other big tasks","* DONE: System setup\n* DONE: Recover Password","## Medium tasks","* DONE: Merge processAssets modell\n* DONE: Table of content duplicated for published pages\n* DONE: Session handling: csrf fail and session start error if restrictions are active\n* DONE: Image and files for meta","## Open tasks","* DONE: Sitemap and ping\n* DONE: Version check\n* DONE: Proxy support\n* DONE: SVG checker: https:\/\/github.com\/TribalSystems\/SVG-Sanitizer\n* DONE: Backend form builder\n* DONE: Image generation on the fly\n* DONE: Delete folder in base level\n* DONE: Make folder delete easier with glob or scandir\n* DONE: fix error messages (check models)\n* DONE: error status codes (check middleware)\n* DONE: Warn if open another block\n* DONE: Customfields not styled yet\n* DOING: Fix error api systemnavi + validate\n* FIXED: System stores html or sends wrong error messsages\n* FIXED: Wrong frontend navigation if unpublished pages\n* DONE: Icon for hidden pages\n* DOING: Responsive design\n* DONE: Captcha integration\n* DONE: Solution for logo and favicon\n* FIXED: Raw editor jumps if you edit long text at the end\n* Reference feature\n* Typemill Utilities\n* Markdown secure rendering\n* finish youtube component\n* Handle formdata centrally ???\n* BUG: Error fields in account form not styled correctly\n* DONE: Update CSS for themes\n* BUG: Codefield jumps on editing","## later","* Clear cache\n* Show security Log\n* User search only for +10 users\n* For api translations should be done completely in backoffice\n* Change translation files so they are loaded in settings instead of adding them manually to settings-defaults.yaml","## Cleanups:","* DONE: Events\n* DONE: Error messages\n* DONE: Translations","## Info: Select userroles","* Userroles for file restriction: in vue-blox-components loaded via api\n* Userroles for userfields: in php model user getUserFields()\n* Userroles for meta: in php controller apiAuthorMeta getMeta()\n* Plugins and themes: in php model extension getThemeDefinitions()","## Info: License Check","* On activation in apiControllerExtension. It checks the license in yaml.\n* In plugin php code with setPremiumLicense\n* In static plugins, it checks manual premium list and method setPremiumLicense and more ","## Plugins","* MAKER: Rebuild search\n* MAKER: Rebuild contactform with shortcode","## Status codes","| Status code | Description | \n|---|---|\n| 200 ok | cell | \n| 400 bad request | The request was unacceptable due to missing or invalid parameter. | \n| 401 unauthorized | The request requires an authorization. | \n| (402 request failed) | The parameters where there but the request failed for other reasons. | \n| 403 forbidden | The user is authenticated but he has not enough rights. | \n| 404 not found | new | \n| 500 internal server error | new |","## Upgrade","* Delete content of system folder\n* Upload new content of system folder with folders typemill and vendor\n* Delete settings file \n* upload new index.php file\n* Upload new htaccess file"]
|
30
data/css/cyanine-custom.css
Normal file
30
data/css/cyanine-custom.css
Normal file
@ -0,0 +1,30 @@
|
||||
.landingpageintro h1{
|
||||
display: inline-block;
|
||||
background: white;
|
||||
padding: 4px 10px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.landingpageintro p{
|
||||
display: inline;
|
||||
background: white;
|
||||
line-height: 2.1rem;
|
||||
padding: 6px 4px 4px;
|
||||
}
|
||||
article a[href^="http"]::after,
|
||||
article a[href^="https://"]::after
|
||||
{
|
||||
content: "";
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
margin-left: 4px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z'/%3E%3Cpath fill-rule='evenodd' d='M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z'/%3E%3C/svg%3E");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
display: inline-block;
|
||||
}
|
||||
a[href^="https://www.electrictoolbox.com"] {
|
||||
background: none;
|
||||
padding-right: 0;
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -18,7 +18,7 @@
|
||||
keyPath: '0.1'
|
||||
/welcome/write-content:
|
||||
navtitle: 'write content'
|
||||
hide: false
|
||||
hide: true
|
||||
noindex: false
|
||||
path: /00-welcome/02-write-content.md
|
||||
keyPath: '0.2'
|
||||
@ -38,7 +38,7 @@
|
||||
navtitle: 'To Dos'
|
||||
hide: false
|
||||
noindex: false
|
||||
path: /00-welcome/05-todos.txt
|
||||
path: /00-welcome/05-todos.txtmd
|
||||
keyPath: '0.5'
|
||||
/cyanine-theme:
|
||||
navtitle: 'cyanine theme'
|
||||
|
File diff suppressed because one or more lines are too long
@ -47,4 +47,25 @@ class ControllerApiSystemThemes extends Controller
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
|
||||
}
|
||||
|
||||
public function updateThemeCss(Request $request, Response $response)
|
||||
{
|
||||
$params = $request->getParsedBody();
|
||||
$themename = $params['theme'];
|
||||
$themecss = $params['css'];
|
||||
|
||||
# validate css input
|
||||
$themecss = strip_tags($themecss);
|
||||
|
||||
# store updated css
|
||||
$settings = new Settings();
|
||||
$updatedSettings = $settings->updateThemeCss($themename, $themecss);
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => Translations::translate('settings have been saved'),
|
||||
'code' => $updatedSettings
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
|
||||
}
|
||||
}
|
@ -29,13 +29,11 @@ class ControllerWebFrontend extends Controller
|
||||
$userrole = $request->getAttribute('c_userrole');
|
||||
$username = $request->getAttribute('c_username');
|
||||
|
||||
|
||||
# GET THE NAVIGATION
|
||||
$navigation = new Navigation();
|
||||
$liveNavigation = $navigation->getLiveNavigation($urlinfo, $langattr);
|
||||
$draftNavigation = $navigation->getDraftNavigation($urlinfo, $langattr);
|
||||
$home = false;
|
||||
|
||||
|
||||
# GET THE PAGINATION
|
||||
$currentpage = $navigation->getCurrentPage($args);
|
||||
if($currentpage)
|
||||
@ -56,6 +54,7 @@ class ControllerWebFrontend extends Controller
|
||||
$extendedNavigation = $navigation->getExtendedNavigation($urlinfo, $langattr);
|
||||
|
||||
$pageinfo = $extendedNavigation[$url] ?? false;
|
||||
|
||||
if(!$pageinfo)
|
||||
{
|
||||
return $this->c->get('view')->render($response->withStatus(404), '404.twig', [
|
||||
@ -66,23 +65,46 @@ class ControllerWebFrontend extends Controller
|
||||
|
||||
$keyPathArray = explode(".", $pageinfo['keyPath']);
|
||||
|
||||
$liveNavigation = $navigation->setActiveNaviItems($liveNavigation, $keyPathArray);
|
||||
#
|
||||
$item = $navigation->getItemWithKeyPath($draftNavigation, $keyPathArray);
|
||||
|
||||
$item = $navigation->getItemWithKeyPath($liveNavigation, $keyPathArray);
|
||||
# but what if parent is unpublished ??
|
||||
if(!$item)
|
||||
{
|
||||
return $this->c->get('view')->render($response->withStatus(404), '404.twig', [
|
||||
'title' => 'Page not found',
|
||||
'description' => 'We did not find the page you where looking for.'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$liveNavigation = $this->c->get('dispatcher')->dispatch(new OnPagetreeLoaded($liveNavigation), 'onPagetreeLoaded')->getData();
|
||||
|
||||
|
||||
# CREATE THE BREADCRUMB
|
||||
$breadcrumb = $navigation->getBreadcrumb($liveNavigation, $item->keyPathArray);
|
||||
$breadcrumb = $navigation->getBreadcrumb($draftNavigation, $item->keyPathArray);
|
||||
$breadcrumb = $this->c->get('dispatcher')->dispatch(new OnBreadcrumbLoaded($breadcrumb), 'onBreadcrumbLoaded')->getData();
|
||||
|
||||
# CHECK IF WHOLE TREE IS PUBLISHED
|
||||
foreach($breadcrumb as $page)
|
||||
{
|
||||
if($page->status == 'unpublished')
|
||||
{
|
||||
return $this->c->get('view')->render($response->withStatus(404), '404.twig', [
|
||||
'title' => 'Page not found',
|
||||
'description' => 'We did not find the page you where looking for.'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
# GET THE LIVE NAVIGATION (keyPathArray does not match here!!!)
|
||||
$liveNavigation = $navigation->getLiveNavigation($urlinfo, $langattr);
|
||||
|
||||
# STRIP OUT HIDDEN PAGES
|
||||
$liveNavigation = $navigation->removeHiddenPages($liveNavigation);
|
||||
# we could cache navigation without hidden pages??
|
||||
|
||||
# SET PAGEs ACTIVE
|
||||
$liveNavigation = $navigation->setActiveNaviItems($liveNavigation, $breadcrumb);
|
||||
|
||||
# DISPATCH LIVE NAVIGATION
|
||||
$liveNavigation = $this->c->get('dispatcher')->dispatch(new OnPagetreeLoaded($liveNavigation), 'onPagetreeLoaded')->getData();
|
||||
|
||||
# ADD BACKWARD-/FORWARD PAGINATION
|
||||
$item = $navigation->getPagingForItem($liveNavigation, $item);
|
||||
|
@ -82,14 +82,79 @@ class Folder
|
||||
$folderContent[] = $item;
|
||||
}
|
||||
|
||||
/* store the name of the last file */
|
||||
# store the name of the last file
|
||||
$last = implode($nameParts);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $folderContent;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* scans content of a folder recursively and keeps the index order
|
||||
* vars: folder path as string
|
||||
* returns: multi-dimensional array with names of folders and files
|
||||
*/
|
||||
public function scanFolderCheckIndex($folderPath, $draft = false)
|
||||
{
|
||||
$folderItems = scandir($folderPath);
|
||||
$folderContent = array();
|
||||
|
||||
# if it is the live version and if it is a folder that is not published, then do not show the folder and its content.
|
||||
if(!$draft && !in_array('index.md', $folderItems)){ return false; }
|
||||
|
||||
foreach ($folderItems as $key => $item)
|
||||
{
|
||||
if (!in_array($item, array(".","..")) && substr($item, 0, 1) != '.')
|
||||
{
|
||||
if (is_dir($folderPath . DIRECTORY_SEPARATOR . $item))
|
||||
{
|
||||
|
||||
$subFolder = $item;
|
||||
$folderPublished = file_exists($folderPath . DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . 'index.md');
|
||||
|
||||
# scan that folder only if it is a draft or if the folder is published (contains index.md)
|
||||
if($draft OR $folderPublished)
|
||||
{
|
||||
# we need s countable index here
|
||||
$folderContent[$subFolder] = $this->scanFolder($folderPath . DIRECTORY_SEPARATOR . $subFolder, $draft);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$nameParts = $this->getStringParts($item);
|
||||
$fileType = array_pop($nameParts);
|
||||
|
||||
if($fileType == 'md')
|
||||
{
|
||||
$folderContent[] = $item;
|
||||
}
|
||||
|
||||
if($fileType == 'txt')
|
||||
{
|
||||
if(isset($last) && ($last == implode($nameParts)) )
|
||||
{
|
||||
array_pop($folderContent);
|
||||
$item = $item . 'md';
|
||||
}
|
||||
$folderContent[] = $item;
|
||||
|
||||
if(!$draft)
|
||||
{
|
||||
$index = count($folderContent)-1;
|
||||
unset($folderContent[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
# store the name of the last file
|
||||
$last = implode($nameParts);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return $folderContent;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transforms array of folder item into an array of item-objects with additional information for each item
|
||||
|
@ -80,15 +80,19 @@ class Navigation extends Folder
|
||||
|
||||
$allowedmainnavi = [];
|
||||
|
||||
$activeitem = false;
|
||||
|
||||
foreach($mainnavi as $name => $naviitem)
|
||||
{
|
||||
if($acl->isAllowed($userrole, $naviitem['aclresource'], $naviitem['aclprivilege']))
|
||||
{
|
||||
# set the navi of current route active
|
||||
$thisRoute = '/tm/' . $name;
|
||||
|
||||
if(strpos($urlinfo['route'], $thisRoute) !== false)
|
||||
{
|
||||
$naviitem['active'] = true;
|
||||
$activeitem = true;
|
||||
}
|
||||
|
||||
$allowedmainnavi[$name] = $naviitem;
|
||||
@ -99,6 +103,12 @@ class Navigation extends Folder
|
||||
if(isset($allowedmainnavi['system']))
|
||||
{
|
||||
unset($allowedmainnavi['account']);
|
||||
|
||||
# if no active item has been found, then it is submenu under system
|
||||
if(!$activeitem)
|
||||
{
|
||||
$allowedmainnavi['system']['active'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
# set correct editor mode according to user settings
|
||||
@ -112,16 +122,19 @@ class Navigation extends Folder
|
||||
|
||||
public function getSystemNavigation($userrole, $acl, $urlinfo, $dispatcher, $routeparser)
|
||||
{
|
||||
$systemnavi = $this->storage->getYaml('systemSettings', '', 'systemnavi.yaml');
|
||||
$systemnavi = $dispatcher->dispatch(new OnSystemnaviLoaded($systemnavi), 'onSystemnaviLoaded')->getData();
|
||||
$systemnavi = $this->storage->getYaml('systemSettings', '', 'systemnavi.yaml');
|
||||
$systemnavi = $dispatcher->dispatch(new OnSystemnaviLoaded($systemnavi), 'onSystemnaviLoaded')->getData();
|
||||
|
||||
$allowedsystemnavi = [];
|
||||
$allowedsystemnavi = [];
|
||||
|
||||
$route = trim($urlinfo['route'], '/');
|
||||
|
||||
foreach($systemnavi as $name => $naviitem)
|
||||
{
|
||||
$naviitem['url'] = $routeparser->urlFor($naviitem['routename']);
|
||||
$naviitem['url'] = $routeparser->urlFor($naviitem['routename']);
|
||||
$itemurl = trim($naviitem['url'], '/');
|
||||
|
||||
if(strpos( trim($naviitem['url'], '/'), trim($urlinfo['route'], '/')))
|
||||
if(strpos( $itemurl, $route ) !== false)
|
||||
{
|
||||
$naviitem['active'] = true;
|
||||
}
|
||||
@ -224,7 +237,6 @@ class Navigation extends Folder
|
||||
return $this->basicLiveNavigation;
|
||||
}
|
||||
|
||||
# creates a fresh structure with published and non-published pages for the author
|
||||
public function createBasicLiveNavigation($urlinfo, $language)
|
||||
{
|
||||
# scan the content of the folder
|
||||
@ -330,6 +342,7 @@ class Navigation extends Folder
|
||||
|
||||
public function getItemWithKeyPath($navigation, array $searchArray, $baseUrl = null)
|
||||
{
|
||||
|
||||
$item = false;
|
||||
|
||||
# if it is the homepage
|
||||
@ -352,7 +365,37 @@ class Navigation extends Folder
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function setActiveNaviItems($navigation, array $searchArray)
|
||||
# used with scan folder that generates own indexes for live version
|
||||
public function setActiveNaviItems($navigation, $breadcrumb)
|
||||
{
|
||||
foreach($breadcrumb as $crumbkey => $page)
|
||||
{
|
||||
foreach($navigation as $itemkey => $item)
|
||||
{
|
||||
if($page->urlRelWoF == $item->urlRelWoF)
|
||||
{
|
||||
unset($breadcrumb[$crumbkey]);
|
||||
|
||||
if(empty($breadcrumb))
|
||||
{
|
||||
$navigation[$itemkey]->active = true;
|
||||
}
|
||||
elseif(isset($navigation[$itemkey]->folderContent))
|
||||
{
|
||||
$navigation[$itemkey]->activeParent = true;
|
||||
$navigation[$itemkey]->folderContent = $this->setActiveNaviItems($navigation[$itemkey]->folderContent, $breadcrumb);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $navigation;
|
||||
}
|
||||
|
||||
# used with scan folder that keeps index from draft version
|
||||
public function setActiveNaviItemsWithKeyPath($navigation, array $searchArray)
|
||||
{
|
||||
foreach($searchArray as $key => $itemKey)
|
||||
{
|
||||
@ -491,6 +534,7 @@ class Navigation extends Folder
|
||||
if(!isset($navigation[$searchArray[$i]])){ return false; }
|
||||
$item = $navigation[$searchArray[$i]];
|
||||
|
||||
|
||||
if($i == count($searchArray)-1)
|
||||
{
|
||||
$item->active = true;
|
||||
@ -536,9 +580,16 @@ class Navigation extends Folder
|
||||
$item->thisChapter = $this->getItemWithKeyPath($navigation, $thisChapArray);
|
||||
}
|
||||
|
||||
$flat = $this->flatten($navigation, $item->keyPath);
|
||||
$flat = $this->flatten($navigation, $item->urlRel);
|
||||
|
||||
$itemkey = $flat[0];
|
||||
|
||||
# if no previous or next is found (e.g. hidden page)
|
||||
if(!is_int($itemkey))
|
||||
{
|
||||
return $item;
|
||||
}
|
||||
|
||||
if($itemkey > 1)
|
||||
{
|
||||
$item->prevItem = $flat[$itemkey-1];
|
||||
@ -551,13 +602,13 @@ class Navigation extends Folder
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function flatten($navigation, $keyPath, $flat = [])
|
||||
{
|
||||
public function flatten($navigation, $urlRel, $flat = [])
|
||||
{
|
||||
foreach($navigation as $key => $item)
|
||||
{
|
||||
$flat[] = clone($item);
|
||||
|
||||
if($keyPath === $item->keyPath)
|
||||
if($item->urlRel == $urlRel)
|
||||
{
|
||||
array_unshift($flat, count($flat));
|
||||
}
|
||||
@ -566,134 +617,13 @@ class Navigation extends Folder
|
||||
{
|
||||
$last = array_key_last($flat);
|
||||
unset($flat[$last]->folderContent);
|
||||
$flat = $this->flatten($item->folderContent, $keyPath, $flat);
|
||||
$flat = $this->flatten($item->folderContent, $urlRel, $flat);
|
||||
}
|
||||
}
|
||||
|
||||
return $flat;
|
||||
}
|
||||
|
||||
public function getPagingForItemOld($navigation, $item)
|
||||
{
|
||||
# if page is home
|
||||
if(trim($item->pathWithoutType, DIRECTORY_SEPARATOR) == 'index')
|
||||
{
|
||||
return $item;
|
||||
}
|
||||
|
||||
$keyPos = count($item->keyPathArray)-1;
|
||||
$thisChapArray = $item->keyPathArray;
|
||||
$nextItemArray = $item->keyPathArray;
|
||||
$prevItemArray = $item->keyPathArray;
|
||||
|
||||
$item->thisChapter = false;
|
||||
$item->prevItem = false;
|
||||
$item->nextItem = false;
|
||||
|
||||
|
||||
/************************
|
||||
* ADD THIS CHAPTER *
|
||||
************************/
|
||||
|
||||
if($keyPos > 0)
|
||||
{
|
||||
array_pop($thisChapArray);
|
||||
$item->thisChapter = $this->getItemWithKeyPath($navigation, $thisChapArray);
|
||||
}
|
||||
|
||||
/************************
|
||||
* ADD NEXT ITEM *
|
||||
************************/
|
||||
|
||||
if($item->elementType == 'folder')
|
||||
{
|
||||
# get the first element in the folder
|
||||
$item->nextItem = isset($item->folderContent[0]) ? clone($item->folderContent[0]) : false;
|
||||
}
|
||||
|
||||
# the item is a file or an empty folder
|
||||
if(!$item->nextItem)
|
||||
{
|
||||
# walk to the next file in the same hierarchy
|
||||
$nextItemArray[$keyPos]++;
|
||||
|
||||
# get the key of the last element in this hierarchy level
|
||||
# if there is no chapter, then it is probably an empty first-level-folder. Count content to get the number of first level items
|
||||
$lastKey = $item->thisChapter ? array_key_last($item->thisChapter->folderContent) : count($navigation);
|
||||
|
||||
# as long as the nextItemArray is smaller than the last key in this hierarchy level, search for the next item
|
||||
# this ensures that it does not stop if key is missing (e.g. if the next page is hidden)
|
||||
while( ($nextItemArray[$keyPos] <= $lastKey) && !$item->nextItem = $this->getItemWithKeyPath($navigation, $nextItemArray) )
|
||||
{
|
||||
$nextItemArray[$keyPos]++;
|
||||
}
|
||||
}
|
||||
|
||||
# there is no next file or folder in this level, so walk up the hierarchy to the next folder or file
|
||||
while(!$item->nextItem)
|
||||
{
|
||||
# delete the array level with the current item, so you are in the parent folder
|
||||
array_pop($nextItemArray);
|
||||
|
||||
# if the array is empty now, then you where in the base level already, so break
|
||||
if(empty($nextItemArray)) break;
|
||||
|
||||
# define the key position where you are right now
|
||||
$newKeyPos = count($nextItemArray)-1;
|
||||
|
||||
# go to the next position
|
||||
$nextItemArray[$newKeyPos]++;
|
||||
|
||||
# search for 5 items in case there are some hidden elements
|
||||
$maxlength = $nextItemArray[$newKeyPos]+5;
|
||||
while( ($nextItemArray[$newKeyPos] <= $maxlength) && !$item->nextItem = $this->getItemWithKeyPath($navigation, $nextItemArray) )
|
||||
{
|
||||
$nextItemArray[$newKeyPos]++;
|
||||
}
|
||||
}
|
||||
|
||||
/************************
|
||||
* ADD PREVIOUS ITEM *
|
||||
************************/
|
||||
|
||||
# check if element is the first in the array
|
||||
$first = ($prevItemArray[$keyPos] == 0) ? true : false;
|
||||
|
||||
if(!$first)
|
||||
{
|
||||
$prevItemArray[$keyPos]--;
|
||||
|
||||
while($prevItemArray[$keyPos] >= 0 && !$item->prevItem = $this->getItemWithKeyPath($navigation, $prevItemArray))
|
||||
{
|
||||
$prevItemArray[$keyPos]--;
|
||||
}
|
||||
|
||||
# if no item is found, then all previous items are hidden, so set first item to true and it will walk up the array later
|
||||
if(!$item->prevItem)
|
||||
{
|
||||
$first = true;
|
||||
}
|
||||
elseif($item->prevItem && $item->prevItem->elementType == 'folder' && !empty($item->prevItem->folderContent))
|
||||
{
|
||||
# if the previous item is a folder, the get the last item of that folder
|
||||
$item->prevItem = $this->getLastItemOfFolder($item->prevItem);
|
||||
}
|
||||
}
|
||||
|
||||
# if it is the first item in the folder (or all other files are hidden)
|
||||
if($first)
|
||||
{
|
||||
# then the previous item is the containing chapter
|
||||
$item->prevItem = $item->thisChapter;
|
||||
}
|
||||
|
||||
if($item->prevItem && $item->prevItem->elementType == 'folder'){ unset($item->prevItem->folderContent); }
|
||||
if($item->nextItem && $item->nextItem->elementType == 'folder'){ unset($item->nextItem->folderContent); }
|
||||
if($item->thisChapter){unset($item->thisChapter->folderContent); }
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function getLastItemOfFolder($folder)
|
||||
{
|
||||
$lastItem = end($folder->folderContent);
|
||||
|
@ -156,6 +156,16 @@ class Settings
|
||||
return false;
|
||||
}
|
||||
|
||||
public function updateThemeCss(string $name, string $css)
|
||||
{
|
||||
if($this->storage->writeFile('cacheFolder', '', $name . '-custom.css', $css))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function array_is_list(array $arr)
|
||||
{
|
||||
if ($arr === [])
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% extends 'layouts/layoutAuth.twig' %}
|
||||
|
||||
{% block title %}{{ 'Setup'|translate }}{% endblock %}
|
||||
{% block title %}{{ translate('Setup') }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
</ul>
|
||||
{% else %}
|
||||
<h2 class="text-4xl py-5">{{ translate('Welcome to Typemill') }}</h2>
|
||||
<p>{{ translate('Hey writer, author, editor, content-guru, or website-manager.') }} {{ translate(' We hope you like Typemill, because we coded it just for you.'}}</p>
|
||||
<p>{{ translate('Hey writer, author, editor, content-guru, or website-manager.') }} {{ translate(' We hope you like Typemill, because we coded it just for you.') }}</p>
|
||||
<p>{{ translate('Get inspired and enjoy your writing') }}!</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -1022,10 +1022,6 @@ video {
|
||||
width: 33.333333%;
|
||||
}
|
||||
|
||||
.w-half {
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.w-80 {
|
||||
width: 20rem;
|
||||
}
|
||||
@ -1046,6 +1042,10 @@ video {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.w-half {
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.w-3\/4 {
|
||||
width: 75%;
|
||||
}
|
||||
@ -1144,10 +1144,6 @@ video {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
@ -2113,21 +2109,109 @@ video {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.dark\:text-gray-400 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(156 163 175 / var(--tw-text-opacity));
|
||||
}
|
||||
.dark .dark\:border-stone-900 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(28 25 23 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.sm\:table-row {
|
||||
display: table-row;
|
||||
}
|
||||
.dark .dark\:border-stone-200 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(231 229 228 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.sm\:flex-none {
|
||||
flex: none;
|
||||
}
|
||||
.dark .dark\:border-stone-600 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(87 83 78 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.dark .dark\:bg-stone-600 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(87 83 78 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark .dark\:bg-stone-700 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(68 64 60 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark .dark\:bg-stone-900 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(28 25 23 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark .dark\:bg-stone-200 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(231 229 228 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark .dark\:text-gray-400 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(156 163 175 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark .dark\:text-stone-50 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(250 250 249 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark .dark\:text-stone-200 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(231 229 228 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark .dark\:text-stone-900 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(28 25 23 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark .hover\:dark\:border-stone-600:hover {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(87 83 78 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.dark .hover\:dark\:border-teal-500:hover {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(20 184 166 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.dark .hover\:dark\:border-stone-200:hover {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(231 229 228 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.dark .hover\:dark\:bg-stone-900:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(28 25 23 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark .hover\:dark\:bg-stone-200:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(231 229 228 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark .hover\:dark\:text-stone-900:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(28 25 23 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark .focus\:dark\:border-stone-600:focus {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(87 83 78 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.dark .focus\:dark\:text-stone-900:focus {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(28 25 23 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark .active\:dark\:border-stone-600:active {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(87 83 78 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.dark .active\:dark\:text-stone-900:active {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(28 25 23 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
@ -2141,10 +2225,6 @@ video {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.lg\:mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.lg\:mt-0 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
@ -2165,24 +2245,20 @@ video {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lg\:h-12 {
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.lg\:w-1\/2 {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.lg\:w-1\/4 {
|
||||
width: 25%;
|
||||
.lg\:w-half {
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.lg\:w-3\/4 {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.lg\:w-half {
|
||||
width: 48%;
|
||||
.lg\:w-1\/4 {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.lg\:flex-row {
|
||||
|
@ -2,7 +2,7 @@ app.component('component-text', {
|
||||
props: ['id', 'description', 'maxlength', 'hidden', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'css', 'errors'],
|
||||
template: `<div :class="css ? css : ''" class="w-full mt-5 mb-5">
|
||||
<label :for="name" class="block mb-1 font-medium">{{ $filters.translate(label) }}</label>
|
||||
<input type="text" class="h-12 w-full border px-2 py-3" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
<input type="text" class="text-stone-900 h-12 w-full border px-2 py-3" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
:id="id"
|
||||
:maxlength="maxlength"
|
||||
:readonly="readonly"
|
||||
@ -28,7 +28,7 @@ app.component('component-textarea', {
|
||||
props: ['id', 'description', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'css', 'value', 'errors'],
|
||||
template: `<div :class="css ? css : ''" class="w-full mt-5 mb-5">
|
||||
<label :for="name" class="block mb-1 font-medium">{{ $filters.translate(label) }}</label>
|
||||
<textarea rows="8" class="w-full border border-stone-300 bg-stone-200 px-2 py-3"
|
||||
<textarea rows="8" class="w-full border border-stone-300 text-stone-900 bg-stone-200 px-2 py-3"
|
||||
:id="id"
|
||||
:class="css"
|
||||
:readonly="readonly"
|
||||
@ -128,7 +128,7 @@ app.component('component-select', {
|
||||
props: ['id', 'description', 'readonly', 'required', 'disabled', 'label', 'name', 'type', 'css', 'options', 'value', 'errors', 'dataset', 'userroles'],
|
||||
template: `<div :class="css ? css : ''" class="w-full mt-5 mb-5">
|
||||
<label :for="name" class="block mb-1 font-medium">{{ $filters.translate(label) }}</label>
|
||||
<select class="form-select block w-full border border-stone-300 bg-stone-200 px-2 py-3 h-12 transition ease-in-out"
|
||||
<select class="form-select block w-full border border-stone-300 text-stone-900 bg-stone-200 px-2 py-3 h-12 transition ease-in-out"
|
||||
:id="id"
|
||||
:name="name"
|
||||
:required="required"
|
||||
@ -258,7 +258,7 @@ app.component('component-number', {
|
||||
props: ['id', 'description', 'min', 'max', 'maxlength', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'css', 'value', 'errors'],
|
||||
template: `<div :class="css ? css : ''" class="w-full mt-5 mb-5">
|
||||
<label :for="name" class="block mb-1 font-medium">{{ $filters.translate(label) }}</label>
|
||||
<input type="number" class="h-12 w-full border border-stone-300 bg-stone-200 px-2 py-3"
|
||||
<input type="number" class="h-12 w-full border border-stone-300 text-stone-900 bg-stone-200 px-2 py-3"
|
||||
:id="id"
|
||||
:min="min"
|
||||
:max="max"
|
||||
@ -291,7 +291,7 @@ app.component('component-date', {
|
||||
<path fill-rule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="date" class="h-12 w-full border pl-10 pr-2 py-3" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
<input type="date" class="h-12 w-full border pl-10 pr-2 py-3 text-stone-900" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
:id="id"
|
||||
:readonly="readonly"
|
||||
:required="required"
|
||||
@ -323,7 +323,7 @@ app.component('component-email', {
|
||||
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="email" class="h-12 w-full border pl-10 pr-2 py-3" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
<input type="email" class="h-12 w-full border pl-10 pr-2 py-3 text-stone-900" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
:id="id"
|
||||
:maxlength="maxlength"
|
||||
:readonly="readonly"
|
||||
@ -355,7 +355,7 @@ app.component('component-tel', {
|
||||
<path d="M22 20c-2 2-2 4-4 4s-4-2-6-4-4-4-4-6 2-2 4-4-4-8-6-8-6 6-6 6c0 4 4.109 12.109 8 16s12 8 16 8c0 0 6-4 6-6s-6-8-8-6z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="tel" class="h-12 w-full border pl-10 pr-2 py-3" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
<input type="tel" class="h-12 w-full border pl-10 pr-2 py-3 text-stone-900" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
:id="id"
|
||||
:maxlength="maxlength"
|
||||
:readonly="readonly"
|
||||
@ -388,7 +388,7 @@ app.component('component-url', {
|
||||
<path d="M8 31.625c-2.037 0-3.952-0.793-5.392-2.233-2.973-2.973-2.973-7.81 0-10.783l2.743-2.743c0.635-0.635 1.664-0.635 2.298 0s0.635 1.663 0 2.298l-2.743 2.743c-1.706 1.706-1.706 4.481 0 6.187 0.826 0.826 1.925 1.281 3.094 1.281s2.267-0.455 3.094-1.281l6-6c1.706-1.706 1.706-4.481 0-6.187-0.635-0.635-0.635-1.663 0-2.298s1.663-0.635 2.298 0c2.973 2.973 2.973 7.81 0 10.783l-6 6c-1.44 1.44-3.355 2.233-5.392 2.233z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="url" class="h-12 w-full border pl-10 pr-2 py-3" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
<input type="url" class="h-12 w-full border pl-10 pr-2 py-3 text-stone-900" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
:id="id"
|
||||
:maxlength="maxlength"
|
||||
:readonly="readonly"
|
||||
@ -457,7 +457,7 @@ app.component('component-password', {
|
||||
<path d="M18.5 14h-0.5v-6c0-3.308-2.692-6-6-6h-4c-3.308 0-6 2.692-6 6v6h-0.5c-0.825 0-1.5 0.675-1.5 1.5v15c0 0.825 0.675 1.5 1.5 1.5h17c0.825 0 1.5-0.675 1.5-1.5v-15c0-0.825-0.675-1.5-1.5-1.5zM6 8c0-1.103 0.897-2 2-2h4c1.103 0 2 0.897 2 2v6h-8v-6z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input :type="fieldType" class="h-12 w-full border pl-10 pr-10 py-1" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
<input :type="fieldType" class="h-12 w-full border pl-10 pr-10 py-1 text-stone-900" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
:id="id"
|
||||
:maxlength="maxlength"
|
||||
:readonly="readonly"
|
||||
@ -582,7 +582,7 @@ app.component('component-customfields', {
|
||||
<input
|
||||
type="text"
|
||||
placeholder="key"
|
||||
class="h-12 w-full border px-2 py-3 border-stone-300 bg-stone-200"
|
||||
class="h-12 w-full border px-2 py-3 border-stone-300 bg-stone-200 text-stone-900"
|
||||
:class="pairobject.keyerror"
|
||||
:value="pairobject.key"
|
||||
@input="updatePairKey(pairindex,$event)">
|
||||
@ -594,7 +594,7 @@ app.component('component-customfields', {
|
||||
</svg>
|
||||
<textarea
|
||||
placeholder="value"
|
||||
class="w-full border px-2 py-3 border-stone-300 bg-stone-200"
|
||||
class="w-full border px-2 py-3 border-stone-300 bg-stone-200 text-stone-900"
|
||||
:class="pairobject.valueerror"
|
||||
v-html="pairobject.value"
|
||||
@input="updatePairValue(pairindex,$event)"></textarea>
|
||||
@ -751,7 +751,7 @@ app.component('component-image', {
|
||||
<label class="block mb-1">{{ $filters.translate('Image URL (read only)') }}</label>
|
||||
<div class="flex">
|
||||
<button @click.prevent="deleteImage()" class="w-1/6 bg-stone-200 hover:bg-rose-500 hover:text-white">x</button>
|
||||
<input type="text" class="h-12 w-5/6 border px-1 py-1" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
<input type="text" class="h-12 w-5/6 border px-1 py-1 text-stone-900" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
:id="id"
|
||||
:maxlength="maxlength"
|
||||
readonly="readonly"
|
||||
|
@ -34,12 +34,12 @@ const app = Vue.createApp({
|
||||
<div class="w-half border-2 border-stone-200 p-4 my-8 text-center">
|
||||
<h2 class="text-3 font-bold mb-4">Maker License</h2>
|
||||
<p class="mb-4">Use all maker-prodcuts (plugins and themes) for one year. The subscription will automatically refresh after a year until you cancel it.</p>
|
||||
<a href="https://typemill.net/buy">Buy on Typemill</a>
|
||||
<a class="p-2 block dark:bg-stone-600 hover:dark:bg-stone-900 dark:text-stone-200" href="https://typemill.net/buy">Buy on Typemill</a>
|
||||
</div>
|
||||
<div class="w-half border-2 border-stone-200 p-4 my-8 text-center">
|
||||
<h2 class="text-3 font-bold mb-4">Business License</h2>
|
||||
<p class="mb-4">Use all business- and maker-products (plugins, themes, services) for one year. The subscription will automatically refresh after a year until you cancel it.</p>
|
||||
<a href="https://typemill.net/buy">Buy on Typemill</a>
|
||||
<a class="p-2 block dark:bg-stone-600 hover:dark:bg-stone-900 dark:text-stone-200" href="https://typemill.net/buy">Buy on Typemill</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,7 +2,7 @@ const app = Vue.createApp({
|
||||
template: `<Transition name="initial" appear>
|
||||
<div class="w-full">
|
||||
<ul>
|
||||
<li v-for="(plugin,pluginname) in formDefinitions" class="w-full my-4 bg-stone-100">
|
||||
<li v-for="(plugin,pluginname) in formDefinitions" class="w-full my-8 bg-stone-100 border border-stone-200">
|
||||
<p v-if="versions[pluginname] !== undefined"><a href="https://plugins.typemill.net" class="block p-2 text-center bg-rose-500 text-white">Please update to version {{ versions[pluginname].version }}</a></p>
|
||||
<div class="flex justify-between w-full px-8 py-3 border-b border-white" :class="getActiveClass(pluginname)">
|
||||
<p class="py-2">License: {{ plugin.license }}</p>
|
||||
@ -21,7 +21,7 @@ const app = Vue.createApp({
|
||||
<p>{{plugin.description}}</p>
|
||||
</div>
|
||||
<div class="w-full mt-6 flex justify-between">
|
||||
<button @click="setCurrent(pluginname)" class="flex-1 flex items-center justify-center space-x-4 p-3 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">
|
||||
<button v-if="hasSettings(pluginname)" @click="setCurrent(pluginname)" class="flex-1 flex items-center justify-center space-x-4 p-3 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">
|
||||
<span>{{ $filters.translate('Configure') }}</span>
|
||||
<span :class="(current == pluginname) ? 'border-b-8 border-b-white' : 'border-t-8 border-t-white'" class="h-0 w-0 border-x-8 border-x-transparent"></span>
|
||||
</button>
|
||||
@ -133,18 +133,18 @@ const app = Vue.createApp({
|
||||
|
||||
},
|
||||
methods: {
|
||||
getActiveClass: function(pluginname)
|
||||
getActiveClass(pluginname)
|
||||
{
|
||||
if(this.formData[pluginname]['active'])
|
||||
{
|
||||
return 'bg-stone-200';
|
||||
}
|
||||
},
|
||||
getLinkToLicense: function()
|
||||
getLinkToLicense()
|
||||
{
|
||||
return tmaxios.defaults.baseURL + "/tm/license";
|
||||
},
|
||||
checkLicense: function(haystack, needle)
|
||||
checkLicense(haystack, needle)
|
||||
{
|
||||
if(needle == 'MAKER' || needle == 'BUSINESS')
|
||||
{
|
||||
@ -155,7 +155,7 @@ const app = Vue.createApp({
|
||||
}
|
||||
return true;
|
||||
},
|
||||
activate: function(pluginname)
|
||||
activate(pluginname)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
@ -178,7 +178,15 @@ const app = Vue.createApp({
|
||||
}
|
||||
});
|
||||
},
|
||||
setCurrent: function(name)
|
||||
hasSettings(pluginname)
|
||||
{
|
||||
if(this.formDefinitions[pluginname].forms !== undefined)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
setCurrent(name)
|
||||
{
|
||||
if(this.current == name)
|
||||
{
|
||||
@ -189,11 +197,11 @@ const app = Vue.createApp({
|
||||
this.current = name;
|
||||
}
|
||||
},
|
||||
selectComponent: function(type)
|
||||
selectComponent(type)
|
||||
{
|
||||
return 'component-'+type;
|
||||
},
|
||||
save: function()
|
||||
save()
|
||||
{
|
||||
this.reset();
|
||||
var self = this;
|
||||
@ -220,7 +228,7 @@ const app = Vue.createApp({
|
||||
}
|
||||
});
|
||||
},
|
||||
reset: function()
|
||||
reset()
|
||||
{
|
||||
this.errors = {};
|
||||
this.message = '';
|
||||
|
@ -4,7 +4,7 @@ const app = Vue.createApp({
|
||||
<p v-if="version.system !== undefined"><a href="https://typemill.net" class="block p-2 text-center bg-rose-500 text-white">Please update typemill to version {{ version.system }}</a></p>
|
||||
<ul class="flex flex-wrap mt-4 mb-4">
|
||||
<li v-for="tab in tabs">
|
||||
<button class="px-2 py-2 border-b-2 border-stone-200 hover:border-stone-700 transition duration-100" :class="(tab == currentTab) ? 'border-stone-700' : ''" @click.prevent="activateTab(tab)">{{ $filters.translate(tab) }}</button>
|
||||
<button class="px-2 py-2 border-b-2 border-stone-200 dark:border-stone-900 hover:border-stone-700 hover:dark:border-stone-200 transition duration-100" :class="(tab == currentTab) ? 'border-stone-700 dark:border-stone-200' : ''" @click.prevent="activateTab(tab)">{{ $filters.translate(tab) }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-for="(fieldDefinition, fieldname) in formDefinitions">
|
||||
@ -22,7 +22,7 @@ const app = Vue.createApp({
|
||||
</div>
|
||||
<div class="my-5">
|
||||
<div :class="messageClass" class="block w-full h-8 px-3 py-1 my-1 text-white transition duration-100">{{ $filters.translate(message) }}</div>
|
||||
<input type="submit" @click.prevent="save()" :value="$filters.translate('save')" class="w-full p-3 my-1 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">
|
||||
<input type="submit" @click.prevent="save()" :value="$filters.translate('save')" class="w-full p-3 my-1 dark:bg-stone-600 hover:dark:bg-stone-900 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">
|
||||
</div>
|
||||
</form>
|
||||
</Transition>`,
|
||||
|
@ -1,13 +1,13 @@
|
||||
const systemnavi = Vue.createApp({
|
||||
template: `
|
||||
<ul class="lg:mr-2 border-l-2 border-stone-200">
|
||||
<ul class="lg:mr-2">
|
||||
<button @click="toggle" class="lg:hidden w-full flex-1 flex items-center justify-center space-x-4 p-2 mb-2 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">
|
||||
<span>{{ $filters.translate('Menu') }}</span>
|
||||
<span :class="expanded ? 'border-b-8 border-b-white' : 'border-t-8 border-t-white'" class="h-0 w-0 border-x-8 border-x-transparent"></span>
|
||||
</button>
|
||||
<div class="lg:block" :class="expanded ? '' : 'hidden'">
|
||||
<li v-for="(navitem, name) in systemnavi" :key="name" class="mb-1">
|
||||
<a :href="navitem.url" class="block p-2 border-l-4 hover:bg-stone-50 hover:border-teal-500 transition duration-100" :class="navitem.active ? ' active bg-stone-50 border-cyan-500' : ' border-slate-200'">
|
||||
<a :href="navitem.url" class="block p-2 border-l-4 hover:bg-stone-50 hover:border-teal-500 dark:border-stone-200 dark:bg-stone-700 hover:dark:bg-stone-200 dark:text-stone-50 hover:dark:text-stone-900 transition duration-100" :class="navitem.active ? ' active bg-stone-50 border-cyan-500 dark:bg-stone-200 dark:text-stone-900' : ' border-slate-200'">
|
||||
<svg class="icon {{ navitem.icon }} mr-2"><use xlink:href="#{{ navitem.icon }}"></use></svg> {{ $filters.translate(navitem.title) }}
|
||||
</a>
|
||||
</li>
|
||||
|
@ -2,7 +2,7 @@ const app = Vue.createApp({
|
||||
template: `<Transition name="initial" appear>
|
||||
<div class="w-full">
|
||||
<ul>
|
||||
<li v-for="(theme,themename) in formDefinitions" class="w-full my-4 bg-stone-100">
|
||||
<li v-for="(theme,themename) in formDefinitions" class="w-full my-8 bg-stone-100 dark:bg-stone-600 border border-stone-200">
|
||||
<p v-if="versions[themename] !== undefined"><a href="https://themes.typemill.net" class="block p-2 text-center bg-rose-500 text-white">Please update to version {{ versions[themename].version }}</a></p>
|
||||
<div class="flex justify-between w-full px-8 py-3 border-b border-white" :class="getActiveClass(themename)">
|
||||
<p class="py-2">License: {{ theme.license }}</p>
|
||||
@ -22,7 +22,7 @@ const app = Vue.createApp({
|
||||
<p>{{theme.description}}</p>
|
||||
</div>
|
||||
<div class="lg:w-1/2 w-full h-48 overflow-hidden">
|
||||
<img :src="theme.preview" class="w-full">
|
||||
<img :src="getSrc(theme.preview)" class="w-full">
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full mt-6 flex justify-between">
|
||||
@ -140,24 +140,28 @@ const app = Vue.createApp({
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
deactivateThemes: function()
|
||||
deactivateThemes()
|
||||
{
|
||||
for (const theme in this.formData) {
|
||||
delete this.formData[theme].active;
|
||||
}
|
||||
},
|
||||
getActiveClass: function(themename)
|
||||
getActiveClass(themename)
|
||||
{
|
||||
if(this.formData[themename]['active'])
|
||||
{
|
||||
return 'bg-stone-200';
|
||||
return 'bg-stone-200 dark:bg-stone-900';
|
||||
}
|
||||
},
|
||||
getLinkToLicense: function()
|
||||
getSrc(preview)
|
||||
{
|
||||
return data.urlinfo.baseurl + preview;
|
||||
},
|
||||
getLinkToLicense()
|
||||
{
|
||||
return tmaxios.defaults.baseURL + "/tm/license";
|
||||
},
|
||||
checkLicense: function(haystack, needle)
|
||||
checkLicense(haystack, needle)
|
||||
{
|
||||
if(needle == 'MAKER' || needle == 'BUSINESS')
|
||||
{
|
||||
@ -168,7 +172,7 @@ const app = Vue.createApp({
|
||||
}
|
||||
return true;
|
||||
},
|
||||
activate: function(themename)
|
||||
activate(themename)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
@ -193,7 +197,7 @@ const app = Vue.createApp({
|
||||
}
|
||||
});
|
||||
},
|
||||
setCurrent: function(name)
|
||||
setCurrent(name)
|
||||
{
|
||||
if(this.current == name)
|
||||
{
|
||||
@ -204,13 +208,14 @@ const app = Vue.createApp({
|
||||
this.current = name;
|
||||
}
|
||||
},
|
||||
selectComponent: function(type)
|
||||
selectComponent(type)
|
||||
{
|
||||
return 'component-'+type;
|
||||
},
|
||||
save: function()
|
||||
save()
|
||||
{
|
||||
this.reset();
|
||||
|
||||
var self = this;
|
||||
|
||||
tmaxios.post('/api/v1/theme',{
|
||||
@ -235,14 +240,35 @@ const app = Vue.createApp({
|
||||
self.errors = error.response.data.errors;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
updateCSS: function()
|
||||
updateCSS()
|
||||
{
|
||||
/* check if css has been modified */
|
||||
/* if so, send to api endpoint */
|
||||
var selfcss = this;
|
||||
|
||||
tmaxios.post('/api/v1/themecss',{
|
||||
'theme': this.current,
|
||||
'css': this.formData[this.current].customcss
|
||||
})
|
||||
.then(function (response)
|
||||
{
|
||||
self.messageClass = 'bg-teal-500';
|
||||
self.message = response.data.message;
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
self.message = handleErrorMessage(error);
|
||||
self.messageClass = 'bg-rose-500';
|
||||
if(error.response.data.errors !== undefined)
|
||||
{
|
||||
self.errors = error.response.data.errors;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
reset: function()
|
||||
reset()
|
||||
{
|
||||
this.errors = {};
|
||||
this.message = '';
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="{{ settings.darkmode ? 'dark' :'darkmodeoff' }}" >
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="{{ settings.darkmode ? 'dark' :'darkmodeoff' }}" >
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="{{ settings.darkmode ? 'dark' :'darkmodeoff' }}" >
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
@ -24,11 +24,11 @@
|
||||
{{ assets.renderCSS() }}
|
||||
|
||||
</head>
|
||||
<body class="bg-stone-100">
|
||||
<body class="bg-stone-100 dark:bg-stone-900">
|
||||
|
||||
{% include 'partials/symbols.twig' %}
|
||||
|
||||
<header class="border-b-2 border-stone-200">
|
||||
<header class="border-b-2 border-stone-200 dark:border-stone-600">
|
||||
{% include 'partials/mainNavi.twig' %}
|
||||
</header>
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
<aside class="lg:w-1/4">
|
||||
{% include 'partials/systemNavi.twig' %}
|
||||
</aside>
|
||||
<article class="lg:w-3/4 bg-stone-50 shadow-md p-8">
|
||||
<article class="lg:w-3/4 bg-stone-50 dark:bg-stone-700 dark:text-stone-200 shadow-md p-8">
|
||||
{% block content %}{% endblock %}
|
||||
</article>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="{{ settings.darkmode ? 'dark' :'darkmodeoff' }}" >
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
|
@ -1,13 +1,19 @@
|
||||
<nav class="max-w-6xl m-auto flex justify-between">
|
||||
<div class="lg:p-3 p-2 flex">
|
||||
<a class="text-2xl font-bold" href="{{ base_url() }}/tm/content/{{ settings.editor }}">T<span class="lg:inline hidden">ypemill</span></a>
|
||||
<a class="text-2xl font-bold dark:text-stone-200" href="{{ base_url() }}/tm/content/{{ settings.editor }}">T<span class="lg:inline hidden">ypemill</span></a>
|
||||
<div id="kixote"></div>
|
||||
</div>
|
||||
<ul class="flex border-l-2 border-stone-200">
|
||||
<ul class="flex border-l-2 border-stone-200 dark:border-stone-600">
|
||||
{% for name,navitem in mainnavi %}
|
||||
<li class="border-r-2 border-stone-200">
|
||||
<a class="inline-block lg:px-4 lg:pt-4 lg:pb-3 lg:border-b-4 px-3 pt-3 pb-3 border-b-2 hover:border-stone-700 hover:bg-stone-50 focus:bg-stone-50 active:bg-stone-50 transition duration-100{{ navitem.active ? ' bg-stone-50 border-stone-700' : ' border-stone-100' }}" href="{{ url_for(navitem.routename) }}">{{ translate(navitem.title)|capitalize }}</a>
|
||||
</li>
|
||||
{% if settings.darkmode %}
|
||||
<li class="border-r-2 border-stone-200 dark:border-stone-600">
|
||||
<a class="inline-block lg:px-4 lg:pt-4 lg:pb-3 lg:border-b-4 px-3 pt-3 pb-3 border-b-2 dark:text-stone-200 hover:bg-stone-50 hover:dark:bg-stone-200 hover:dark:text-stone-900 focus:dark:text-stone-900 active:dark:text-stone-900 hover:dark:border-stone-600 focus:dark:border-stone-600 active:dark:border-stone-600 transition duration-100{{ navitem.active ? ' dark:bg-stone-200 dark:text-stone-900 dark:border-stone-600' : ' dark:border-stone-900' }}" href="{{ url_for(navitem.routename) }}">{{ translate(navitem.title)|capitalize }}</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="border-r-2 border-stone-200 dark:border-stone-600">
|
||||
<a class="inline-block lg:px-4 lg:pt-4 lg:pb-3 lg:border-b-4 px-3 pt-3 pb-3 border-b-2 hover:border-stone-700 hover:bg-stone-50 focus:bg-stone-50 active:bg-stone-50 transition duration-100{{ navitem.active ? ' bg-stone-50 border-stone-700' : ' border-stone-100' }}" href="{{ url_for(navitem.routename) }}">{{ translate(navitem.title)|capitalize }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
@ -29,6 +29,7 @@ $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) {
|
||||
$group->get('/settings', ControllerApiSystemSettings::class . ':getSettings')->setName('api.settings.get')->add(new ApiAuthorization($acl, 'system', 'view')); # admin
|
||||
$group->post('/settings', ControllerApiSystemSettings::class . ':updateSettings')->setName('api.settings.set')->add(new ApiAuthorization($acl, 'system', 'update')); # admin
|
||||
$group->post('/license', ControllerApiSystemLicense::class . ':createLicense')->setName('api.license.create')->add(new ApiAuthorization($acl, 'system', 'update')); # admin
|
||||
$group->post('/themecss', ControllerApiSystemThemes::class . ':updateThemeCss')->setName('api.themecss.set')->add(new ApiAuthorization($acl, 'system', 'update')); # admin
|
||||
$group->post('/theme', ControllerApiSystemThemes::class . ':updateTheme')->setName('api.theme.set')->add(new ApiAuthorization($acl, 'system', 'update')); # admin
|
||||
$group->post('/plugin', ControllerApiSystemPlugins::class . ':updatePlugin')->setName('api.plugin.set')->add(new ApiAuthorization($acl, 'system', 'update')); # admin
|
||||
$group->post('/extensions', ControllerApiSystemExtensions::class . ':activateExtension')->setName('api.extension.activate')->add(new ApiAuthorization($acl, 'system', 'update')); # admin
|
||||
|
@ -53,6 +53,11 @@ fieldsetsystem:
|
||||
label: Google sitemap (readonly)
|
||||
css: lg:w-half
|
||||
disabled: true
|
||||
# darkmode:
|
||||
# type: checkbox
|
||||
# label: Darkmode
|
||||
# checkboxlabel: Switch to darkmode for the authoring area
|
||||
# css: lg:w-half
|
||||
fieldsetmedia:
|
||||
type: fieldset
|
||||
legend: Media
|
||||
@ -200,18 +205,10 @@ fieldsetdeveloper:
|
||||
type: checkbox
|
||||
label: Twig cache
|
||||
checkboxlabel: Activate the cache for twig templates
|
||||
refreshcache:
|
||||
type: checkbox
|
||||
label: Refresh cache
|
||||
checkboxlabel: Refresh the cache every 10 minutes. Use this option if you change content-files via FTP.
|
||||
proxy:
|
||||
type: checkbox
|
||||
label: Proxy
|
||||
checkboxlabel: Use x-forwarded-header.
|
||||
trustedproxies:
|
||||
type: text
|
||||
label: Trusted IPs for proxies (comma separated)
|
||||
headersoff:
|
||||
type: checkbox
|
||||
label: Disable headers
|
||||
checkboxlabel: Disable the typemill headers and send your owwn
|
||||
label: Trusted IPs for proxies (comma separated)
|
@ -43,8 +43,8 @@ $timer['start'] = microtime(true);
|
||||
* HIDE ERRORS BY DEFAULT *
|
||||
****************************/
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
ini_set('display_errors', 0);
|
||||
ini_set('display_startup_errors', 0);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
/****************************
|
||||
|
@ -1,6 +1,7 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./system/typemill/author/**/*.{html,js,twig}"],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
screens: {
|
||||
|
Reference in New Issue
Block a user