1
0
mirror of https://github.com/typemill/typemill.git synced 2025-08-06 14:16:46 +02:00

Last fixes official release

This commit is contained in:
trendschau
2023-10-29 16:12:36 +01:00
parent 6c6e7b1a92
commit 66962e0b14
79 changed files with 871 additions and 587 deletions

View File

@@ -1,30 +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;
.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;
}

View File

@@ -3,6 +3,8 @@ meta:
owner: Sebastian
created: '2023-06-12'
time: 22-36-14
modified: '2023-03-26'
title: 'Typemill provides detailed settings, and you have access to nearly all settings in the author panel. Learn the basics in this short video:'
modified: '2023-10-23'
title: 'Setup your website'
description: ' You will find all configurations and settings under the main navigation point settings with the following sub-navigation:'
hide: false
noindex: false

View File

@@ -1,8 +1,16 @@
meta:
navtitle: 'manage access'
title: 'Manage access'
description: ' Restrict Access for the Website'
heroimage: null
heroimagealt: null
owner: Sebastian
author: null
allowedrole: null
alloweduser: null
manualdate: null
modified: '2023-05-06'
created: '2023-06-12'
time: 22-36-36
modified: '2023-05-06'
title: 'Typemill has a build-in system to restrict access to pages or to the whole websites. You can activate both features in the system settings under the section "access rights". If you activate one of the features, then Typemill will use session cookies on all frontend pages. Learn all the details in the following video tutorial:'
description: ' Restrict Access for the Website'
hide: false
noindex: false

View File

@@ -1,8 +1,16 @@
meta:
navtitle: 'get help'
title: 'get help'
description: 'If you found a bug or if you have a question, then please open a new issue on GitHub. Do you need professional help, an individual theme or a special plugin?'
heroimage: null
heroimagealt: null
owner: Sebastian
author: null
allowedrole: null
alloweduser: null
manualdate: null
modified: '2023-05-13'
created: '2023-06-12'
time: 22-36-34
modified: '2023-05-13'
title: 'If you need any help, then please read the [documentation on typemill.net](https://typemill.net/typemill) first. You can also check these [video-tutorials](https://www.youtube.com/channel/UCyghKiX2kK9QIqTf1WT1Xxw) about the basics to create a typemill website.'
description: 'If you found a bug or if you have a question, then please open a new issue on GitHub. Do you need professional help, an individual theme or a special plugin?'
hide: false
noindex: false

View File

@@ -28,6 +28,7 @@ Headlines are simply done with hash chars like this:
### Third Level Headline
A third headline is more decent and lower prioritized than a second level headline.
@@ -55,6 +56,7 @@ A paragraph is a simple text-block separated with a new line above and below.
A paragraph is a simple text-block separated with a new line above and below.
## Soft Linebreak
@@ -70,6 +72,7 @@ He said: "again"
For a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return.
She said: "Hello"
@@ -87,6 +90,7 @@ For bold text use two **asterix** or two __underscores__.
For italic text use one *asterix* or one _underscore_.
For bold text use two **asterix** or two __underscores__.
@@ -110,6 +114,7 @@ For an ordered list use whatever number you want and add a dot:
For an unordered list use a dash
* like
@@ -137,6 +142,7 @@ Easily created for example with three dashes like this:
Easily created for example with three dashes like this:
---
@@ -156,6 +162,7 @@ You can even use a download-link like []()
This is an ordinary [Link](http://typemill.net).
Links can also be [relative](/info).
@@ -186,6 +193,7 @@ The same rules as with links, but with a !
The same rules as with links, but with a !
![](media/tmp/kai-dahms-v0nbnxlwjzk-3unsp-1.webp)
@@ -207,6 +215,7 @@ You can link an image with a nested syntax like this:
You can link an image with a nested syntax like this:
[![alt-text](media/files/markdown.png){.imgClass}](https://typemill.net)
@@ -228,6 +237,7 @@ You can controll the image position with the classes .left, .right and .middle l
![](media/tmp/kai-dahms-v0nbnxlwjzk-3unsp-1.webp)
The first image should float on the left side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "center". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog). Images in a separate line are rendered with the html5 elements `figure` and `figcapture`.
@@ -254,6 +264,7 @@ There are always some women and men with wise words
There always some women and men with wise words
> But I usually don't read them, to be honest.
@@ -273,6 +284,7 @@ Add the footnote text at the bottom of the page like this:
You can write footnotes[^1] with markdown.
Scroll down to the end of the page[^2] and look for the footnotes.
@@ -291,6 +303,7 @@ Footnotes won't work with the visual editor right now, so please use the raw mod
You won't see the abbreviation directly, but if you write HTML or W3C somewhere, then you can see the tooltip with the explanation.
*[HTML]: Hyper Text Markup Language
@@ -311,6 +324,7 @@ Orange
Apple
: Pomaceous fruit of plants of the genus Malus in
the family Rosaceae.
@@ -322,18 +336,18 @@ Orange
You can create different notices if you add a '!', '!!', '!!!', '!!!!' before a line. This will wrap the content into a div-class with the classes `notice1`, `notice2`, `notice3` and `notice4`. You can also span notices over several lines. This logic follows some other CMS like Grav, Lektor or Yellow and it is not compatible with other markdown processors or editors.
> ! Notice 1
> !
> ! Please note that you can use **markdown** inside of the notice so you can *format* your text here.
Notice 2
Please note that you can use **markdown** inside of the notice so you can *format* your text here.
! **Notice 3**
! Notice 1
!
! Please note that you can use **markdown** inside of the notice so you can *format* your text here.
!! **Notice 2**
!!
!! Please note that you can use **markdown** inside of the notice so you can *format* your text here.
!!! **Notice 3**
!!!
!!! Please note that you can use **markdown** inside of the notice so you can *format* your text here.
## Tables
````
@@ -348,6 +362,7 @@ Please note that you can use **markdown** inside of the notice so you can *forma
| Name | Usage |
|---|---|
| My Name | For Me |
@@ -364,6 +379,7 @@ Let us create some `<?php inlineCode(); ?>` like this
Let us create some `<?php inlineCode(); ?>` and now let us check, if a codeblock works:
````
@@ -381,6 +397,7 @@ Use four apostroph like this:
## Math
Please activate the math-plugin to use mathematical expressions with LaTeX syntax. You can choose between MathJax or the newer KaTeX library. MathJax is included from a CDN, KaTeX is included in the plugin. So if you don't want to fetch code from a CDN, use KaTeX instead. The markdown syntax in TYPEMILL is the same for both libraries.
@@ -396,6 +413,7 @@ inline \(x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)\) math
inline $x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)$ math
inline \(x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)\) math
@@ -415,6 +433,7 @@ x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)
$$
x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)
$$

View File

@@ -105,18 +105,45 @@
* 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
* DONE: Typemill Utilities
* DONE: Update CSS for themes
* DONE: test with different user rights
* Markdown secure rendering
* finish youtube component
* Handle formdata centrally ???
* BUG: Error fields in account form not styled correctly
* DONE: Update CSS for themes
* BUG: Codefield jumps on editing
* False for owner on live?
## Dark Mode
* DONE: system
* DONE: content-navigation
* DONE: visual editor preview
* DONE: visual editor edit modes
* DONE: raw editor
* DONE: meta
* DONE: other tabs
* DONE: modals
* DONE: medialib
* DONE: publish-bar.
## Feedback GitHub
* FIXED: Website restriction
* NO ERROR: Change slug of blog
* FIXED: undefined array key "title" in TwigMetaExtension on line 25
* FIXED: CSS for navigation
* DONE: Test with 8.2.7 (deprecation reports)
* NOT REPRODUCED: Meta from home folder?
* automatic generated password in firefox
* FIXED: upload hero image in landinpage
* FIXED: Restriction for custom css to 10000 characters
* NOT REPRODUCED: Custom css löschen => false
## later
* Handle formdata centrally ???
* Reference Feature
* Clear cache
* Show security Log
* User search only for +10 users
@@ -161,7 +188,8 @@
## Upgrade
* Delete content of system folder
* Switch server to php 8.0 at least
* Delete content of system folders
* Upload new content of system folder with folders typemill and vendor
* Backup and delete settings file
* upload new index.php file

View File

@@ -2,17 +2,13 @@ meta:
navtitle: 'To Dos'
title: 'A list of open tasks'
description: ' Visual Editor with more stuff'
heroimage: null
heroimagealt: null
owner: Sebastian
author: null
heroimage: media/live/screenshot-2023-08-03-at-15-23-44-setup-3.png
heroimagealt: ''
owner: 'Sebastian, testauthor'
author: ''
allowedrole: ''
alloweduser: null
manualdate: null
modified: '2023-06-19'
created: '2023-06-19'
time: 15-15-01
reference: null
referencetype: null
hide: false
noindex: false

File diff suppressed because one or more lines are too long

View File

@@ -32,13 +32,13 @@
navtitle: 'markdown test'
hide: false
noindex: false
path: /00-welcome/05-markdown-test.md
path: /00-welcome/04-markdown-test.md
keyPath: '0.4'
/welcome/todos:
/welcome/todoss:
navtitle: 'To Dos'
hide: false
noindex: false
path: /00-welcome/06-todos.md
path: /00-welcome/05-todoss.md
keyPath: '0.5'
/cyanine-theme:
navtitle: 'cyanine theme'

View File

@@ -11,3 +11,8 @@
127.0.0.1;2023-09-15 06:06:46;wrong login
127.0.0.1;2023-09-15 22:03:38;wrong login
127.0.0.1;2023-09-16 08:49:53;wrong login
127.0.0.1;2023-10-23 20:42:13;wrong login
127.0.0.1;2023-10-23 21:26:26;wrong login
127.0.0.1;2023-10-24 21:52:19;wrong login
127.0.0.1;2023-10-26 20:57:09;wrong login
127.0.0.1;2023-10-28 12:18:34;wrong login

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -141,8 +141,7 @@ abstract class Controller
return $formDefinitions;
}
# used to protect api access, can we do it with middleware?
protected function validateRights($userrole, $resource, $action)
protected function userroleIsAllowed($userrole, $resource, $action)
{
$acl = $this->c->get('acl');
@@ -151,10 +150,45 @@ abstract class Controller
return true;
}
die("PLEASE UPDATE THE METHOD validateRights in controller.php");
return false;
}
protected function userIsAllowed($username, $pagemeta)
{
if(
isset($pagemeta['meta']['owner']) &&
$pagemeta['meta']['owner'] &&
$pagemeta['meta']['owner'] !== ''
)
{
$allowedusers = array_map('trim', explode(",", $pagemeta['meta']['owner']));
if(
in_array($username, $allowedusers)
)
{
return true;
}
}
return false;
}
# used to protect api access, can we do it with middleware?
protected function validateRights($userrole, $resource, $action)
{
# check ownership. THIS WILL FAIL ANYWAY!!!
# MAYBE WE SHOUD ADD THIS CHECK INTO MIDDLEWARE, TOO ?
$acl = $this->c->get('acl');
if($acl->isAllowed($userrole, $resource, $action))
{
return true;
}
die("PLEASE UPDATE THE METHOD validateRights in controller.php");
$writeMeta = new writeMeta();
$pagemeta = $writeMeta->getPageMeta($this->settings, $this->item);

View File

@@ -23,16 +23,6 @@ class ControllerApiAuthorArticle extends Controller
{
public function publishArticle(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->articlePublish($params);
@@ -59,6 +49,22 @@ class ControllerApiAuthorArticle extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'publish'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
# publish content
$content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher'));
$draftMarkdown = $content->getDraftMarkdown($item);
@@ -106,16 +112,6 @@ class ControllerApiAuthorArticle extends Controller
public function unpublishArticle(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->articlePublish($params);
@@ -142,6 +138,22 @@ class ControllerApiAuthorArticle extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'publish'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
# publish content
$content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher'));
$draftMarkdown = $content->getDraftMarkdown($item);
@@ -183,16 +195,6 @@ class ControllerApiAuthorArticle extends Controller
public function updateDraft(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'mycontent', 'edit');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(401);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->articleUpdate($params);
@@ -219,6 +221,22 @@ class ControllerApiAuthorArticle extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
# save draft content
$content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher'));
$markdown = $params['title'] . PHP_EOL . PHP_EOL . $params['body'];
@@ -246,16 +264,6 @@ class ControllerApiAuthorArticle extends Controller
public function publishDraft(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'mycontent', 'edit');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(401);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->articleUpdate($params);
@@ -282,6 +290,22 @@ class ControllerApiAuthorArticle extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
# save draft content
$content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher'));
$markdown = $params['title'] . PHP_EOL . PHP_EOL . $params['body'];
@@ -326,16 +350,6 @@ class ControllerApiAuthorArticle extends Controller
public function discardArticleChanges(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'mycontent', 'edit');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->articlePublish($params);
@@ -362,6 +376,22 @@ class ControllerApiAuthorArticle extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
# publish content
$content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher'));
$content->deleteDraft($item);
@@ -387,16 +417,6 @@ class ControllerApiAuthorArticle extends Controller
public function createArticle(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'mycontent', 'create');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->navigationItem($params);
@@ -495,7 +515,13 @@ class ControllerApiAuthorArticle extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(500);
}
$storage->updateYaml('contentFolder', $folderPath, $namePath . '.yaml', ['meta' => ['navtitle' => $params['item_name']]]);
$storage->updateYaml('contentFolder', $folderPath, $namePath . '.yaml',
['meta' => [
'navtitle' => $params['item_name'],
'owner' => $request->getAttribute('c_username'),
'created' => date("Y-m-d"),
'time' => date("H-i-s")
]]);
}
elseif($params['type'] == 'folder')
{
@@ -508,7 +534,13 @@ class ControllerApiAuthorArticle extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(500);
}
$storage->writeFile('contentFolder', $folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content);
$storage->updateYaml('contentFolder', $folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.yaml', ['meta' => ['navtitle' => $params['item_name']]]);
$storage->updateYaml('contentFolder', $folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.yaml',
['meta' => [
'navtitle' => $params['item_name'],
'owner' => $request->getAttribute('c_username'),
'created' => date("Y-m-d"),
'time' => date("H-i-s")
]]);
# always redirect to a folder
# $url = $urlinfo['baseurl'] . '/tm/content/' . $this->settings['editor'] . $folder->urlRelWoF . '/' . $slug;
@@ -527,16 +559,6 @@ class ControllerApiAuthorArticle extends Controller
public function createPost(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'mycontent', 'create');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->navigationItem($params);
@@ -619,7 +641,13 @@ class ControllerApiAuthorArticle extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(500);
}
$storage->updateYaml('contentFolder', $folderPath, $namePath . '.yaml', ['meta' => ['navtitle' => $params['item_name']]]);
$storage->updateYaml('contentFolder', $folderPath, $namePath . '.yaml',
['meta' => [
'navtitle' => $params['item_name'],
'owner' => $request->getAttribute('c_username'),
'created' => date("Y-m-d"),
'time' => date("H-i-s")
]]);
}
elseif($params['type'] == 'folder')
{
@@ -644,16 +672,6 @@ class ControllerApiAuthorArticle extends Controller
public function renameArticle(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'mycontent', 'edit');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->articleRename($params);
@@ -680,6 +698,22 @@ class ControllerApiAuthorArticle extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
# check if name exists
$parentUrl = str_replace($item->slug, '', $item->urlRelWoF);
if($parentUrl == '/')
@@ -732,16 +766,6 @@ class ControllerApiAuthorArticle extends Controller
public function sortArticle(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->navigationSort($params);
@@ -768,6 +792,31 @@ class ControllerApiAuthorArticle extends Controller
$navigation = new Navigation();
$draftNavigation = $navigation->getDraftNavigation($urlinfo, $langattr);
$item = $navigation->getItemWithKeyPath($draftNavigation, $itemKeyPath);
if(!$item)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('page not found'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
$extendedNavigation = $navigation->getExtendedNavigation($urlinfo, $langattr);
$pageinfo = $extendedNavigation[$params['url']] ?? false;
if(!$pageinfo)
@@ -861,16 +910,6 @@ class ControllerApiAuthorArticle extends Controller
public function deleteArticle(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'delete');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->articlePublish($params);
@@ -897,6 +936,22 @@ class ControllerApiAuthorArticle extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'delete'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
$content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher'));
# check if it is a folder and if the folder has published pages.

View File

@@ -8,22 +8,13 @@ use Slim\Routing\RouteContext;
use Typemill\Models\Navigation;
use Typemill\Models\Validation;
use Typemill\Models\Content;
use Typemill\Models\Meta;
use Typemill\Static\Translations;
class ControllerApiAuthorBlock extends Controller
{
public function addBlock(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
@@ -51,6 +42,22 @@ class ControllerApiAuthorBlock extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
$content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher'));
$draftMarkdown = $content->getDraftMarkdown($item);
@@ -119,16 +126,6 @@ class ControllerApiAuthorBlock extends Controller
public function moveBlock(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->blockMove($params);
@@ -155,6 +152,22 @@ class ControllerApiAuthorBlock extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
$content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher'));
$draftMarkdown = $content->getDraftMarkdown($item);
@@ -212,16 +225,6 @@ class ControllerApiAuthorBlock extends Controller
public function updateBlock(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->blockInput($params);
@@ -248,6 +251,22 @@ class ControllerApiAuthorBlock extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
$content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher'));
$draftMarkdown = $content->getDraftMarkdown($item);
@@ -318,16 +337,6 @@ class ControllerApiAuthorBlock extends Controller
public function deleteBlock(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->blockDelete($params);
@@ -354,6 +363,22 @@ class ControllerApiAuthorBlock extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
{
# then check if user is the owner of this content
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
$content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher'));
$draftMarkdown = $content->getDraftMarkdown($item);

View File

@@ -15,18 +15,6 @@ class ControllerApiAuthorMeta extends Controller
{
public function getMeta(Request $request, Response $response, $args)
{
# is it really needed? Check middleware if rights are validated there already
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$url = $request->getQueryParams()['url'] ?? false;
$navigation = new Navigation();
@@ -50,6 +38,20 @@ class ControllerApiAuthorMeta extends Controller
$metadata = $meta->addMetaDefaults($metadata, $item, $this->settings['author']);
}
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'view'))
{
# then check if user is the owner of this content
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
# if item is a folder
if($item->elementType == "folder" && isset($item->contains))
{
@@ -103,16 +105,6 @@ class ControllerApiAuthorMeta extends Controller
public function updateMeta(Request $request, Response $response, $args)
{
$validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update');
if(!$validRights)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
$params = $request->getParsedBody();
$validate = new Validation();
$validInput = $validate->metaInput($params);
@@ -140,11 +132,26 @@ class ControllerApiAuthorMeta extends Controller
}
$meta = new Meta();
$metadata = $meta->getMetaData($item);
# if user is not allowed to perform this action (e.g. not admin)
if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'update'))
{
# then check if user is the owner of this content
if(!$this->userIsAllowed($request->getAttribute('c_username'), $metadata))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You do not have enough rights.'),
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
# if item is a folder
if($item->elementType == "folder" && isset($item->contains))
{
$metadata['meta']['contains'] = isset($pagemeta['meta']['contains']) ? $pagemeta['meta']['contains'] : $item->contains;
$metadata['meta']['contains'] = isset($metadata['meta']['contains']) ? $metadata['meta']['contains'] : $item->contains;
# get global metadefinitions
$metadefinitions = $meta->getMetaDefinitions($this->settings, $folder = true);
@@ -167,7 +174,20 @@ class ControllerApiAuthorMeta extends Controller
$tabdefinitions['fields'] = $this->addDatasets($tabdefinitions['fields']);
$tabdefinitions = $this->flattenTabFields($tabdefinitions['fields'], []);
# create validation object
$validated['data'] = $validate->recursiveValidation($tabdefinitions, $params['data']);
if(!empty($validate->errors))
{
$errors[$params['tab']] = $validate->errors;
$response->getBody()->write(json_encode([
'message' => Translations::translate('Please correct the errors in the form.'),
'errors' => $errors
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
}
/*
$errors = [];
# take the user input data and iterate over all fields and values
@@ -202,8 +222,7 @@ class ControllerApiAuthorMeta extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
}
$pageMeta = $meta->getMetaData($item);
*/
# extended
$navigation = new Navigation();
@@ -213,10 +232,10 @@ class ControllerApiAuthorMeta extends Controller
if($params['tab'] == 'meta')
{
# if manual date has been modified
if( $this->hasChanged($params['data'], $pageMeta['meta'], 'manualdate'))
if( $this->hasChanged($params['data'], $metadata['meta'], 'manualdate'))
{
# update the time
$params['data']['time'] = date('H-i-s', time());
$validated['data']['time'] = date('H-i-s', time());
# if it is a post, then rename the post
if($item->elementType == "file" && strlen($item->order) == 12)
@@ -225,7 +244,7 @@ class ControllerApiAuthorMeta extends Controller
$metadate = $params['data']['manualdate'];
if($metadate == '')
{
$metadate = $pageMeta['meta']['created'];
$metadate = $metadata['meta']['created'];
}
$datetime = $metadate . '-' . $params['data']['time'];
$datetime = implode(explode('-', $datetime));
@@ -242,8 +261,8 @@ class ControllerApiAuthorMeta extends Controller
}
# if folder has changed and contains pages instead of posts or posts instead of pages
if($item->elementType == "folder" && isset($params['data']['contains']) && isset($pageMeta['meta']['contains']) && $this->hasChanged($params['data'], $pageMeta['meta'], 'contains'))
{
if($item->elementType == "folder" && isset($params['data']['contains']) && isset($metadata['meta']['contains']) && $this->hasChanged($params['data'], $metadata['meta'], 'contains'))
{
if($meta->folderContainsFolders($item))
{
$response->getBody()->write(json_encode([
@@ -275,9 +294,9 @@ class ControllerApiAuthorMeta extends Controller
}
# normalize the meta-input
$params['data']['navtitle'] = (isset($params['data']['navtitle']) && $params['data']['navtitle'] !== null )? $params['data']['navtitle'] : '';
$params['data']['hide'] = (isset($params['data']['hide']) && $params['data']['hide'] !== null) ? $params['data']['hide'] : false;
$params['data']['noindex'] = (isset($params['data']['noindex']) && $params['data']['noindex'] !== null) ? $params['data']['noindex'] : false;
$validated['data']['navtitle'] = (isset($params['data']['navtitle']) && $params['data']['navtitle'] !== null )? $params['data']['navtitle'] : '';
$validated['data']['hide'] = (isset($params['data']['hide']) && $params['data']['hide'] !== null) ? $params['data']['hide'] : false;
$validated['data']['noindex'] = (isset($params['data']['noindex']) && $params['data']['noindex'] !== null) ? $params['data']['noindex'] : false;
# input values are empty but entry in structure exists
if(
@@ -290,11 +309,11 @@ class ControllerApiAuthorMeta extends Controller
}
elseif(
# check if navtitle or hide-value has been changed
($this->hasChanged($params['data'], $pageMeta['meta'], 'navtitle'))
($this->hasChanged($params['data'], $metadata['meta'], 'navtitle'))
OR
($this->hasChanged($params['data'], $pageMeta['meta'], 'hide'))
($this->hasChanged($params['data'], $metadata['meta'], 'hide'))
OR
($this->hasChanged($params['data'], $pageMeta['meta'], 'noindex'))
($this->hasChanged($params['data'], $metadata['meta'], 'noindex'))
)
{
$navigation->clearNavigation();
@@ -302,10 +321,10 @@ class ControllerApiAuthorMeta extends Controller
}
# add the new/edited metadata
$pageMeta[$params['tab']] = $params['data'];
$metadata[$params['tab']] = $validated['data'];
# store the metadata
$store = $meta->updateMeta($pageMeta, $item);
$store = $meta->updateMeta($metadata, $item);
if($store === true)
{

View File

@@ -126,14 +126,21 @@ class ControllerApiSystemUsers extends Controller
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
}
# if a non-admin-user tries to update another account
if(!$isAdmin AND ($username !== $request->getAttribute('username')) )
# if it is a non-admin-user
if(!$isAdmin)
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You are not allowed to update another user.')
]));
# do not change userrole
unset($userdata['userrole']);
# if a non-admin-user tries to update another account
if(($username !== $request->getAttribute('c_username')))
{
$response->getBody()->write(json_encode([
'message' => Translations::translate('You are not allowed to update another user.')
]));
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
return $response->withHeader('Content-Type', 'application/json')->withStatus(403);
}
}
# cleanup password entry

View File

@@ -71,6 +71,7 @@ class ControllerWebAuthor extends Controller
return $this->c->get('view')->render($response, 'content/blox-editor.twig', [
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'content' => $draftMarkdownHtml,
'jsdata' => [
@@ -139,6 +140,7 @@ class ControllerWebAuthor extends Controller
return $this->c->get('view')->render($response, 'content/raw-editor.twig', [
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'content' => $draftMarkdownHtml,
'jsdata' => [

View File

@@ -48,6 +48,7 @@ class ControllerWebFrontend extends Controller
{
$item = $navigation->getHomepageItem($urlinfo['baseurl']);
$item->active = true;
$home = true;
}
else
{
@@ -65,10 +66,8 @@ class ControllerWebFrontend extends Controller
$keyPathArray = explode(".", $pageinfo['keyPath']);
#
$item = $navigation->getItemWithKeyPath($draftNavigation, $keyPathArray);
# but what if parent is unpublished ??
if(!$item)
{
return $this->c->get('view')->render($response->withStatus(404), '404.twig', [
@@ -257,7 +256,7 @@ class ControllerWebFrontend extends Controller
}
$pagedata = [
'home' => false,
'home' => $home,
'navigation' => $liveNavigation,
'title' => $title,
'content' => $contentHtml,

View File

@@ -41,6 +41,7 @@ class ControllerWebSystem extends Controller
# 'captcha' => $this->checkIfAddCaptcha(),
# 'basicauth' => $user->getBasicAuth(),
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'jsdata' => [
'settings' => $this->settings,
@@ -92,6 +93,7 @@ class ControllerWebSystem extends Controller
return $this->c->get('view')->render($response, 'system/themes.twig', [
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'jsdata' => [
'systemnavi' => $systemNavigation,
@@ -145,6 +147,7 @@ class ControllerWebSystem extends Controller
return $this->c->get('view')->render($response, 'system/plugins.twig', [
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'jsdata' => [
'systemnavi' => $systemNavigation,
@@ -188,6 +191,7 @@ class ControllerWebSystem extends Controller
return $this->c->get('view')->render($response, 'system/license.twig', [
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'jsdata' => [
'systemnavi' => $systemNavigation,
@@ -225,6 +229,7 @@ class ControllerWebSystem extends Controller
return $this->c->get('view')->render($response, 'system/account.twig', [
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'jsdata' => [
'systemnavi' => $systemNavigation,
@@ -269,6 +274,7 @@ class ControllerWebSystem extends Controller
return $this->c->get('view')->render($response, 'system/users.twig', [
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'jsdata' => [
'systemnavi' => $systemNavigation,
@@ -313,6 +319,7 @@ class ControllerWebSystem extends Controller
return $this->c->get('view')->render($response, 'system/user.twig', [
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'jsdata' => [
'systemnavi' => $systemNavigation,
@@ -345,6 +352,7 @@ class ControllerWebSystem extends Controller
return $this->c->get('view')->render($response, 'system/usernew.twig', [
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'jsdata' => [
'systemnavi' => $systemNavigation,
@@ -387,6 +395,7 @@ class ControllerWebSystem extends Controller
return $this->c->get('view')->render($response, 'layouts/layoutSystemBlank.twig', [
'settings' => $this->settings,
'darkmode' => $request->getAttribute('c_darkmode'),
'mainnavi' => $mainNavigation,
'jsdata' => [
'systemnavi' => $systemNavigation,

View File

@@ -22,7 +22,13 @@ class TwigMetaExtension extends AbstractExtension
$metadata = $meta->getMetaData($item);
if(!$metadata OR $metadata['meta']['title'] == '' OR $metadata['meta']['description'] == '')
if(
!$metadata
OR !isset($metadata['meta']['title'])
OR $metadata['meta']['title'] == ''
OR !isset($metadata['meta']['description'])
OR $metadata['meta']['description'] == ''
)
{
$metadata = $meta->addMetaDefaults($metadata, $item, $settings['author']);
}

View File

@@ -21,7 +21,7 @@ class ApiAuthorization implements MiddlewareInterface
{
if(!$this->acl->isAllowed($request->getAttribute('c_userrole'), $this->resource, $this->action))
{
$message = 'userrole: ' . $request->getAttribute('c_userrole') . ' resource: ' . $this->resource . ' action: ' . $this->action;
$message = 'Permission denied. Your are an ' . $request->getAttribute('c_userrole') . ' and you cannot ' . $this->action . ' this ' . $this->resource;
$response = new Response();
$response->getBody()->write(json_encode([

View File

@@ -44,6 +44,10 @@ class SessionMiddleware implements MiddlewareInterface
$request = $request->withAttribute('c_username', $userdata['username']);
$request = $request->withAttribute('c_userrole', $userdata['userrole']);
if(isset($userdata['darkmode']))
{
$request = $request->withAttribute('c_darkmode', $userdata['darkmode']);
}
}
}

View File

@@ -26,7 +26,7 @@ class WebAuthorization implements MiddlewareInterface
return $response->withHeader('Location', $this->router->urlFor('user.account'))->withStatus(302);
}
$response = $handler->handle($request);
return $response;

View File

@@ -34,6 +34,10 @@ class WebRedirectIfUnauthenticated implements MiddlewareInterface
$request = $request->withAttribute('c_username', $userdata['username']);
$request = $request->withAttribute('c_userrole', $userdata['userrole']);
if(isset($userdata['darkmode']))
{
$request = $request->withAttribute('c_darkmode', $userdata['darkmode']);
}
# this executes code from routes first and then executes middleware
$response = $handler->handle($request);

View File

@@ -44,6 +44,7 @@ class Validation
# checks if email is available if user is created
Validator::addRule('emailAvailable', function($field, $value, array $params, array $fields) use ($user)
{
if(!$value){ return false; }
$email = trim($value);
if($email == '' OR $user->findUsersByEmail($email)){ return false; }
return true;
@@ -255,10 +256,8 @@ class Validation
{
return true;
}
else
{
return false;
}
return false;
}
/**
@@ -322,10 +321,12 @@ class Validation
$v->rule('required', ['username'])->message("required");
$v->rule('alphaNum', 'username')->message("invalid");
$v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
if($v->validate())
{
return true;
}
return false;
}
@@ -336,10 +337,12 @@ class Validation
$v->rule('required', ['email'])->message("required");
$v->rule('noHTML', 'email')->message(" contains HTML");
$v->rule('lengthBetween', 'email', 3, 50)->message("Length between 3 - 50");
if($v->validate())
{
return true;
}
return false;
}
@@ -431,14 +434,12 @@ class Validation
$v->rule('lengthBetween', 'item_name', 1, 60);
$v->rule('in', 'type', ['file', 'folder']);
if($v->validate())
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
return $v->errors();
}
public function blockInput(array $params)
@@ -452,11 +453,9 @@ class Validation
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
return $v->errors();
}
public function blockMove(array $params)
@@ -471,10 +470,8 @@ class Validation
{
return true;
}
else
{
return $v->errors();
}
return $v->errors();
}
public function blockDelete(array $params)
@@ -488,10 +485,8 @@ class Validation
{
return true;
}
else
{
return $v->errors();
}
return $v->errors();
}
public function articlePublish(array $params)
@@ -515,10 +510,8 @@ class Validation
{
return true;
}
else
{
return $v->errors();
}
return $v->errors();
}
public function articleUpdate(array $params)
@@ -544,10 +537,8 @@ class Validation
{
return true;
}
else
{
return $v->errors();
}
return $v->errors();
}
public function articleRename(array $params)
@@ -562,11 +553,9 @@ class Validation
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
return $v->errors();
}
public function metaInput(array $params)
@@ -578,11 +567,9 @@ class Validation
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
return $v->errors();
}
/**
@@ -599,7 +586,27 @@ class Validation
$v->rule('lengthBetween', 'password', 5, 50);
$v->rule('equals', 'passwordrepeat', 'password');
return $this->validationResult($v);
if($v->validate())
{
return true;
}
return $v->errors();
}
public function newPassword(array $params)
{
$v = new Validator($params);
$v->rule('required', ['password', 'newpassword']);
$v->rule('lengthBetween', 'newpassword', 5, 50);
$v->rule('equals', 'passwordrepeat', 'password');
if($v->validate())
{
return true;
}
return $v->errors();
}
@@ -619,14 +626,12 @@ class Validation
$v->rule('noHTML', 'title');
$v->rule('markdownSecure', 'content');
if($v->validate())
if($v->validate())
{
return true;
}
else
{
return $v->errors();
}
}
return $v->errors();
}
/**
@@ -687,7 +692,8 @@ class Validation
}
break;
case "codearea":
$v->rule('lengthMax', $fieldName, 10000);
$v->rule('lengthMax', $fieldName, 50000);
# how prevent bad code here?
break;
case "color":
$v->rule('regex', $fieldName, '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/');
@@ -830,65 +836,4 @@ class Validation
return $output;
}
/**
* result for validation
*
* @param obj $v the validation object.
* @return bool
*/
public function checkArray($arrayvalues, $v)
{
die('I think checkArray not in use anymore');
foreach($arrayvalues as $key => $value)
{
if(is_array($value))
{
$this->checkArray($value, $v);
}
$v->rule('noHTML', $value);
$v->rule('lengthMax', $value, 1000);
}
return $v;
}
public function validationResult($v, $name = false)
{
die("do not use validationResults in validation model anymore");
if($v->validate())
{
return true;
}
else
{
if($name == 'meta')
{
return $v->errors();
}
elseif($name)
{
if(isset($_SESSION['errors'][$name]))
{
foreach ($v->errors() as $key => $val)
{
$_SESSION['errors'][$name][$key] = $val;
break;
}
}
else
{
$_SESSION['errors'][$name] = $v->errors();
}
}
else
{
$_SESSION['errors'] = $v->errors();
}
return false;
}
}
}

View File

@@ -10,7 +10,7 @@
<span class="px-4 py-2 border-b-2 border-stone-200">meta</span>
</div>
<div class="px-12 py-8 bg-stone-50">
<div class="px-12 py-8 bg-stone-50 dark:bg-stone-700 dark:text-stone-200">
{% for block in content %}
<div class="blox-preview px-6 py-3 mb-1">{{ block.html|raw }}</div>
{% endfor %}

View File

@@ -177,6 +177,9 @@
.sideaction:hover ~ .blox-preview{
background: rgb(245 245 244);
}
.dark .sideaction:hover ~ .blox-preview{
background: rgb(28 25 23);
}
@@ -225,6 +228,14 @@
.blox-preview tr:nth-child(even){ background-color:rgb(245 245 244); }
.blox-preview td{ padding: 5px;}
.dark .blox-preview thead{
background: rgb(28 25 23);
}
.dark .blox-preview tr:nth-child(even){ background-color:transparent; }
.dark table, .dark th, .dark td {
border: 1px solid rgb(87 83 78);
}
.blox-preview dl{
border-top: 1px solid rgb(20 184 166);
border-bottom: 1px solid rgb(20 184 166);
@@ -249,12 +260,16 @@
.blox-preview dd{
padding-left: 40px;
}
.blox-preview pre,.blox-preview code{
white-space: pre;
color: #333;
background: rgb(245 245 244);
}
.dark .blox-preview pre,.dark .blox-preview code{
white-space: pre;
color: #fff;
background: rgb(87 83 78);
}
.blox-preview code{
display: inline-block;
padding: 0 0.5em;
@@ -286,6 +301,11 @@
padding-top:12px;
padding-bottom:12px;
}
.dark .blox-preview blockquote{
color: #fff;
background: rgb(87 83 78);
}
.blox-preview blockquote:before {
position: absolute;
left: 0px;
@@ -323,6 +343,13 @@
background: rgb(245 245 244);
padding: 10px;
}
.dark .blox-preview .notice1,
.dark .blox-preview .notice2,
.dark .blox-preview .notice3,
.dark .blox-preview .notice4{
color: #fff;
background: rgb(87 83 78);
}
.blox-preview .notice1:before,
.blox-preview .notice2:before,
.blox-preview .notice3:before,
@@ -353,11 +380,13 @@
position: relative;
text-align: center;
}
.blox-preview img.youtube{
.blox-preview figure.youtube{
position: relative;
}
.blox-preview button.play-video {
.blox-preview .youtube::before {
position: absolute;
left:50%;
top: 50%;
margin-top: -50px;
margin-left: -50px;
@@ -369,22 +398,22 @@
border: 0px;
padding: 0;
text-align: center;
content: '';
}
.blox-preview button.play-video:hover {
background: #cc4146;
}
.blox-preview button.play-video::after {
.blox-preview .youtube::after {
position: absolute;
top: 50%;
left:50%;
margin: -20px 0 0 -15px;
height: 0;
width: 0;
border-style: solid;
border-width: 20px 0 20px 40px;
border-color: transparent transparent transparent rgba(255, 255, 255, 0.75);
content: ' ';
content: '';
}
.blox-preview ul.TOC,
.blox-preview .TOC ul{
list-style: none;
@@ -402,6 +431,7 @@
}
.blox-preview ul.TOC{
background: rgb(245 245 244);
background: transparent;
width: 100%;
padding: 20px;
box-sizing:border-box;
@@ -528,4 +558,18 @@
.blox-editor input.urlinput:focus{
outline: 0px;
border: 0px;
}
/****************
** NAVIGATION **
****************/
.pl-15{
padding-left: 3.6rem;
}
.pl-18{
padding-left: 4rem;
}
.pl-21{
padding-left: 5rem;
}

View File

@@ -838,6 +838,10 @@ video {
margin-bottom: 2rem;
}
.mt-1 {
margin-top: 0.25rem;
}
.mt-5 {
margin-top: 1.25rem;
}
@@ -1266,10 +1270,6 @@ video {
border-left-width: 4px;
}
.border-l-2 {
border-left-width: 2px;
}
.border-b-8 {
border-bottom-width: 8px;
}
@@ -1282,6 +1282,10 @@ video {
border-bottom-width: 4px;
}
.border-l-2 {
border-left-width: 2px;
}
.border-solid {
border-style: solid;
}
@@ -1330,11 +1334,6 @@ video {
border-color: rgb(250 204 21 / var(--tw-border-opacity));
}
.border-stone-100 {
--tw-border-opacity: 1;
border-color: rgb(245 245 244 / var(--tw-border-opacity));
}
.border-rose-500 {
--tw-border-opacity: 1;
border-color: rgb(244 63 94 / var(--tw-border-opacity));
@@ -1365,6 +1364,11 @@ video {
border-color: rgb(255 228 230 / var(--tw-border-opacity));
}
.border-stone-100 {
--tw-border-opacity: 1;
border-color: rgb(245 245 244 / var(--tw-border-opacity));
}
.border-x-transparent {
border-left-color: transparent;
border-right-color: transparent;
@@ -1424,16 +1428,16 @@ video {
background-color: rgb(254 226 226 / var(--tw-bg-opacity));
}
.bg-stone-900 {
--tw-bg-opacity: 1;
background-color: rgb(28 25 23 / var(--tw-bg-opacity));
}
.bg-stone-600 {
--tw-bg-opacity: 1;
background-color: rgb(87 83 78 / var(--tw-bg-opacity));
}
.bg-stone-900 {
--tw-bg-opacity: 1;
background-color: rgb(28 25 23 / var(--tw-bg-opacity));
}
.bg-rose-500 {
--tw-bg-opacity: 1;
background-color: rgb(244 63 94 / var(--tw-bg-opacity));
@@ -1654,6 +1658,10 @@ video {
padding-top: 0.75rem;
}
.pl-24 {
padding-left: 6rem;
}
.text-left {
text-align: left;
}
@@ -1766,9 +1774,9 @@ video {
color: rgb(168 162 158 / var(--tw-text-opacity));
}
.text-stone-700 {
.text-stone-900 {
--tw-text-opacity: 1;
color: rgb(68 64 60 / var(--tw-text-opacity));
color: rgb(28 25 23 / var(--tw-text-opacity));
}
.text-stone-500 {
@@ -1776,16 +1784,16 @@ video {
color: rgb(120 113 108 / var(--tw-text-opacity));
}
.text-stone-900 {
--tw-text-opacity: 1;
color: rgb(28 25 23 / var(--tw-text-opacity));
}
.text-stone-300 {
--tw-text-opacity: 1;
color: rgb(214 211 209 / var(--tw-text-opacity));
}
.text-stone-700 {
--tw-text-opacity: 1;
color: rgb(68 64 60 / var(--tw-text-opacity));
}
.text-red-500 {
--tw-text-opacity: 1;
color: rgb(239 68 68 / var(--tw-text-opacity));
@@ -1926,11 +1934,6 @@ video {
border-color: rgb(68 64 60 / var(--tw-border-opacity));
}
.hover\:border-stone-200:hover {
--tw-border-opacity: 1;
border-color: rgb(231 229 228 / var(--tw-border-opacity));
}
.hover\:border-teal-500:hover {
--tw-border-opacity: 1;
border-color: rgb(20 184 166 / var(--tw-border-opacity));
@@ -2040,11 +2043,6 @@ video {
border-color: rgb(37 99 235 / var(--tw-border-opacity));
}
.focus\:border-stone-200:focus {
--tw-border-opacity: 1;
border-color: rgb(231 229 228 / var(--tw-border-opacity));
}
.focus\:bg-white:focus {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
@@ -2090,11 +2088,6 @@ video {
background-color: rgb(231 229 228 / var(--tw-bg-opacity));
}
.disabled\:bg-stone-50:disabled {
--tw-bg-opacity: 1;
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
}
.disabled\:text-stone-800:disabled {
--tw-text-opacity: 1;
color: rgb(41 37 36 / var(--tw-text-opacity));
@@ -2109,14 +2102,12 @@ video {
visibility: visible;
}
.dark .dark\:border-stone-900 {
--tw-border-opacity: 1;
border-color: rgb(28 25 23 / var(--tw-border-opacity));
.dark .dark\:border {
border-width: 1px;
}
.dark .dark\:border-stone-200 {
--tw-border-opacity: 1;
border-color: rgb(231 229 228 / var(--tw-border-opacity));
.dark .dark\:border-0 {
border-width: 0px;
}
.dark .dark\:border-stone-600 {
@@ -2129,9 +2120,14 @@ video {
border-color: rgb(68 64 60 / 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\:border-stone-900 {
--tw-border-opacity: 1;
border-color: rgb(28 25 23 / var(--tw-border-opacity));
}
.dark .dark\:border-stone-200 {
--tw-border-opacity: 1;
border-color: rgb(231 229 228 / var(--tw-border-opacity));
}
.dark .dark\:bg-stone-700 {
@@ -2139,9 +2135,9 @@ video {
background-color: rgb(68 64 60 / var(--tw-bg-opacity));
}
.dark .dark\:bg-stone-200 {
.dark .dark\:bg-stone-600 {
--tw-bg-opacity: 1;
background-color: rgb(231 229 228 / var(--tw-bg-opacity));
background-color: rgb(87 83 78 / var(--tw-bg-opacity));
}
.dark .dark\:bg-stone-900 {
@@ -2149,9 +2145,9 @@ video {
background-color: rgb(28 25 23 / var(--tw-bg-opacity));
}
.dark .dark\:text-gray-400 {
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity));
.dark .dark\:bg-stone-200 {
--tw-bg-opacity: 1;
background-color: rgb(231 229 228 / var(--tw-bg-opacity));
}
.dark .dark\:text-stone-200 {
@@ -2159,31 +2155,71 @@ video {
color: rgb(231 229 228 / 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-900 {
--tw-text-opacity: 1;
color: rgb(28 25 23 / var(--tw-text-opacity));
}
.dark .dark\:text-stone-600 {
--tw-text-opacity: 1;
color: rgb(87 83 78 / var(--tw-text-opacity));
}
.dark .dark\:text-gray-400 {
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity));
}
.dark .dark\:text-stone-300 {
--tw-text-opacity: 1;
color: rgb(214 211 209 / var(--tw-text-opacity));
}
.dark .dark\:text-stone-50 {
--tw-text-opacity: 1;
color: rgb(250 250 249 / var(--tw-text-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\:border-rose-500:hover {
--tw-border-opacity: 1;
border-color: rgb(244 63 94 / var(--tw-border-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\:bg-teal-500:hover {
--tw-bg-opacity: 1;
background-color: rgb(20 184 166 / var(--tw-bg-opacity));
}
.dark .hover\:dark\:bg-rose-500:hover {
--tw-bg-opacity: 1;
background-color: rgb(244 63 94 / var(--tw-bg-opacity));
}
.dark .hover\:dark\:bg-stone-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(87 83 78 / var(--tw-bg-opacity));
}
.dark .hover\:dark\:bg-stone-900:hover {
--tw-bg-opacity: 1;
background-color: rgb(28 25 23 / var(--tw-bg-opacity));
}
.dark .dark\:hover\:bg-stone-200:hover {
--tw-bg-opacity: 1;
background-color: rgb(231 229 228 / 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));
@@ -2194,11 +2230,6 @@ video {
background-color: rgb(20 184 166 / var(--tw-bg-opacity));
}
.dark .dark\:hover\: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));
@@ -2224,6 +2255,16 @@ video {
color: rgb(28 25 23 / var(--tw-text-opacity));
}
.dark .disabled\:dark\:bg-stone-600:disabled {
--tw-bg-opacity: 1;
background-color: rgb(87 83 78 / var(--tw-bg-opacity));
}
.dark .disabled\:dark\:text-stone-200:disabled {
--tw-text-opacity: 1;
color: rgb(231 229 228 / var(--tw-text-opacity));
}
@media (min-width: 768px) {
.md\:max-w-md {
max-width: 28rem;

View File

@@ -14,7 +14,9 @@ let typemillUtilities = {
var youtubeItem = this.youtubeItems[i];
this.addYoutubePlayButton(youtubeItem);
}
}
}
console.info(this.youtubeItems);
},
addYoutubePlayButton: function(element)

View File

@@ -71,7 +71,7 @@ bloxeditor.component('headline-component', {
<use xlink:href="#icon-header"></use>
</svg>
</div>
<button class="absolute w-6 top-0 bottom-0 left-0 border-r-2 border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-stone-50 transition-1" @click.prevent="headlinedown">
<button class="absolute w-6 top-0 bottom-0 left-0 border-r-2 border-stone-700 bg-stone-200 dark:bg-stone-600 hover:bg-teal-500 hover:dark:bg-teal-500 hover:text-stone-50 transition-1" @click.prevent="headlinedown">
<div class="absolute w-6 top-3 text-center">{{ level }}</div>
</button>
<input class="opacity-1 w-full bg-transparent pr-6 pl-10 py-3 outline-none" :class="hlevel" type="text" v-model="compmarkdown" ref="markdown" :disabled="disabled" @input="updatemarkdown">
@@ -334,9 +334,9 @@ bloxeditor.component('code-component', {
<use xlink:href="#icon-embed"></use>
</svg>
</div>
<div class="w-full flex p-3 border-b-2 border-stone-700 bg-stone-200">
<div class="w-full flex p-3 border-b-2 border-stone-700 bg-stone-100 dark:bg-stone-900">
<label class="pr-2 py-1" for="language">{{ $filters.translate('Language') }}: </label>
<input class="px-2 py-1 flex-grow text-stone-700 focus:outline-none" name="language" type="text" v-model="language" :disabled="disabled" @input="createlanguage">
<input class="px-2 py-1 flex-grow focus:outline-none bg-stone-200 text-stone-900" name="language" type="text" v-model="language" :disabled="disabled" @input="createlanguage">
</div>
<textarea class="font-mono text-sm opacity-1 w-full bg-transparent px-6 py-3 outline-none" ref="markdown" v-model="codeblock" :disabled="disabled" @input="createmarkdown"></textarea>
</div>`,
@@ -562,7 +562,7 @@ bloxeditor.component('notice-component', {
<use xlink:href="#icon-exclamation-circle"></use>
</svg>
</div>
<button class="absolute w-6 top-0 bottom-0 left-0 border-r-2 border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-stone-50 transition-1" :class="noteclass" @click.prevent="noticedown">
<button class="absolute w-6 top-0 bottom-0 left-0 border-r-2 border-stone-700 bg-stone-200 dark:bg-stone-600 hover:bg-teal-500 hover:dark:bg-teal-500 hover:text-stone-50 transition-1" :class="noteclass" @click.prevent="noticedown">
<div class="absolute w-6 top-3 text-center">{{ prefix }}</div>
</button>
<textarea class="opacity-1 w-full bg-transparent pr-6 pl-10 py-3 outline-none notice" ref="markdown" v-model="notice" :disabled="disabled" @input="updatemarkdown($event.target.value)"></textarea>
@@ -884,7 +884,7 @@ bloxeditor.component('definition-component', {
definitionList: [],
}
},
template: `<div class="definitionList">
template: `<div class="definitionList dark:border dark:border-stone-600">
<div class="absolute top-3 -left-5 text-stone-400">
<svg class="icon icon-dots-two-vertical">
<use xlink:href="#icon-dots-two-vertical"></use>
@@ -895,15 +895,15 @@ bloxeditor.component('definition-component', {
item-key="id"
@end="moveDefinition">
<template #item="{element, index}">
<div class="definitionRow border-b border-stone-300">
<div class="definitionRow border-b border-stone-300 dark:border-stone-600">
<div class="relative flex p-6">
<div class="definitionTerm">
<input type="text" class="p-2 w-100 text-stone-700 focus:outline-none" :placeholder="element.term" :value="element.term" :disabled="disabled" @input="updateterm($event,index)" @blur="updateMarkdown">
<input type="text" class="p-2 w-100 text-stone-900 bg-stone-200 focus:outline-none" :placeholder="element.term" :value="element.term" :disabled="disabled" @input="updateterm($event,index)" @blur="updateMarkdown">
</div>
<div class="flex-grow">
<div class="flex mb-2" v-for="(description,ddindex) in element.descriptions">
<svg class="icon icon-dots-two-vertical mt-3"><use xlink:href="#icon-dots-two-vertical"></use></svg>
<textarea class="flex-grow p-2 focus:outline-none" :placeholder="description" v-html="element.descriptions[ddindex]" :disabled="disabled" @input="updatedescription($event, index, ddindex)" @keydown.13.prevent="enter" @blur="updateMarkdown"></textarea>
<textarea class="flex-grow p-2 focus:outline-none bg-stone-200 text-stone-900" :placeholder="description" v-html="element.descriptions[ddindex]" :disabled="disabled" @input="updatedescription($event, index, ddindex)" @keydown.13.prevent="enter" @blur="updateMarkdown"></textarea>
<button title="delete description" class="text-white bg-stone-700 w-6 h-6 text-xs hover:bg-rose-500" @click.prevent="deleteItem($event,index,ddindex)">
<svg class="icon icon-minus">
<use xlink:href="#icon-minus"></use>
@@ -1308,8 +1308,8 @@ bloxeditor.component('image-component', {
</div>
<Transition name="initial" appear>
<div v-if="showmedialib" class="fixed top-0 left-0 right-0 bottom-0 bg-stone-100 z-50">
<button class="w-full bg-stone-200 hover:bg-rose-500 hover:text-white p-2 transition duration-100" @click.prevent="showmedialib = false">{{ $filters.translate('close library') }}</button>
<div v-if="showmedialib" class="fixed top-0 left-0 right-0 bottom-0 bg-stone-100 dark:bg-stone-700 z-50">
<button class="w-full bg-stone-200 dark:bg-stone-900 hover:dark:bg-rose-500 hover:bg-rose-500 hover:text-white p-2 transition duration-100" @click.prevent="showmedialib = false">{{ $filters.translate('close library') }}</button>
<medialib parentcomponent="images" @addFromMedialibEvent="addFromMedialibFunction"></medialib>
</div>
</Transition>
@@ -1326,23 +1326,23 @@ bloxeditor.component('image-component', {
<div class="imgmeta p-8" v-if="imgmeta">
<div class="flex mb-2">
<label class="w-1/5 py-2" for="imgalt">{{ $filters.translate('Alt-Text') }}: </label>
<input class="w-4/5 p-2" name="imgalt" type="text" placeholder="alt" @input="createmarkdown" v-model="imgalt" max="100" />
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" name="imgalt" type="text" placeholder="alt" @input="createmarkdown" v-model="imgalt" max="100" />
</div>
<div class="flex mb-2">
<label class="w-1/5 py-2" for="imgtitle">{{ $filters.translate('Title') }}: </label>
<input class="w-4/5 p-2" name="imgtitle" type="text" placeholder="title" v-model="imgtitle" @input="createmarkdown" max="64" />
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" name="imgtitle" type="text" placeholder="title" v-model="imgtitle" @input="createmarkdown" max="64" />
</div>
<div class="flex mb-2">
<label class="w-1/5 py-2" for="imgcaption">{{ $filters.translate('Caption') }}: </label>
<input class="w-4/5 p-2" title="imgcaption" type="text" placeholder="caption" v-model="imgcaption" @input="createmarkdown" max="140" />
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" title="imgcaption" type="text" placeholder="caption" v-model="imgcaption" @input="createmarkdown" max="140" />
</div>
<div class="flex mb-2">
<label class="w-1/5 py-2" for="imgurl">{{ $filters.translate('Link') }}: </label>
<input class="w-4/5 p-2" title="imgurl" type="url" placeholder="url" v-model="imglink" @input="createmarkdown" />
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" title="imgurl" type="url" placeholder="url" v-model="imglink" @input="createmarkdown" />
</div>
<div class="flex mb-2">
<label class="w-1/5 py-2" for="imgclass">{{ $filters.translate('Class') }}: </label>
<select class="w-4/5 p-2 bg-white" title="imgclass" v-model="imgclass" @change="createmarkdown">
<select class="w-4/5 p-2 bg-stone-200 text-stone-900" title="imgclass" v-model="imgclass" @change="createmarkdown">
<option value="center">{{ $filters.translate('Center') }}</option>
<option value="left">{{ $filters.translate('Left') }}</option>
<option value="right">{{ $filters.translate('Right') }}</option>
@@ -1350,8 +1350,8 @@ bloxeditor.component('image-component', {
</div>
<div class="flex mb-2">
<label class="w-1/5 py-2" for="imgsizes">{{ $filters.translate('width/height') }}:</label>
<input class="w-2/5 p-2 mr-1" title="imgwidth" type="text" :placeholder="originalwidth" v-model="imgwidth" @input="changewidth" max="6" />
<input class="w-2/5 p-2 ml-1" title="imgheight" type="text" :placeholder="originalheight" v-model="imgheight" @input="changeheight" max="6" />
<input class="w-2/5 p-2 mr-1 bg-stone-200 text-stone-900" title="imgwidth" type="text" :placeholder="originalwidth" v-model="imgwidth" @input="changewidth" max="6" />
<input class="w-2/5 p-2 ml-1 bg-stone-200 text-stone-900" title="imgheight" type="text" :placeholder="originalheight" v-model="imgheight" @input="changeheight" max="6" />
</div>
<div class="mb-2">
<label v-if="showresize" for="saveoriginal" class="flex w-full">
@@ -1504,6 +1504,7 @@ bloxeditor.component('image-component', {
this.imgpreview = data.urlinfo.baseurl + '/' + value;
this.showmedialib = false;
this.saveimage = false;
this.imgmeta = true;
this.createmarkdown();
},
@@ -1856,11 +1857,11 @@ bloxeditor.component('file-component', {
<input title="fileid" type="hidden" placeholder="id" v-model="fileid" @input="createmarkdown" max="140" />
<div class="flex mb-2">
<label class="w-1/5 py-2" for="filetitle">{{ $filters.translate('Title') }}: </label>
<input class="w-4/5 p-2" name="filetitle" type="text" placeholder="Add a title for the download-link" v-model="filetitle" @input="createmarkdown" max="64" />
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" name="filetitle" type="text" placeholder="Add a title for the download-link" v-model="filetitle" @input="createmarkdown" max="64" />
</div>
<div class="flex mb-2">
<label class="w-1/5 py-2" for="filerestriction">Access for: </label>
<select class="w-4/5 p-2 bg-white" name="filerestriction" v-model="selectedrole" @change="updaterestriction">
<select class="w-4/5 p-2 bg-stone-200 text-stone-900" name="filerestriction" v-model="selectedrole" @change="updaterestriction">
<option disabled value="">{{ $filters.translate('Please select') }}</option>
<option v-for="role in userroles">{{ role }}</option>
</select>
@@ -2133,10 +2134,9 @@ bloxeditor.component('video-component', {
<use xlink:href="#icon-play"></use>
</svg>
</div>
<div>{{ markdown }}</div>
<div class="flex mt-2 mb-2">
<label class="w-1/5 py-2" for="video">{{ $filters.translate('Link to youtube') }}: </label>
<input class="w-4/5 p-2 bg-white" type="url" ref="markdown" placeholder="https://www.youtube.com/watch?v=" :value="markdown" :disabled="disabled" @input="updatemarkdown($event.target.value)">
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" type="url" ref="markdown" placeholder="https://www.youtube.com/watch?v=" :value="markdown" :disabled="disabled" @input="updatemarkdown($event.target.value)">
</div>
</div>`,
data: function(){
@@ -2268,14 +2268,14 @@ bloxeditor.component('shortcode-component', {
<use xlink:href="#icon-square-brackets"></use>
</svg>
</div>
<div v-if="shortcodedata" class="p-8 bg-stone-100" ref="markdown">
<div v-if="shortcodedata" class="p-8 bg-stone-100 dark:bg-stone-900" ref="markdown">
<div class="flex mt-2 mb-2">
<label class="w-1/5 py-2" for="shortcodename">{{ $filters.translate('Shortcode') }}: </label>
<select class="w-4/5 p-2 bg-white" title="shortcodename" v-model="shortcodename" @change="createmarkdown(shortcodename)"><option v-for="shortcode,name in shortcodedata" :value="name">{{ name }}</option></select>
<select class="w-4/5 p-2 bg-stone-200 text-stone-900" title="shortcodename" v-model="shortcodename" @change="createmarkdown(shortcodename)"><option v-for="shortcode,name in shortcodedata" :value="name">{{ name }}</option></select>
</div>
<div class="flex mt-2 mb-2" v-for="key,attribute in shortcodedata[shortcodename]">
<label class="w-1/5 py-2" for="key">{{ attribute }}: </label>
<input class="w-4/5 p-2 bg-white" type="search" list="shortcodedata[shortcodename][attribute]" v-model="shortcodedata[shortcodename][attribute].value" @input="createmarkdown(shortcodename,attribute)">
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" type="search" list="shortcodedata[shortcodename][attribute]" v-model="shortcodedata[shortcodename][attribute].value" @input="createmarkdown(shortcodename,attribute)">
<datalist id="shortcodedata[shortcodename][attribute]">
<option v-for="item in shortcodedata[shortcodename][attribute].content" @click="selectsearch(item,attribute)" :value="item"></option>
</datalist>

View File

@@ -1,5 +1,5 @@
const bloxeditor = Vue.createApp({
template: `<div v-if="showblox" class="px-12 py-8 bg-stone-50 shadow-md mb-16">
template: `<div v-if="showblox" class="px-12 py-8 bg-stone-50 dark:bg-stone-700 dark:text-stone-200 shadow-md mb-16">
<draggable
v-model="content"
@start="onStart"
@@ -100,19 +100,19 @@ bloxeditor.component('new-block',{
props: ['markdown', 'index'],
template: `
<div class="w-full mb-4">
<div v-if="!componentType" class="w-full flex p-4">
<div v-if="!componentType" class="w-full flex p-4 dark:bg-stone-900">
<button v-for="button in formats"
class="p-2 m-1 border border-stone-300 bg-stone-100 hover:bg-stone-700 hover:text-stone-50 transition-1"
class="p-2 m-1 border border-stone-300 bg-stone-100 dark:border-stone-700 dark:bg-stone-700 hover:bg-stone-700 hover:dark:bg-stone-600 hover:text-stone-50 transition-1"
@click.prevent="setComponentType( $event, button.component )"
:title="button.title"
v-html="button.label">
</button>
</div>
<div v-if="componentType" class="relative bg-stone-100">
<div v-if="componentType" class="relative bg-stone-100 dark:bg-stone-900">
<component ref="activeComponent" :disabled="disabled" :markdown="newblockmarkdown" :index="index" @saveBlockEvent="saveNewBlock" @updateMarkdownEvent="updateMarkdownFunction" :is="componentType"></component>
<div class="edit-buttons absolute -bottom-3 right-4 z-2 text-xs">
<button class="cancel w-20 p-1 border-r border-stone-700 bg-stone-200 hover:bg-rose-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="closeComponent">{{ $filters.translate('cancel') }}</button>
<button class="save w-20 p-1 border-l border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="beforeSaveNew()">{{ $filters.translate('save') }}</button>
<button class="cancel w-20 p-1 border-r border-stone-700 bg-stone-200 dark:bg-stone-600 hover:bg-rose-500 hover:dark:bg-rose-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="closeComponent">{{ $filters.translate('cancel') }}</button>
<button class="save w-20 p-1 border-l border-stone-700 bg-stone-200 dark:bg-stone-600 hover:bg-teal-500 hover:dark:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="beforeSaveNew()">{{ $filters.translate('save') }}</button>
</div>
</div>
</div>
@@ -232,8 +232,8 @@ bloxeditor.component('content-block', {
props: ['element', 'index'],
template: `
<div :class="{'edit': edit}">
<div v-if="newblock" class="bg-stone-100">
<div class="w-full flex justify-between bg-stone-200">
<div v-if="newblock" class="bg-stone-100 dark:bg-stone-600">
<div class="w-full flex justify-between bg-stone-200 dark:bg-stone-600">
<p class="p-2 pl-4">Choose a content type</p>
<button class="p-2 border-l border-stone-700 hover:text-white hover:bg-rose-500 transition-1" @click="closeNewBlock">{{ $filters.translate('close') }}</button>
</div>
@@ -241,11 +241,11 @@ bloxeditor.component('content-block', {
</div>
<div class="relative blox-wrapper mb-1">
<div v-if="index != 0" class="sideaction hidden absolute -top-3 left-1/2 -translate-x-1/2 z-10 text-xs">
<button class="delete w-16 p-1 border-r border-stone-700 bg-stone-200 hover:bg-rose-500 hover:text-white transition-1" @mousedown.prevent="disableSort()" @click.prevent="deleteBlock">{{ $filters.translate('delete') }}</button>
<button class="add w-16 p-1 border-l border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @mousedown.prevent="disableSort()" @click.prevent="openNewBlock">{{ $filters.translate('add') }}</button>
<button class="delete w-16 p-1 border-r border-stone-700 bg-stone-200 dark:bg-stone-600 hover:bg-rose-500 hover:dark:bg-rose-500 hover:text-white transition-1" @mousedown.prevent="disableSort()" @click.prevent="deleteBlock">{{ $filters.translate('delete') }}</button>
<button class="add w-16 p-1 border-l border-stone-700 bg-stone-200 dark:bg-stone-600 hover:bg-teal-500 hover:dark:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @mousedown.prevent="disableSort()" @click.prevent="openNewBlock">{{ $filters.translate('add') }}</button>
</div>
<div v-if="!edit" class="blox-preview px-6 py-3 hover:bg-stone-100 overflow-hidden transition-1" @click="showEditor" v-html="getHtml(element.html)"></div>
<div v-else class="blox-editor bg-stone-100">
<div v-if="!edit" class="blox-preview px-6 py-3 hover:bg-stone-100 hover:dark:bg-stone-900 overflow-hidden transition-1" @click="showEditor" v-html="getHtml(element.html)"></div>
<div v-else class="blox-editor bg-stone-100 dark:bg-stone-900">
<div v-if="load" class="absolute right-0 top-0 left-0 bottom-0 bg-stone-100 opacity-75">
<svg class="animate-spin h-5 w-5 text-stone-900 absolute top-0 right-0 bottom-0 left-0 m-auto" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
@@ -254,8 +254,8 @@ bloxeditor.component('content-block', {
</div>
<component ref="activeComponent" :disabled="disabled" :markdown="updatedmarkdown" :index="index" @saveBlockEvent="saveBlock" @updateMarkdownEvent="updateMarkdownFunction" :is="componentType"></component>
<div class="edit-buttons absolute -bottom-3 right-4 z-10 text-xs">
<button class="cancel w-20 p-1 border-r border-stone-700 bg-stone-200 hover:bg-rose-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="closeEditor">{{ $filters.translate('cancel') }}</button>
<button class="save w-20 p-1 border-l border-stone-700 bg-stone-200 hover:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="beforeSave()">{{ $filters.translate('save') }}</button>
<button class="cancel w-20 p-1 border-r border-stone-700 bg-stone-200 dark:bg-stone-600 hover:bg-rose-500 hover:dark:bg-rose-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="closeEditor">{{ $filters.translate('cancel') }}</button>
<button class="save w-20 p-1 border-l border-stone-700 bg-stone-200 dark:bg-stone-600 hover:bg-teal-500 hover:dark:bg-teal-500 hover:text-white transition-1" :disabled="disabled" @click.prevent="beforeSave()">{{ $filters.translate('save') }}</button>
</div>
</div>
</div>

View File

@@ -7,11 +7,11 @@ const navigation = Vue.createApp({
</div>
<div class="flex w-full my-px border-y border-stone-200 dark:border-stone-900 font-bold">
<div class="border-l-4" :class="getStatusClass(home.status)"></div>
<a :href="getUrl(home.urlRelWoF)" class="flex-grow p-1 pl-3 border-stone-50 hover:bg-teal-500 hover:text-stone-50 dark:hover:bg-stone-200 hover:dark:text-stone-900" :class="home.active ? 'text-stone-50 bg-teal-500' : ''">
<a :href="getUrl(home.urlRelWoF)" class="flex-grow p-1 pl-3 border-stone-50 hover:bg-teal-500 hover:text-stone-50 dark:hover:bg-stone-200 hover:dark:text-stone-900" :class="home.active ? 'text-stone-50 bg-teal-500 dark:bg-stone-200 dark:text-stone-900' : 'dark:bg-stone-700'">
{{ $filters.translate(home.name) }}
</a>
</div>
<div class="pl-2 pl-3 pl-4 pl-6 pl-8 pl-9 pl-10 pl-12 pl-15 text-stone-50"></div>
<div class="pl-2 pl-3 pl-4 pl-6 pl-8 pl-9 pl-10 pl-12 pl-15 pl-18 pl-21 pl-24 text-stone-50"></div>
<navilevel :navigation="navigation" :expanded="expanded" />
</div>`,
data: function () {
@@ -183,10 +183,10 @@ navigation.component('navilevel',{
</template>
<template #footer>
<li>
<div class="flex w-full my-px border-b border-stone-200 hover:bg-stone-200 group">
<div class="flex w-full my-px border mt-1 mb-1 border-stone-300 dark:border-stone-600 hover:bg-stone-200 group">
<div class="border-l-4 border-stone-200"></div>
<div class="flex-grow">
<input :class="getNaviInputLevel(parentId)" class="w-full p-1 bg-transparent border-0 border-stone-100 hover:border-stone-200 focus:border-stone-200 focus:bg-stone-200 focus:outline-none" placeholder="..." v-model="newItem">
<input :class="getNaviInputLevel(parentId)" class="w-full p-1 bg-transparent focus:bg-stone-200 focus:outline-none dark:text-stone-600" placeholder="..." v-model="newItem">
</div>
<!-- <div class="w-1/4 invisible group-hover:visible"> -->
<div class="flex">

View File

@@ -2,8 +2,9 @@ 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="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'"
<input type="text" class="text-stone-900 h-12 w-full border px-2 py-3"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:maxlength="maxlength"
:readonly="readonly"
:hidden="hidden"
@@ -30,6 +31,7 @@ app.component('component-textarea', {
<label :for="name" class="block mb-1 font-medium">{{ $filters.translate(label) }}</label>
<textarea rows="8" class="w-full border border-stone-300 text-stone-900 bg-stone-200 px-2 py-3"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:class="css"
:readonly="readonly"
:required="required"
@@ -72,6 +74,7 @@ app.component('component-codearea', {
<div class="codearea">
<textarea data-el="editor" class="editor" ref="editor"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:readonly="readonly"
:required="required"
:disabled="disabled"
@@ -130,6 +133,7 @@ app.component('component-select', {
<label :for="name" class="block mb-1 font-medium">{{ $filters.translate(label) }}</label>
<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"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:name="name"
:required="required"
:disabled="disabled"
@@ -159,8 +163,9 @@ app.component('component-checkbox', {
template: `<div :class="css ? css : ''" class="w-full mt-5 mb-5">
<div class="block mb-1 font-medium">{{ $filters.translate(label) }}</div>
<label :for="name" class="inline-flex items-start">
<input type="checkbox" class="w-6 h-6"
<input type="checkbox" class="w-6 h-6"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:readonly="readonly"
:required="required"
:disabled="disabled"
@@ -199,6 +204,7 @@ app.component('component-checkboxlist', {
<label class="flex items-start mb-2 mt-2" v-for="option, optionvalue in options" >
<input type="checkbox" class="w-6 h-6"
:id="optionvalue"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:value="optionvalue"
v-model="checkedoptions"
@change="update(checkedoptions, name)">
@@ -234,6 +240,7 @@ app.component('component-radio', {
<label class="flex items-start mb-2 mt-2" v-for="option,optionvalue in options">
<input type="radio" class="w-6 h-6"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:readonly="readonly"
:required="required"
:disabled="disabled"
@@ -260,6 +267,7 @@ app.component('component-number', {
<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 text-stone-900 bg-stone-200 px-2 py-3"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:min="min"
:max="max"
:maxlength="maxlength"
@@ -291,8 +299,9 @@ 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 text-stone-900" :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"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:readonly="readonly"
:required="required"
:disabled="disabled"
@@ -323,8 +332,9 @@ 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 text-stone-900" :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"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:maxlength="maxlength"
:readonly="readonly"
:required="required"
@@ -355,8 +365,9 @@ 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 text-stone-900" :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"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:maxlength="maxlength"
:readonly="readonly"
:required="required"
@@ -388,8 +399,9 @@ 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 text-stone-900" :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"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:maxlength="maxlength"
:readonly="readonly"
:required="required"
@@ -420,9 +432,10 @@ app.component('component-color', {
<path d="M30.828 1.172c-1.562-1.562-4.095-1.562-5.657 0l-5.379 5.379-3.793-3.793-4.243 4.243 3.326 3.326-14.754 14.754c-0.252 0.252-0.358 0.592-0.322 0.921h-0.008v5c0 0.552 0.448 1 1 1h5c0 0 0.083 0 0.125 0 0.288 0 0.576-0.11 0.795-0.329l14.754-14.754 3.326 3.326 4.243-4.243-3.793-3.793 5.379-5.379c1.562-1.562 1.562-4.095 0-5.657zM5.409 30h-3.409v-3.409l14.674-14.674 3.409 3.409-14.674 14.674z"></path>
</svg>
</div>
<input type="color" class="h-12 w-full border pl-10 pr-1 py-1" :class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
<input type="color" class="h-12 w-full border pl-10 pr-1 py-1"
:id="id"
:maxlength="maxlength"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:maxlength="maxlength"
:readonly="readonly"
:required="required"
:disabled="disabled"
@@ -457,8 +470,9 @@ 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 text-stone-900" :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"
:id="id"
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
:maxlength="maxlength"
:readonly="readonly"
:required="required"
@@ -740,17 +754,17 @@ app.component('component-image', {
</div>
</div>
<div class="lg:w-half w-full ph3 lh-copy f6 relative">
<div class="relative w-full bg-stone-700 hover:bg-stone-900">
<p class="relative w-full text-white text-center px-2 py-3"><svg class="icon icon-upload baseline"><use xlink:href="#icon-upload"></use></svg> {{ $filters.translate('upload an image') }}</p>
<div class="relative w-full bg-stone-600 hover:bg-stone-900">
<p class="relative w-full text-white text-center px-2 py-3"><svg class="icon icon-upload mr-1"><use xlink:href="#icon-upload"></use></svg> {{ $filters.translate('upload an image') }}</p>
<input class="absolute w-full top-0 opacity-0 bg-stone-900 cursor-pointer px-2 py-3" type="file" name="image" accept="image/*" @change="onFileChange( $event )" />
</div>
<div class="w-full mt-3">
<button class="w-full bg-stone-700 hover:bg-stone-900 text-white px-2 py-3 text-center cursor-pointer transition duration-100" @click.prevent="showmedialib = true"><svg class="icon icon-image baseline"><use xlink:href="#icon-image"></use></svg> {{ $filters.translate('select from medialib') }}</button>
<button class="w-full bg-stone-600 hover:bg-stone-900 text-white px-2 py-3 text-center cursor-pointer transition duration-100" @click.prevent="showmedialib = true"><svg class="icon icon-image mr-1"><use xlink:href="#icon-image"></use></svg> {{ $filters.translate('select from medialib') }}</button>
</div>
<div class="w-full mt-3">
<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>
<button @click.prevent="deleteImage()" class="w-1/6 bg-stone-200 dark:bg-stone-600 hover:bg-rose-500 hover:dark:bg-rose-500 hover:text-white">x</button>
<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"
@@ -764,7 +778,7 @@ app.component('component-image', {
@input="update($event, name)">
</div>
<div v-if="qualitylabel" class="w-full mt-3">
<button class="w-full cursor-pointer bg-stone-200 hover:bg-stone-300 text-center px-1 py-1 transition duration-100" @click.prevent="switchQuality(value)">{{ qualitylabel }}</button>
<button class="w-full cursor-pointer bg-stone-200 dark:bg-stone-600 hover:bg-stone-300 hover:dark:bg-stone-900 text-center px-1 py-1 transition duration-100" @click.prevent="switchQuality(value)">{{ qualitylabel }}</button>
</div>
</div>
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
@@ -773,11 +787,11 @@ app.component('component-image', {
</div>
<Transition name="initial" appear>
<div v-if="showmedialib" class="fixed top-0 left-0 right-0 bottom-0 bg-stone-100 z-50">
<button class="w-full bg-stone-200 hover:bg-rose-500 hover:text-white p-2 transition duration-100" @click.prevent="showmedialib = false">{{ $filters.translate('close library') }}</button>
<div v-if="showmedialib" class="fixed top-0 left-0 right-0 bottom-0 bg-stone-100 dark:bg-stone-700 z-50">
<button class="w-full bg-stone-200 dark:bg-stone-900 hover:dark:bg-rose-500 hover:bg-rose-500 hover:text-white p-2 transition duration-100" @click.prevent="showmedialib = false">{{ $filters.translate('close library') }}</button>
<medialib parentcomponent="images" @addFromMedialibEvent="addFromMedialibFunction"></medialib>
</div>
</Transition>
</Transition>
</div>`,
mounted: function() {
@@ -896,7 +910,7 @@ app.component('component-image', {
sharedself.load = false;
if(error.response)
{
console.info(error.response);
console.info(error.response);
/* publishController.errors.message = error.response.data.errors; */
}
});

View File

@@ -5,8 +5,8 @@ const medialib = {
<div class="w-1/4">
<div class="w-full relative">
<div class="flex">
<input v-model="search" class="h-12 px-2 py-3 border border-stone-300 bg-stone-200">
<div class="w-1/4 h-12 px-2 py-3 center bg-stone-700 hover:bg-stone-900 text-white">
<input v-model="search" class="h-12 px-2 py-3 text-stone-900 border border-stone-300 bg-stone-200">
<div class="w-1/4 h-12 px-2 py-3 center text-center bg-stone-700 dark:bg-stone-600 hover:bg-stone-900 hover:dark:bg-stone-900 text-white">
<svg class="icon icon-search">
<use xlink:href="#icon-search"></use>
</svg>
@@ -16,8 +16,8 @@ const medialib = {
<div v-if="showimages">
<h3 class="border-b-2 border-stone-700 pt-6 pb-3">Images</h3>
<div class="my-3">
<button @click.prevent="showImages('pageImages')" :class="isActive('pageImages')" class="px-2 py-1 mr-2 hover:bg-stone-700 hover:text-stone-50 transition duration-100">{{ $filters.translate('this page') }}</button>
<button @click.prevent="showImages('allImages')" :class="isActive('allImages')" class="px-2 py-1 hover:bg-stone-700 hover:text-stone-50 transition duration-100">{{ $filters.translate('all pages') }}</button>
<button @click.prevent="showImages('pageImages')" :class="isActive('pageImages')" class="px-2 py-1 mr-2 hover:bg-stone-700 hover:dark:bg-stone-900 hover:text-stone-50 transition duration-100">{{ $filters.translate('this page') }}</button>
<button @click.prevent="showImages('allImages')" :class="isActive('allImages')" class="px-2 py-1 hover:bg-stone-700 hover:dark:bg-stone-900 hover:text-stone-50 transition duration-100">{{ $filters.translate('all pages') }}</button>
</div>
</div>
<div v-if="showfiles">
@@ -40,10 +40,10 @@ const medialib = {
</svg> click to select
</span>
</a>
<div class="flex">
<div class="flex bg-stone-50 dark:bg-stone-600">
<div class="w-3/4 truncate p-3">{{ image.name }}</div>
<div class="w-1/4 flex">
<button @click.prevent="showImageDetails(image,index)" class="w-1/2 bg-stone-50 hover:bg-teal-500 hover:text-white transition duration-100">
<button @click.prevent="showImageDetails(image,index)" class="w-1/2 hover:bg-teal-500 hover:text-white transition duration-100">
<svg class="icon icon-info">
<use xlink:href="#icon-info"></use>
</svg>
@@ -61,41 +61,41 @@ const medialib = {
<Transition name="fade">
<div class="px-5" v-if="showimagedetails">
<div class="flex flex-wrap item-start relative">
<div class="w-1/2 bg-stone-50">
<div class="w-1/2 bg-stone-50 dark:bg-stone-600">
<div class="w-80 h-80 table-cell align-middle bg-chess">
<img :src="getImageUrl(imagedetaildata.src_live)" class="max-w-xs max-h-80 table mx-auto">
</div>
</div>
<div class="w-1/2 bg-stone-50 p-4 text-xs">
<div class="text-stone-500 mt-3 mb-1">Name</div>
<div class="w-1/2 bg-stone-50 dark:bg-stone-600 p-4 text-xs">
<div class="text-stone-500 dark:text-stone-300 mt-3 mb-1">Name</div>
<div class="font-bold">{{ imagedetaildata.name}}</div>
<div class="text-stone-500 mt-3 mb-1">URL</div>
<div class="text-stone-500 dark:text-stone-300 mt-3 mb-1">URL</div>
<div class="font-bold">{{ getImageUrl(imagedetaildata.src_live)}}</div>
<div class="flex flex-wrap item-start">
<div class="w-1/2">
<div class="text-stone-500 mt-3 mb-1">Size</div>
<div class="text-stone-500 dark:text-stone-300 mt-3 mb-1">Size</div>
<div class="font-bold">{{ getSize(imagedetaildata.bytes) }}</div>
</div>
<div class="w-1/2">
<div class="text-stone-500 mt-3 mb-1">Dimensions</div>
<div class="text-stone-500 dark:text-stone-300 mt-3 mb-1">Dimensions</div>
<div class="font-bold">{{ imagedetaildata.width }}x{{ imagedetaildata.height }} px</div>
</div>
<div class="w-1/2">
<div class="text-stone-500 mt-3 mb-1">Type</div>
<div class="text-stone-500 dark:text-stone-300 mt-3 mb-1">Type</div>
<div class="font-bold">{{ imagedetaildata.type }}</div>
</div>
<div class="w-1/2">
<div class="text-stone-500 mt-3 mb-1">Date</div>
<div class="text-stone-500 dark:text-stone-300 mt-3 mb-1">Date</div>
<div class="font-bold">{{ getDate(imagedetaildata.timestamp) }}</div>
</div>
</div>
<div class="w-full flex justify-between mt-8">
<button @click.prevent="selectImage(imagedetaildata)" class="w-1/2 p-2 mr-2 bg-stone-200 hover:bg-teal-500 hover:text-white transition duration-100">
<button @click.prevent="selectImage(imagedetaildata)" class="w-1/2 p-2 mr-2 bg-stone-200 dark:bg-stone-900 hover:bg-teal-500 hover:dark:bg-teal-500 hover:text-white transition duration-100">
<svg class="icon icon-check">
<use xlink:href="#icon-check"></use>
</svg> select
</button>
<button @click.prevent="deleteImage(imagedetaildata, detailindex)" class="w-1/2 p-2 bg-stone-200 hover:bg-rose-500 hover:text-white transition duration-100">
<button @click.prevent="deleteImage(imagedetaildata, detailindex)" class="w-1/2 p-2 bg-stone-200 dark:bg-stone-900 hover:bg-rose-500 hover:dark:bg-rose-500 hover:text-white transition duration-100">
<svg class="icon icon-trash-o baseline">
<use xlink:href="#icon-trash-o"></use>
</svg> delete
@@ -316,9 +316,9 @@ const medialib = {
{
if(this.active == activestring)
{
return 'bg-stone-700 text-stone-50';
return 'bg-stone-700 dark:bg-stone-900 text-stone-50';
}
return 'bg-stone-200';
return 'bg-stone-200 dark:bg-stone-600';
},
getBackgroundImage(image)
{

View File

@@ -5,8 +5,8 @@ const app = Vue.createApp({
v-for="tab in tabs"
v-on:click="currentTab = tab"
:key="tab"
class="px-4 py-2 border-b-2 border-stone-200 hover:border-stone-700 hover:bg-stone-50 transition duration-100"
:class="(tab == currentTab) ? 'bg-stone-50 border-stone-700' : ''"
class="px-4 py-2 border-b-2 border-stone-200 hover:border-stone-700 hover:bg-stone-50 dark:text-stone-200 dark:bg-stone-700 dark:border-stone-600 hover:dark:bg-stone-200 hover:dark:text-stone-900 transition duration-100"
:class="(tab == currentTab) ? 'bg-stone-50 border-stone-700 dark:bg-stone-200 dark:text-stone-900' : ''"
>
{{ $filters.translate(tab) }}
</button>
@@ -146,18 +146,17 @@ const app = Vue.createApp({
{
if(error.response)
{
self.message = 'please correct the errors above';
self.messageClass = 'bg-rose-500';
self.formErrors = error.response.data.errors;
self.message = 'please correct the errors above';
let message = handleErrorMessage(error);
/* does it make sense to change logic and show errors in publisher?
if(message)
if(typeof error.response.data.message != "undefined")
{
eventBus.$emit('publishermessage', message);
self.message = error.response.data.message;
}
if(typeof error.response.data.errors != "undefined")
{
self.formErrors = error.response.data.errors;
}
*/
}
});
},
@@ -174,14 +173,14 @@ app.component('tab-meta', {
disabled: true,
}
},
template: `<section>
template: `<section class="dark:bg-stone-700 dark:text-stone-200">
<form>
<div v-if="slug !== false">
<div class="w-full relative">
<label class="block mb-1 font-medium">{{ $filters.translate('Slug') }}</label>
<div class="flex">
<input
class="h-12 w-3/4 border px-2 py-3 border-stone-300 bg-stone-200"
class="h-12 w-3/4 border px-2 py-3 border-stone-300 bg-stone-200 text-stone-900"
type="text"
v-model="slug"
pattern="[a-z0-9]"
@@ -223,7 +222,7 @@ app.component('tab-meta', {
</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="saveInput()" :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="saveInput()" :value="$filters.translate('save')" class="w-full p-3 my-1 bg-stone-700 dark:bg-stone-600 hover:bg-stone-900 hover:dark:bg-stone-900 text-white cursor-pointer transition duration-100">
</div>
</form>
</section>`,

View File

@@ -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-8 bg-stone-100 dark:bg-stone-600 border border-stone-200">
<li v-for="(plugin,pluginname) in formDefinitions" class="w-full my-8 bg-stone-100 dark:bg-stone-700 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 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">
<button v-if="hasSettings(pluginname)" @click="setCurrent(pluginname)" class="flex-1 flex items-center justify-center space-x-4 p-3 bg-stone-700 dark:bg-stone-600 hover:bg-stone-900 hover:dark: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>
@@ -56,7 +56,7 @@ const app = Vue.createApp({
<div class="my-5">
<div :class="messageClass" class="block w-full h-8 px-3 py-1 my-1 text-white transition duration-100">{{ message }}</div>
<div class="w-full mt-6 flex justify-between">
<button type="submit" @click.prevent="save()" class="flex-1 p-3 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">{{ $filters.translate('Save') }}</button>
<button type="submit" @click.prevent="save()" class="flex-1 p-3 bg-stone-700 dark:bg-stone-600 hover:bg-stone-900 hover:dark:bg-stone-900 text-white cursor-pointer transition duration-100">{{ $filters.translate('Save') }}</button>
<a v-if="!checkLicense(license, plugin.license)" href="https://typemill.net/buy" target="_blank" class="flex-1 ml-3 p-3 py-4 text-center bg-teal-500 hover:bg-teal-600 text-white cursor-pointer transition duration-100">{{ $filters.translate('Buy a license') }}</a>
<a v-else-if="plugin.paypal" :href="plugin.paypal" target="_blank" class="flex-1 ml-3 p-3 py-4 text-center bg-teal-500 hover:bg-teal-600 text-white cursor-pointer transition duration-100">{{ $filters.translate('Donate') }} {{plugin.amount}},-</a>
</div>

View File

@@ -2,16 +2,16 @@ const publisher = Vue.createApp({
template: `
<div id="publishController" class="text-sm" v-cloak>
<div v-if="message" :class="messageClass" class="block w-full px-3 py-1 text-white transition duration-100">{{ message }}</div>
<div class="flex justify-between px-6 py-3">
<div class="flex justify-between px-6 py-3 dark:bg-stone-900">
<div class="flex">
<div class="border-l-4 w-32 px-2 py-2" :class="getStatusClass(item.status)">
<div class="border-l-4 w-32 px-2 py-2 dark:text-stone-200" :class="getStatusClass(item.status)">
{{ $filters.translate(item.status) }}
</div>
<button
v-if="raw"
@click.prevent="saveDraft"
:disabled="nochanges"
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
class="cursor-pointer ml-1 w-24 px-4 py-2 border dark:border-0 border-stone-200 text-white disabled:bg-stone-200 disabled:text-stone-900 disabled:dark:bg-stone-600 disabled:dark:text-stone-200 disabled:cursor-not-allowed transition"
:class="publishClass"
>
{{ $filters.translate('draft') }}
@@ -20,7 +20,7 @@ const publisher = Vue.createApp({
v-if="raw"
@click.prevent="publishDraft"
:disabled="nopublish"
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
class="cursor-pointer ml-1 w-24 px-4 py-2 border dark:border-0 border-stone-200 text-white disabled:bg-stone-200 disabled:text-stone-900 disabled:dark:bg-stone-600 disabled:dark:text-stone-200 disabled:cursor-not-allowed transition"
:class="publishClass"
>
{{ $filters.translate('publish') }}
@@ -29,7 +29,7 @@ const publisher = Vue.createApp({
v-if="visual"
@click.prevent="publishArticle"
:disabled="isPublished"
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
class="cursor-pointer ml-1 w-24 px-4 py-2 border dark:border-0 border-stone-200 text-white disabled:bg-stone-200 disabled:text-stone-900 disabled:dark:bg-stone-600 disabled:dark:text-stone-200 disabled:cursor-not-allowed transition"
:class="publishClass"
>
{{ $filters.translate('publish') }}
@@ -37,7 +37,7 @@ const publisher = Vue.createApp({
<button
@click.prevent="showModal = 'discard'"
:disabled="!isModified"
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white bg-yellow-500 hover:bg-yellow-600 disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
class="cursor-pointer ml-1 w-24 px-4 py-2 border dark:border-0 border-stone-200 text-white bg-yellow-500 hover:bg-yellow-600 disabled:bg-stone-200 disabled:text-stone-900 disabled:dark:bg-stone-600 disabled:dark:text-stone-200 disabled:cursor-not-allowed transition"
>
{{ $filters.translate('discard') }}
</button>
@@ -45,14 +45,14 @@ const publisher = Vue.createApp({
v-if="item.originalName != 'home'"
@click.prevent="checkUnpublish"
:disabled="isUnpublished"
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 text-white bg-teal-500 hover:bg-teal-600 disabled:bg-stone-50 disabled:text-stone-900 disabled:cursor-not-allowed transition"
class="cursor-pointer ml-1 w-24 px-4 py-2 border dark:border-0 border-stone-200 text-white bg-teal-500 hover:bg-teal-600 disabled:bg-stone-200 disabled:text-stone-900 disabled:dark:bg-stone-600 disabled:dark:text-stone-200 disabled:cursor-not-allowed transition"
>
{{ $filters.translate('unpublish') }}
</button>
<button
v-if="item.originalName != 'home'"
@click.prevent="showModal = 'delete'"
class="cursor-pointer ml-1 w-24 px-4 py-2 border border-stone-200 bg-stone-50 hover:bg-rose-500 hover:text-white transition"
class="cursor-pointer ml-1 w-24 px-4 py-2 border dark:border-0 border-stone-200 bg-stone-50 hover:bg-rose-500 hover:text-white transition"
>
{{ $filters.translate('delete') }}
</button>

View File

@@ -1,12 +1,12 @@
const raweditor = Vue.createApp({
template: `
<fieldset v-if="showraw" class="px-12 py-8 bg-stone-50 shadow-md mb-16">
<fieldset v-if="showraw" class="px-12 py-8 bg-stone-50 dark:bg-stone-700 dark:text-stone-200 shadow-md mb-16">
<div class="w-full px-6 py-3" :class="{'error' : errors.title}">
<label class="block mb-1 font-medium" for="title">{{ $filters.translate('Title') }}*</label>
<input
name="title"
type="text"
class="w-full p-4 text-white bg-stone-700 text-3xl"
class="w-full p-4 text-white bg-stone-700 dark:bg-stone-900 text-3xl"
v-model="title"
@input="updateTitle"
required
@@ -20,7 +20,7 @@ const raweditor = Vue.createApp({
id="rawcontent"
name="raweditor"
data-el="editor"
class="editor"
class="editor dark:bg-stone-900 dark:border-stone-600"
ref="raweditor"
v-model="content"
@input="updateBody"

View File

@@ -4,12 +4,12 @@ const modal = {
<div class="fixed w-full h-100 inset-0 z-50 overflow-hidden flex justify-center items-center bg-stone-700 bg-opacity-90">
<div class="border border-teal-500 dark:border-stone-200 shadow-lg bg-white dark:bg-stone-600 w-11/12 md:max-w-md mx-auto shadow-lg z-50 overflow-y-auto">
<div class="text-left p-6">
<div class="text-2xl font-bold">
<div class="text-2xl font-bold text-stone-900 dark:text-stone-200">
<slot name="header">
default header
</slot>
</div>
<div class="my-5">
<div class="my-5 text-stone-900 dark:text-stone-200">
<slot name="body">
default body
</slot>
@@ -72,7 +72,7 @@ const loginform = Vue.createApp({
template: `<transition name="initial" appear>
<div v-if="show" class="fixed w-full h-100 inset-0 z-50 overflow-hidden flex justify-center items-center bg-stone-700 bg-opacity-90">
<div class="border border-teal-500 dark:border-stone-200 shadow-lg bg-white dark:bg-stone-600 w-11/12 md:max-w-md mx-auto shadow-lg z-50 overflow-y-auto">
<div class="text-left p-6">
<div class="text-stone-900 dark:text-stone-200 text-left p-6">
<div class="text-2xl font-bold"><h2>You are logged out</h2></div>
<div class="my-5">
<p>You can visit the login page and authenticate again. Or you can close this window but you cannot perform any actions.</p>

View File

@@ -7,7 +7,7 @@ const systemnavi = Vue.createApp({
</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 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'">
<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 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 dark:text-stone-50'">
<svg class="icon {{ navitem.icon }} mr-2"><use xlink:href="#{{ navitem.icon }}"></use></svg> {{ $filters.translate(navitem.title) }}
</a>
</li>

View File

@@ -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-8 bg-stone-100 dark:bg-stone-600 border border-stone-200">
<li v-for="(theme,themename) in formDefinitions" class="w-full my-8 bg-stone-100 dark:bg-stone-700 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>
@@ -26,7 +26,7 @@ const app = Vue.createApp({
</div>
</div>
<div class="w-full mt-6 flex justify-between">
<button @click="setCurrent(themename)" 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 @click="setCurrent(themename)" class="flex-1 flex items-center justify-center space-x-4 p-3 bg-stone-700 dark:bg-stone-600 hover:bg-stone-900 hover:dark:bg-stone-900 text-white cursor-pointer transition duration-100">
<span>Configure</span>
<span :class="(current == themename) ? '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>
@@ -61,7 +61,7 @@ const app = Vue.createApp({
<div class="my-5">
<div :class="messageClass" class="block w-full h-8 px-3 py-1 my-1 text-white transition duration-100">{{ message }}</div>
<div class="w-full mt-6 flex justify-between">
<button type="submit" @click.prevent="save()" class="flex-1 p-3 bg-stone-700 hover:bg-stone-900 text-white cursor-pointer transition duration-100">Save</button>
<button type="submit" @click.prevent="save()" class="flex-1 p-3 bg-stone-700 dark:bg-stone-600 hover:bg-stone-900 hover:dark:bg-stone-900 text-white cursor-pointer transition duration-100">Save</button>
<a v-if="!checkLicense(license, theme.license)" href="https://typemill.net/buy" target="_blank" class="flex-1 ml-3 p-3 py-4 text-center bg-teal-500 hover:bg-teal-600 text-white cursor-pointer transition duration-100">Buy a license</a>
<a v-else-if="theme.paypal" :href="theme.paypal" target="_blank" class="flex-1 ml-3 p-3 py-4 text-center bg-teal-500 hover:bg-teal-600 text-white cursor-pointer transition duration-100">Donate {{theme.amount}},-</a>
</div>

View File

@@ -31,7 +31,7 @@ const app = Vue.createApp({
</div>
</form>
<div class="my-5 text-center">
<button @click.prevent="showModal = true" class="p-3 px-4 text-rose-500 border border-rose-100 hover:border-rose-500 cursor-pointer transition duration-100">{{ $filters.translate('delete user') }}</button>
<button @click.prevent="showModal = true" class="p-3 px-4 text-rose-500 border border-rose-100 dark:border-stone-600 hover:dark:border-rose-500 hover:border-rose-500 cursor-pointer transition duration-100">{{ $filters.translate('delete user') }}</button>
<modal v-if="showModal" @close="showModal = false">
<template #header>
<h3>{{ $filters.translate('Delete user') }}</h3>

View File

@@ -184,7 +184,7 @@ app.component('searchbox', {
<button @click.prevent="setFilter('usermail')" :class="checkActive('usermail')" class="px-2 py-2 border-b-4 hover:bg-stone-200 hover:border-stone-700 transition duration-100">{{ $filters.translate('e-mail') }}</button>
</div>
<div class="w-100 lg:flex">
<select v-if="this.filter == 'userrole'" v-model="searchterm" class="lg:w-3/4 w-full h-12 px-2 py-3 border border-stone-300 bg-stone-200">
<select v-if="this.filter == 'userrole'" v-model="searchterm" class="lg:w-3/4 w-full h-12 px-2 py-3 text-stone-900 border border-stone-300 bg-stone-200">
<option v-for="role in userroles">{{role}}</option>
</select>
<input v-else type="text" v-model="searchterm" class="lg:w-3/4 w-full h-12 px-2 py-3 border border-stone-300 bg-stone-200 text-stone-900">

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en" class="{{ settings.darkmode ? 'dark' : 'darkmodeoff' }}" >
<html lang="en" class="{{ darkmode ? 'dark' : 'darkmodeoff' }}" >
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en" class="{{ settings.darkmode ? 'dark' : 'darkmodeoff' }}" >
<html lang="en" class="{{ darkmode ? 'dark' : 'darkmodeoff' }}" >
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en" class="{{ settings.darkmode ? 'dark' :'darkmodeoff' }}" >
<html lang="en" class="{{ darkmode ? 'dark' :'darkmodeoff' }}" >
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>

View File

@@ -5,7 +5,7 @@
</div>
<ul class="flex border-l-2 border-stone-200 dark:border-stone-600">
{% for name,navitem in mainnavi %}
{% if settings.darkmode %}
{% if 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>

View File

@@ -64,7 +64,7 @@ $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) {
$group->post('/article/rename', ControllerApiAuthorArticle::class . ':renameArticle')->setName('api.article.rename')->add(new ApiAuthorization($acl, 'content', 'publish'));
$group->post('/article/publish', ControllerApiAuthorArticle::class . ':publishArticle')->setName('api.article.publish')->add(new ApiAuthorization($acl, 'content', 'publish'));
$group->delete('/article/unpublish', ControllerApiAuthorArticle::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new ApiAuthorization($acl, 'content', 'unpublish'));
$group->delete('/article/discard', ControllerApiAuthorArticle::class . ':discardArticleChanges')->setName('api.article.discard')->add(new ApiAuthorization($acl, 'content', 'edit'));
$group->delete('/article/discard', ControllerApiAuthorArticle::class . ':discardArticleChanges')->setName('api.article.discard')->add(new ApiAuthorization($acl, 'content', 'update'));
$group->delete('/article', ControllerApiAuthorArticle::class . ':deleteArticle')->setName('api.article.delete')->add(new ApiAuthorization($acl, 'content', 'delete'));
$group->post('/article', ControllerApiAuthorArticle::class . ':createArticle')->setName('api.article.create')->add(new ApiAuthorization($acl, 'content', 'create')); # author
$group->put('/draft', ControllerApiAuthorArticle::class . ':updateDraft')->setName('api.draft.update')->add(new ApiAuthorization($acl, 'content', 'create')); # author
@@ -72,10 +72,10 @@ $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) {
$group->post('/post', ControllerApiAuthorArticle::class . ':createPost')->setName('api.post.create')->add(new ApiAuthorization($acl, 'content', 'create'));
# BLOCKS
$group->post('/block', ControllerApiAuthorBlock::class . ':addBlock')->setName('api.block.add')->add(new ApiAuthorization($acl, 'mycontent', 'create'));
$group->post('/block', ControllerApiAuthorBlock::class . ':addBlock')->setName('api.block.add')->add(new ApiAuthorization($acl, 'mycontent', 'update'));
$group->put('/block/move', ControllerApiAuthorBlock::class . ':moveBlock')->setName('api.block.move')->add(new ApiAuthorization($acl, 'mycontent', 'view'));
$group->put('/block', ControllerApiAuthorBlock::class . ':updateBlock')->setName('api.block.update')->add(new ApiAuthorization($acl, 'mycontent', 'update'));
$group->delete('/block', ControllerApiAuthorBlock::class . ':deleteBlock')->setName('api.block.delete')->add(new ApiAuthorization($acl, 'mycontent', 'delete'));
$group->delete('/block', ControllerApiAuthorBlock::class . ':deleteBlock')->setName('api.block.delete')->add(new ApiAuthorization($acl, 'mycontent', 'update'));
$group->post('/video', ControllerApiImage::class . ':saveVideoImage')->setName('api.video.save')->add(new ApiAuthorization($acl, 'mycontent', 'view'));
# SHORTCODE

View File

@@ -77,4 +77,13 @@ if(isset($routes['web']) && !empty($routes['web']))
}
}
$app->get('/[{route:.*}]', ControllerWebFrontend::class . ':index')->setName('home');
if(isset($settings['access']) && $settings['access'] != '')
{
# if access for website is restricted
$app->get('/[{route:.*}]', ControllerWebFrontend::class . ':index')->setName('home')->add(new WebAuthorization($routeParser, $acl, 'account', 'view'));
}
else
{
# if access is not restricted
$app->get('/[{route:.*}]', ControllerWebFrontend::class . ':index')->setName('home');
}

View File

@@ -78,20 +78,20 @@ meta:
hidden: true
css: hidden
pattern: '[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
fieldsetreference:
type: fieldset
legend: Reference
fields:
reference:
type: text
label: Reference to page
maxlength: 60
referencetype:
type: radio
label: Type of reference
options:
copy: Copy (copy the content of the referenced page)
redirect: Redirect (redirect the user to the referenced page)
# fieldsetreference:
# type: fieldset
# legend: Reference
# fields:
# reference:
# type: text
# label: Reference to page
# maxlength: 60
# referencetype:
# type: radio
# label: Type of reference
# options:
# copy: Copy (copy the content of the referenced page)
# redirect: Redirect (redirect the user to the referenced page)
fieldsetvisibility:
type: fieldset
legend: Visibility

View File

@@ -14,22 +14,22 @@ contributor:
- 'view'
- 'create'
- 'update'
- 'delete'
author:
name: author
inherits: contributor
permissions:
content:
- 'create'
- 'view'
editor:
name: editor
inherits: author
permissions:
mycontent:
- 'delete'
- 'publish'
- 'unpublish'
content:
- 'create'
- 'update'
- 'delete'
- 'publish'

View File

@@ -53,11 +53,6 @@ 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

View File

@@ -31,4 +31,9 @@ newpassword:
label: 'New Password'
type: 'password'
autocomplete: 'new-password'
generator: true
generator: true
darkmode:
name: darkmode
label: 'Darkmode'
checkboxlabel: 'Activate the darkmode for me'
type: 'checkbox'

View File

@@ -1,10 +1,10 @@
{% set home = {
"landingpageIntro" : settings.themes.cyanine.landingpageIntro,
"landingpageInfo" : settings.themes.cyanine.landingpageInfo,
"landingpageTeaser" : settings.themes.cyanine.landingpageTeaser,
"landingpageContrast" : settings.themes.cyanine.landingpageContrast,
"landingpageNavi" : settings.themes.cyanine.landingpageNavi,
"landingpageNews" : settings.themes.cyanine.landingpageNews
"landingpageIntro" : settings.themes.cyanine.introPosition,
"landingpageInfo" : settings.themes.cyanine.infoPosition,
"landingpageTeaser" : settings.themes.cyanine.teaserPosition,
"landingpageContrast" : settings.themes.cyanine.contrastPosition,
"landingpageNavi" : settings.themes.cyanine.naviPosition,
"landingpageNews" : settings.themes.cyanine.newsPosition
}
%}

View File

@@ -3,7 +3,7 @@
{% block title %}{{ metatabs.meta.title }} | {{ settings.title }}{% endblock %}
{% block content %}
{% if home and settings.themes.cyanine.landingpage %}
{% include 'home.twig' %}