diff --git a/content/00-welcome/00-markdown-test.yaml b/content/00-welcome/00-markdown-test.yaml index f9ec15c..043ea59 100644 --- a/content/00-welcome/00-markdown-test.yaml +++ b/content/00-welcome/00-markdown-test.yaml @@ -1,8 +1,18 @@ meta: navtitle: 'markdown test' + title: 'Markdown is a simple and universal syntax for text formatting.' + description: 'Developers love markdown, because it is much cleaner and saver than HTML. And they can easily convert markdown to a lot of other document formats like HTML and' + heroimage: null + heroimagealt: null owner: Sebastian + author: null + allowedrole: null + alloweduser: null + manualdate: null + modified: '2023-05-11' created: '2023-06-12' time: 22-36-41 - modified: '2023-05-11' - title: 'Markdown is a simple and universal syntax for text formatting. More and more writers switch to markdown, because they can format their text during the writing process without using any format-buttons. Once they are familiar with the markdown syntax, they can write formatted text much easier and faster than with any standard HTML-editor.' - description: 'Developers love markdown, because it is much cleaner and saver than HTML. And they can easily convert markdown to a lot of other document formats like HTML and' + reference: null + referencetype: null + hide: false + noindex: false diff --git a/content/00-welcome/01-manage-access.yaml b/content/00-welcome/01-manage-access.yaml index 6101b43..4ade9cc 100644 --- a/content/00-welcome/01-manage-access.yaml +++ b/content/00-welcome/01-manage-access.yaml @@ -1,6 +1,6 @@ meta: navtitle: 'manage access' - owner: Sebastian + owner: Unknown created: '2023-06-12' time: 22-36-36 modified: '2023-05-06' diff --git a/content/00-welcome/03-setup-your-website.yaml b/content/00-welcome/03-setup-your-website.yaml index 7d9343d..805e9b8 100644 --- a/content/00-welcome/03-setup-your-website.yaml +++ b/content/00-welcome/03-setup-your-website.yaml @@ -1,6 +1,6 @@ meta: navtitle: 'setup your website' - owner: Sebastian + owner: Unknown created: '2023-06-12' time: 22-36-14 modified: '2023-03-26' diff --git a/content/00-welcome/04-write-content.yaml b/content/00-welcome/04-write-content.yaml index 4538849..010a251 100644 --- a/content/00-welcome/04-write-content.yaml +++ b/content/00-welcome/04-write-content.yaml @@ -1,6 +1,6 @@ meta: navtitle: 'write content' - owner: Sebastian + owner: Unknown created: '2023-06-12' time: 22-09-48 modified: '2023-05-11' diff --git a/content/00-welcome/05-todos.md b/content/00-welcome/05-todos.md index 1bb08d5..a72a3db 100644 --- a/content/00-welcome/05-todos.md +++ b/content/00-welcome/05-todos.md @@ -33,6 +33,7 @@ Biig blocks: * DONE: Media Library * DONE: Posts +* DONE: Setup * Recover Password Small features: @@ -47,6 +48,7 @@ Small features: * Editor: Warn if open another block * Image generation on the fly * Assets +* Bug: Table of content duplicated for published pages Cleanups: diff --git a/content/00-welcome/05-todos.txt b/content/00-welcome/05-todos.txt new file mode 100644 index 0000000..c30cc9d --- /dev/null +++ b/content/00-welcome/05-todos.txt @@ -0,0 +1 @@ +["# ToDos Version 2","[TOC]","## Visual Editor","* FIXED: File is not published from tmp to media\/files if you save the block.","## Raw Editor","* DONE ready","## Medialib","* DONE","## Posts","* Setup","## Plugins","* Asset Class","## Frontend","* DONE\n* DONE: Test restrictions","## ToDos","Biig blocks:","* DONE: Media Library\n* DONE: Posts\n* DONE: Setup\n* DONE: Recover Password","Small features:","* Sitemap and ping\n* Captcha\n* Clear Cache\n* Security Log\n* Backend fields\n* Proxy\n* DONE: Session handling: csrf fail and session start error if restrictions are active\n* Editor: Warn if open another block\n* Image generation on the fly\n* Assets\n* Bug: Table of content duplicated for published pages\n* Bug: Navigation frontend if unpublished pages","Cleanups:","* Events\n* Error messages\n* Translations","## Select userroles","* Userroles for file restriction: in vue-blox-components loaded via api\n* Userroles for userfields: in php model user getUserFields()\n* Userroles for meta: in php controller apiAuthorMeta getMeta()\n* Plugins and themes: in php model extension getThemeDefinitions()","## License Check","* On activation in apiControllerExtension. It checks the license in yaml.\n* In plugin php code with setPremiumLicense\n* In static plugins, it checks manual premium list and method setPremiumLicense and more "] \ No newline at end of file diff --git a/content/00-welcome/05-todos.yaml b/content/00-welcome/05-todos.yaml index 1a374d9..36bebfe 100644 --- a/content/00-welcome/05-todos.yaml +++ b/content/00-welcome/05-todos.yaml @@ -4,7 +4,7 @@ meta: description: ' Visual Editor with more stuff' heroimage: null heroimagealt: null - owner: Sebastian + owner: Unknown author: null allowedrole: contributor alloweduser: null diff --git a/content/01-cyanine-theme/00-new.md b/content/01-cyanine-theme/00-new.md new file mode 100644 index 0000000..16bc8a3 --- /dev/null +++ b/content/01-cyanine-theme/00-new.md @@ -0,0 +1,51 @@ +# ToDo + +[TOC] + +## Visual Editor + +Das ist ein Intro. Es gibt noch viel zu tun, aber das ist am Ende des Tages nicht so schlimm denn wir machen einfach weiter. + +* DONE: Fix toc component in new block +* DONE: Fix hr component in new block +* DONE: finish shortcode component +* DONE: Fix inline formats +* DONE: fix lenght of page +* DONE: Fix design of new block at the end (background color) +* DONE: Move Block +* DONE: Fix headline design +* DONE: Fix save on two enter +* DONE: fix quote design +* DONE: Fix toc preview +* DONE: disable enable +* DONE: Add load sign (from navigation) +* SHIT!!! fiinish youtube component + +## Navigation + +* DONE: fix status in navigation +* fix error messages +* DONE: refresh navigation after changes + +## Publish Controller + +* DONE: Create +* DONE: publish +* DONE: unpublish +* DONE: discard +* DONE: delete +* DONE: save draft +* DONE: switch to raw + +## Raw Editor + +* DONE: + +## Meta Tabs + +* Setup + +## Medialib + +* Setup + diff --git a/content/01-cyanine-theme/00-new.txt b/content/01-cyanine-theme/00-new.txt deleted file mode 100644 index 82b2e4c..0000000 --- a/content/01-cyanine-theme/00-new.txt +++ /dev/null @@ -1 +0,0 @@ -["# ToDo","[TOC]","## Visual Editor","Das ist ein Intro. Es gibt noch viel zu tun, aber das ist am Ende des Tages nicht so schlimm denn wir machen einfach weiter.","* DONE: Fix toc component in new block\n* DONE: Fix hr component in new block\n* DONE: finish shortcode component\n* DONE: Fix inline formats\n* DONE: fix lenght of page\n* DONE: Fix design of new block at the end (background color)\n* DONE: Move Block\n* DONE: Fix headline design\n* DONE: Fix save on two enter\n* DONE: fix quote design\n* DONE: Fix toc preview\n* DONE: disable enable \n* DONE: Add load sign (from navigation)\n* SHIT!!! fiinish youtube component","## Navigation","* DONE: fix status in navigation\n* fix error messages\n* DONE: refresh navigation after changes","## Publish Controller","* DONE: Create \n* DONE: publish\n* DONE: unpublish\n* DONE: discard\n* DONE: delete\n* DONE: save draft\n* DONE: switch to raw","## Raw Editor","* DONE:","## Meta Tabs","* Setup","## Medialib","* Setup"] \ No newline at end of file diff --git a/content/01-cyanine-theme/00-new.yaml b/content/01-cyanine-theme/00-new.yaml index 194e33a..4275f9e 100644 --- a/content/01-cyanine-theme/00-new.yaml +++ b/content/01-cyanine-theme/00-new.yaml @@ -1,2 +1,8 @@ meta: navtitle: new + owner: Unknown + created: '2023-07-20' + time: 14-39-14 + modified: '2023-07-20' + title: ToDo + description: ' Visual Editor' diff --git a/content/01-cyanine-theme/01-landingpage.yaml b/content/01-cyanine-theme/01-landingpage.yaml index 3cac26e..ecb9df8 100644 --- a/content/01-cyanine-theme/01-landingpage.yaml +++ b/content/01-cyanine-theme/01-landingpage.yaml @@ -4,7 +4,7 @@ meta: description: "\nIntro with the content of the home page and an additional link/button.\nInfo with individual markdown content.\nTeaser with two elements. Each element has a" heroimage: null heroimagealt: null - owner: Sebastian + owner: Unknown author: null allowedrole: null alloweduser: null diff --git a/data/navigation/navi-draft.txt b/data/navigation/navi-draft.txt index 7b455b5..75c26a8 100644 --- a/data/navigation/navi-draft.txt +++ b/data/navigation/navi-draft.txt @@ -1 +1 @@ -a:2:{i:0;O:8:"stdClass":22:{s:12:"originalName";s:10:"00-welcome";s:11:"elementType";s:6:"folder";s:8:"contains";s:5:"pages";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"00";s:4:"name";s:7:"welcome";s:4:"slug";s:7:"welcome";s:4:"path";s:11:"/00-welcome";s:15:"pathWithoutType";s:17:"/00-welcome/index";s:9:"urlRelWoF";s:8:"/welcome";s:6:"urlRel";s:17:"/typemill/welcome";s:6:"urlAbs";s:33:"http://localhost/typemill/welcome";s:3:"key";i:0;s:7:"keyPath";i:0;s:12:"keyPathArray";a:1:{i:0;s:1:"0";}s:7:"chapter";i:1;s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:13:"folderContent";a:6:{i:0;O:8:"stdClass":20:{s:12:"originalName";s:19:"00-markdown-test.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"00";s:4:"name";s:13:"markdown test";s:4:"slug";s:13:"markdown-test";s:4:"path";s:31:"/00-welcome/00-markdown-test.md";s:15:"pathWithoutType";s:28:"/00-welcome/00-markdown-test";s:3:"key";i:0;s:7:"keyPath";s:3:"0.0";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"0";}s:7:"chapter";s:3:"1.1";s:9:"urlRelWoF";s:22:"/welcome/markdown-test";s:6:"urlRel";s:31:"/typemill/welcome/markdown-test";s:6:"urlAbs";s:47:"http://localhost/typemill/welcome/markdown-test";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:1;O:8:"stdClass":20:{s:12:"originalName";s:19:"01-manage-access.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"01";s:4:"name";s:13:"manage access";s:4:"slug";s:13:"manage-access";s:4:"path";s:31:"/00-welcome/01-manage-access.md";s:15:"pathWithoutType";s:28:"/00-welcome/01-manage-access";s:3:"key";i:1;s:7:"keyPath";s:3:"0.1";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"1";}s:7:"chapter";s:3:"1.2";s:9:"urlRelWoF";s:22:"/welcome/manage-access";s:6:"urlRel";s:31:"/typemill/welcome/manage-access";s:6:"urlAbs";s:47:"http://localhost/typemill/welcome/manage-access";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:2;O:8:"stdClass":20:{s:12:"originalName";s:14:"02-get-help.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"02";s:4:"name";s:8:"get help";s:4:"slug";s:8:"get-help";s:4:"path";s:26:"/00-welcome/02-get-help.md";s:15:"pathWithoutType";s:23:"/00-welcome/02-get-help";s:3:"key";i:2;s:7:"keyPath";s:3:"0.2";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"2";}s:7:"chapter";s:3:"1.3";s:9:"urlRelWoF";s:17:"/welcome/get-help";s:6:"urlRel";s:26:"/typemill/welcome/get-help";s:6:"urlAbs";s:42:"http://localhost/typemill/welcome/get-help";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:3;O:8:"stdClass":20:{s:12:"originalName";s:24:"03-setup-your-website.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"03";s:4:"name";s:18:"setup your website";s:4:"slug";s:18:"setup-your-website";s:4:"path";s:36:"/00-welcome/03-setup-your-website.md";s:15:"pathWithoutType";s:33:"/00-welcome/03-setup-your-website";s:3:"key";i:3;s:7:"keyPath";s:3:"0.3";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"3";}s:7:"chapter";s:3:"1.4";s:9:"urlRelWoF";s:27:"/welcome/setup-your-website";s:6:"urlRel";s:36:"/typemill/welcome/setup-your-website";s:6:"urlAbs";s:52:"http://localhost/typemill/welcome/setup-your-website";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:4;O:8:"stdClass":20:{s:12:"originalName";s:19:"04-write-content.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"04";s:4:"name";s:13:"write content";s:4:"slug";s:13:"write-content";s:4:"path";s:31:"/00-welcome/04-write-content.md";s:15:"pathWithoutType";s:28:"/00-welcome/04-write-content";s:3:"key";i:4;s:7:"keyPath";s:3:"0.4";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"4";}s:7:"chapter";s:3:"1.5";s:9:"urlRelWoF";s:22:"/welcome/write-content";s:6:"urlRel";s:31:"/typemill/welcome/write-content";s:6:"urlAbs";s:47:"http://localhost/typemill/welcome/write-content";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:5;O:8:"stdClass":20:{s:12:"originalName";s:11:"05-todos.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"05";s:4:"name";s:6:"To Dos";s:4:"slug";s:5:"todos";s:4:"path";s:23:"/00-welcome/05-todos.md";s:15:"pathWithoutType";s:20:"/00-welcome/05-todos";s:3:"key";i:5;s:7:"keyPath";s:3:"0.5";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"5";}s:7:"chapter";s:3:"1.6";s:9:"urlRelWoF";s:14:"/welcome/todos";s:6:"urlRel";s:23:"/typemill/welcome/todos";s:6:"urlAbs";s:39:"http://localhost/typemill/welcome/todos";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}}s:7:"noindex";b:0;}i:1;O:8:"stdClass":22:{s:12:"originalName";s:16:"01-cyanine-theme";s:11:"elementType";s:6:"folder";s:8:"contains";s:5:"pages";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"01";s:4:"name";s:13:"cyanine theme";s:4:"slug";s:13:"cyanine-theme";s:4:"path";s:17:"/01-cyanine-theme";s:15:"pathWithoutType";s:23:"/01-cyanine-theme/index";s:9:"urlRelWoF";s:14:"/cyanine-theme";s:6:"urlRel";s:23:"/typemill/cyanine-theme";s:6:"urlAbs";s:39:"http://localhost/typemill/cyanine-theme";s:3:"key";i:1;s:7:"keyPath";i:1;s:12:"keyPathArray";a:1:{i:0;s:1:"1";}s:7:"chapter";i:2;s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:13:"folderContent";a:5:{i:0;O:8:"stdClass":20:{s:12:"originalName";s:10:"00-new.txt";s:11:"elementType";s:4:"file";s:6:"status";s:11:"unpublished";s:8:"fileType";s:3:"txt";s:5:"order";s:2:"00";s:4:"name";s:3:"new";s:4:"slug";s:3:"new";s:4:"path";s:28:"/01-cyanine-theme/00-new.txt";s:15:"pathWithoutType";s:24:"/01-cyanine-theme/00-new";s:3:"key";i:0;s:7:"keyPath";s:3:"1.0";s:12:"keyPathArray";a:2:{i:0;s:1:"1";i:1;s:1:"0";}s:7:"chapter";s:3:"2.1";s:9:"urlRelWoF";s:18:"/cyanine-theme/new";s:6:"urlRel";s:27:"/typemill/cyanine-theme/new";s:6:"urlAbs";s:43:"http://localhost/typemill/cyanine-theme/new";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:1;O:8:"stdClass":20:{s:12:"originalName";s:17:"01-landingpage.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"01";s:4:"name";s:11:"landingpage";s:4:"slug";s:11:"landingpage";s:4:"path";s:35:"/01-cyanine-theme/01-landingpage.md";s:15:"pathWithoutType";s:32:"/01-cyanine-theme/01-landingpage";s:3:"key";i:1;s:7:"keyPath";s:3:"1.1";s:12:"keyPathArray";a:2:{i:0;s:1:"1";i:1;s:1:"1";}s:7:"chapter";s:3:"2.2";s:9:"urlRelWoF";s:26:"/cyanine-theme/landingpage";s:6:"urlRel";s:35:"/typemill/cyanine-theme/landingpage";s:6:"urlAbs";s:51:"http://localhost/typemill/cyanine-theme/landingpage";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:1;}i:2;O:8:"stdClass":20:{s:12:"originalName";s:22:"02-content-elements.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"02";s:4:"name";s:16:"content elements";s:4:"slug";s:16:"content-elements";s:4:"path";s:40:"/01-cyanine-theme/02-content-elements.md";s:15:"pathWithoutType";s:37:"/01-cyanine-theme/02-content-elements";s:3:"key";i:2;s:7:"keyPath";s:3:"1.2";s:12:"keyPathArray";a:2:{i:0;s:1:"1";i:1;s:1:"2";}s:7:"chapter";s:3:"2.3";s:9:"urlRelWoF";s:31:"/cyanine-theme/content-elements";s:6:"urlRel";s:40:"/typemill/cyanine-theme/content-elements";s:6:"urlAbs";s:56:"http://localhost/typemill/cyanine-theme/content-elements";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:3;O:8:"stdClass":20:{s:12:"originalName";s:22:"03-colors-and-fonts.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"03";s:4:"name";s:16:"colors and fonts";s:4:"slug";s:16:"colors-and-fonts";s:4:"path";s:40:"/01-cyanine-theme/03-colors-and-fonts.md";s:15:"pathWithoutType";s:37:"/01-cyanine-theme/03-colors-and-fonts";s:3:"key";i:3;s:7:"keyPath";s:3:"1.3";s:12:"keyPathArray";a:2:{i:0;s:1:"1";i:1;s:1:"3";}s:7:"chapter";s:3:"2.4";s:9:"urlRelWoF";s:31:"/cyanine-theme/colors-and-fonts";s:6:"urlRel";s:40:"/typemill/cyanine-theme/colors-and-fonts";s:6:"urlAbs";s:56:"http://localhost/typemill/cyanine-theme/colors-and-fonts";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:4;O:8:"stdClass":20:{s:12:"originalName";s:12:"04-footer.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"04";s:4:"name";s:6:"footer";s:4:"slug";s:6:"footer";s:4:"path";s:30:"/01-cyanine-theme/04-footer.md";s:15:"pathWithoutType";s:27:"/01-cyanine-theme/04-footer";s:3:"key";i:4;s:7:"keyPath";s:3:"1.4";s:12:"keyPathArray";a:2:{i:0;s:1:"1";i:1;s:1:"4";}s:7:"chapter";s:3:"2.5";s:9:"urlRelWoF";s:21:"/cyanine-theme/footer";s:6:"urlRel";s:30:"/typemill/cyanine-theme/footer";s:6:"urlAbs";s:46:"http://localhost/typemill/cyanine-theme/footer";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}}s:7:"noindex";b:0;}} \ No newline at end of file +a:2:{i:0;O:8:"stdClass":22:{s:12:"originalName";s:10:"00-welcome";s:11:"elementType";s:6:"folder";s:8:"contains";s:5:"pages";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"00";s:4:"name";s:7:"welcome";s:4:"slug";s:7:"welcome";s:4:"path";s:11:"/00-welcome";s:15:"pathWithoutType";s:17:"/00-welcome/index";s:9:"urlRelWoF";s:8:"/welcome";s:6:"urlRel";s:17:"/typemill/welcome";s:6:"urlAbs";s:33:"http://localhost/typemill/welcome";s:3:"key";i:0;s:7:"keyPath";i:0;s:12:"keyPathArray";a:1:{i:0;s:1:"0";}s:7:"chapter";i:1;s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:13:"folderContent";a:6:{i:0;O:8:"stdClass":20:{s:12:"originalName";s:19:"00-markdown-test.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"00";s:4:"name";s:13:"markdown test";s:4:"slug";s:13:"markdown-test";s:4:"path";s:31:"/00-welcome/00-markdown-test.md";s:15:"pathWithoutType";s:28:"/00-welcome/00-markdown-test";s:3:"key";i:0;s:7:"keyPath";s:3:"0.0";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"0";}s:7:"chapter";s:3:"1.1";s:9:"urlRelWoF";s:22:"/welcome/markdown-test";s:6:"urlRel";s:31:"/typemill/welcome/markdown-test";s:6:"urlAbs";s:47:"http://localhost/typemill/welcome/markdown-test";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:1;O:8:"stdClass":20:{s:12:"originalName";s:19:"01-manage-access.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"01";s:4:"name";s:13:"manage access";s:4:"slug";s:13:"manage-access";s:4:"path";s:31:"/00-welcome/01-manage-access.md";s:15:"pathWithoutType";s:28:"/00-welcome/01-manage-access";s:3:"key";i:1;s:7:"keyPath";s:3:"0.1";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"1";}s:7:"chapter";s:3:"1.2";s:9:"urlRelWoF";s:22:"/welcome/manage-access";s:6:"urlRel";s:31:"/typemill/welcome/manage-access";s:6:"urlAbs";s:47:"http://localhost/typemill/welcome/manage-access";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:2;O:8:"stdClass":20:{s:12:"originalName";s:14:"02-get-help.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"02";s:4:"name";s:8:"get help";s:4:"slug";s:8:"get-help";s:4:"path";s:26:"/00-welcome/02-get-help.md";s:15:"pathWithoutType";s:23:"/00-welcome/02-get-help";s:3:"key";i:2;s:7:"keyPath";s:3:"0.2";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"2";}s:7:"chapter";s:3:"1.3";s:9:"urlRelWoF";s:17:"/welcome/get-help";s:6:"urlRel";s:26:"/typemill/welcome/get-help";s:6:"urlAbs";s:42:"http://localhost/typemill/welcome/get-help";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:3;O:8:"stdClass":20:{s:12:"originalName";s:24:"03-setup-your-website.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"03";s:4:"name";s:18:"setup your website";s:4:"slug";s:18:"setup-your-website";s:4:"path";s:36:"/00-welcome/03-setup-your-website.md";s:15:"pathWithoutType";s:33:"/00-welcome/03-setup-your-website";s:3:"key";i:3;s:7:"keyPath";s:3:"0.3";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"3";}s:7:"chapter";s:3:"1.4";s:9:"urlRelWoF";s:27:"/welcome/setup-your-website";s:6:"urlRel";s:36:"/typemill/welcome/setup-your-website";s:6:"urlAbs";s:52:"http://localhost/typemill/welcome/setup-your-website";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:4;O:8:"stdClass":20:{s:12:"originalName";s:19:"04-write-content.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"04";s:4:"name";s:13:"write content";s:4:"slug";s:13:"write-content";s:4:"path";s:31:"/00-welcome/04-write-content.md";s:15:"pathWithoutType";s:28:"/00-welcome/04-write-content";s:3:"key";i:4;s:7:"keyPath";s:3:"0.4";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"4";}s:7:"chapter";s:3:"1.5";s:9:"urlRelWoF";s:22:"/welcome/write-content";s:6:"urlRel";s:31:"/typemill/welcome/write-content";s:6:"urlAbs";s:47:"http://localhost/typemill/welcome/write-content";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:5;O:8:"stdClass":20:{s:12:"originalName";s:14:"05-todos.txtmd";s:11:"elementType";s:4:"file";s:6:"status";s:8:"modified";s:8:"fileType";s:3:"txt";s:5:"order";s:2:"05";s:4:"name";s:6:"To Dos";s:4:"slug";s:5:"todos";s:4:"path";s:26:"/00-welcome/05-todos.txtmd";s:15:"pathWithoutType";s:20:"/00-welcome/05-todos";s:3:"key";i:5;s:7:"keyPath";s:3:"0.5";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"5";}s:7:"chapter";s:3:"1.6";s:9:"urlRelWoF";s:14:"/welcome/todos";s:6:"urlRel";s:23:"/typemill/welcome/todos";s:6:"urlAbs";s:39:"http://localhost/typemill/welcome/todos";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}}s:7:"noindex";b:0;}i:1;O:8:"stdClass":22:{s:12:"originalName";s:16:"01-cyanine-theme";s:11:"elementType";s:6:"folder";s:8:"contains";s:5:"pages";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"01";s:4:"name";s:13:"cyanine theme";s:4:"slug";s:13:"cyanine-theme";s:4:"path";s:17:"/01-cyanine-theme";s:15:"pathWithoutType";s:23:"/01-cyanine-theme/index";s:9:"urlRelWoF";s:14:"/cyanine-theme";s:6:"urlRel";s:23:"/typemill/cyanine-theme";s:6:"urlAbs";s:39:"http://localhost/typemill/cyanine-theme";s:3:"key";i:1;s:7:"keyPath";i:1;s:12:"keyPathArray";a:1:{i:0;s:1:"1";}s:7:"chapter";i:2;s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:13:"folderContent";a:5:{i:0;O:8:"stdClass":20:{s:12:"originalName";s:9:"00-new.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"00";s:4:"name";s:3:"new";s:4:"slug";s:3:"new";s:4:"path";s:27:"/01-cyanine-theme/00-new.md";s:15:"pathWithoutType";s:24:"/01-cyanine-theme/00-new";s:3:"key";i:0;s:7:"keyPath";s:3:"1.0";s:12:"keyPathArray";a:2:{i:0;s:1:"1";i:1;s:1:"0";}s:7:"chapter";s:3:"2.1";s:9:"urlRelWoF";s:18:"/cyanine-theme/new";s:6:"urlRel";s:27:"/typemill/cyanine-theme/new";s:6:"urlAbs";s:43:"http://localhost/typemill/cyanine-theme/new";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:1;O:8:"stdClass":20:{s:12:"originalName";s:17:"01-landingpage.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"01";s:4:"name";s:11:"landingpage";s:4:"slug";s:11:"landingpage";s:4:"path";s:35:"/01-cyanine-theme/01-landingpage.md";s:15:"pathWithoutType";s:32:"/01-cyanine-theme/01-landingpage";s:3:"key";i:1;s:7:"keyPath";s:3:"1.1";s:12:"keyPathArray";a:2:{i:0;s:1:"1";i:1;s:1:"1";}s:7:"chapter";s:3:"2.2";s:9:"urlRelWoF";s:26:"/cyanine-theme/landingpage";s:6:"urlRel";s:35:"/typemill/cyanine-theme/landingpage";s:6:"urlAbs";s:51:"http://localhost/typemill/cyanine-theme/landingpage";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:1;}i:2;O:8:"stdClass":20:{s:12:"originalName";s:22:"02-content-elements.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"02";s:4:"name";s:16:"content elements";s:4:"slug";s:16:"content-elements";s:4:"path";s:40:"/01-cyanine-theme/02-content-elements.md";s:15:"pathWithoutType";s:37:"/01-cyanine-theme/02-content-elements";s:3:"key";i:2;s:7:"keyPath";s:3:"1.2";s:12:"keyPathArray";a:2:{i:0;s:1:"1";i:1;s:1:"2";}s:7:"chapter";s:3:"2.3";s:9:"urlRelWoF";s:31:"/cyanine-theme/content-elements";s:6:"urlRel";s:40:"/typemill/cyanine-theme/content-elements";s:6:"urlAbs";s:56:"http://localhost/typemill/cyanine-theme/content-elements";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:3;O:8:"stdClass":20:{s:12:"originalName";s:22:"03-colors-and-fonts.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"03";s:4:"name";s:16:"colors and fonts";s:4:"slug";s:16:"colors-and-fonts";s:4:"path";s:40:"/01-cyanine-theme/03-colors-and-fonts.md";s:15:"pathWithoutType";s:37:"/01-cyanine-theme/03-colors-and-fonts";s:3:"key";i:3;s:7:"keyPath";s:3:"1.3";s:12:"keyPathArray";a:2:{i:0;s:1:"1";i:1;s:1:"3";}s:7:"chapter";s:3:"2.4";s:9:"urlRelWoF";s:31:"/cyanine-theme/colors-and-fonts";s:6:"urlRel";s:40:"/typemill/cyanine-theme/colors-and-fonts";s:6:"urlAbs";s:56:"http://localhost/typemill/cyanine-theme/colors-and-fonts";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:4;O:8:"stdClass":20:{s:12:"originalName";s:12:"04-footer.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"04";s:4:"name";s:6:"footer";s:4:"slug";s:6:"footer";s:4:"path";s:30:"/01-cyanine-theme/04-footer.md";s:15:"pathWithoutType";s:27:"/01-cyanine-theme/04-footer";s:3:"key";i:4;s:7:"keyPath";s:3:"1.4";s:12:"keyPathArray";a:2:{i:0;s:1:"1";i:1;s:1:"4";}s:7:"chapter";s:3:"2.5";s:9:"urlRelWoF";s:21:"/cyanine-theme/footer";s:6:"urlRel";s:30:"/typemill/cyanine-theme/footer";s:6:"urlAbs";s:46:"http://localhost/typemill/cyanine-theme/footer";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}}s:7:"noindex";b:0;}} \ No newline at end of file diff --git a/data/navigation/navi-extended.txt b/data/navigation/navi-extended.txt index d951ffa..85722fa 100644 --- a/data/navigation/navi-extended.txt +++ b/data/navigation/navi-extended.txt @@ -38,7 +38,7 @@ navtitle: 'To Dos' hide: false noindex: false - path: /00-welcome/05-todos.md + path: /00-welcome/05-todos.txtmd keyPath: '0.5' /cyanine-theme: navtitle: 'cyanine theme' @@ -50,7 +50,7 @@ navtitle: new hide: false noindex: false - path: /01-cyanine-theme/00-new.txt + path: /01-cyanine-theme/00-new.md keyPath: '1.0' /cyanine-theme/landingpage: navtitle: landingpage diff --git a/data/security/securitylog.txt b/data/security/securitylog.txt new file mode 100644 index 0000000..4687ad8 --- /dev/null +++ b/data/security/securitylog.txt @@ -0,0 +1,4 @@ +127.0.0.1;2023-07-26 15:15:18;wrong input for password recovery +127.0.0.1;2023-07-26 15:24:27;wrong input for password recovery +127.0.0.1;2023-07-26 15:25:30;wrong input for password recovery +127.0.0.1;2023-07-29 22:34:30;wrong login diff --git a/system/typemill/Controllers/Controller.php b/system/typemill/Controllers/Controller.php index 3d85cbd..b177230 100644 --- a/system/typemill/Controllers/Controller.php +++ b/system/typemill/Controllers/Controller.php @@ -101,7 +101,7 @@ abstract class Controller } protected function addDatasets(array $formDefinitions) - { + { foreach($formDefinitions as $fieldname => $field) { if(isset($field['type']) && $field['type'] == 'fieldset') @@ -109,15 +109,25 @@ abstract class Controller $formDefinitions[$fieldname]['fields'] = $this->addDatasets($field['fields']); } - if(isset($field['type']) && ($field['type'] == 'select' ) && isset($field['dataset']) && ($field['dataset'] == 'userroles' ) ) + if(isset($field['type']) && ($field['type'] == 'select' ) ) { - $userroles = [null => null]; - foreach($this->c->get('acl')->getRoles() as $userrole) + # always add null as first option in selectboxes. + $options = [null => null]; + + if(is_array($field['options'])) { - $userroles[$userrole] = $userrole; + $options = array_merge($options, $field['options']); } - $formDefinitions[$fieldname]['options'] = $userroles; + if(isset($field['dataset']) && ($field['dataset'] == 'userroles' )) + { + foreach($this->c->get('acl')->getRoles() as $userrole) + { + $options[$userrole] = $userrole; + } + } + + $formDefinitions[$fieldname]['options'] = $options; } } @@ -178,6 +188,12 @@ abstract class Controller { $fieldvalue = $input[$fieldname]; + # fix false or null values for selectboxes + if($fielddefinitions['type'] == "select" && ($fieldvalue === 'NULL' OR $fieldvalue === false)) + { + $fieldvalue = NULL; + } + $validationresult = $validator->field($fieldname, $fieldvalue, $fielddefinitions); if($validationresult === true) diff --git a/system/typemill/Controllers/ControllerApiSystemExtensions.php b/system/typemill/Controllers/ControllerApiSystemExtensions.php index b46de04..1b557fc 100644 --- a/system/typemill/Controllers/ControllerApiSystemExtensions.php +++ b/system/typemill/Controllers/ControllerApiSystemExtensions.php @@ -7,7 +7,7 @@ use Psr\Http\Message\ResponseInterface as Response; use Typemill\Models\Validation; use Typemill\Models\License; use Typemill\Models\Extension; -use Typemill\Static\Settings; +use Typemill\Models\Settings; class ControllerApiSystemExtensions extends Controller { @@ -79,7 +79,8 @@ class ControllerApiSystemExtensions extends Controller } # store updated settings here - $updatedSettings = Settings::updateSettings($objectdata); + $settings = new Settings(); + $updatedSettings = $settings->updateSettings($objectdata); $response->getBody()->write(json_encode([ 'message' => 'settings have been saved' diff --git a/system/typemill/Controllers/ControllerApiSystemPlugins.php b/system/typemill/Controllers/ControllerApiSystemPlugins.php index b35377b..a9bd73d 100644 --- a/system/typemill/Controllers/ControllerApiSystemPlugins.php +++ b/system/typemill/Controllers/ControllerApiSystemPlugins.php @@ -6,7 +6,7 @@ use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ResponseInterface as Response; use Typemill\Models\Validation; use Typemill\Models\Extension; -use Typemill\Static\Settings; +use Typemill\Models\Settings; class ControllerApiSystemPlugins extends Controller { @@ -43,7 +43,8 @@ class ControllerApiSystemPlugins extends Controller $plugindata['plugins'][$pluginname] = $validatedOutput; # store updated settings here - $updatedSettings = Settings::updateSettings($plugindata); + $settings = new Settings(); + $updatedSettings = $settings->updateSettings($plugindata); $response->getBody()->write(json_encode([ 'message' => 'settings have been saved' diff --git a/system/typemill/Controllers/ControllerApiSystemSettings.php b/system/typemill/Controllers/ControllerApiSystemSettings.php index 70c6d1a..f1ef5c8 100644 --- a/system/typemill/Controllers/ControllerApiSystemSettings.php +++ b/system/typemill/Controllers/ControllerApiSystemSettings.php @@ -7,7 +7,7 @@ use Psr\Http\Message\ResponseInterface as Response; use Typemill\Models\Validation; use Typemill\Models\Extension; use Typemill\Models\User; -use Typemill\Static\Settings; +use Typemill\Models\Settings; # how to translate results in API call ??? @@ -28,8 +28,9 @@ class ControllerApiSystemSettings extends Controller { $params = $request->getParsedBody(); $settingsinput = $params['settings']; + $settingsModel = new Settings(); - $formdefinitions = Settings::getSettingsDefinitions(); + $formdefinitions = $settingsModel->getSettingsDefinitions(); # validate input $validator = new Validation(); @@ -46,7 +47,7 @@ class ControllerApiSystemSettings extends Controller } # store updated settings here - $updatedSettings = Settings::updateSettings($validatedOutput); + $updatedSettings = $settingsModel->updateSettings($validatedOutput); $response->getBody()->write(json_encode([ 'message' => 'settings have been saved', diff --git a/system/typemill/Controllers/ControllerApiSystemThemes.php b/system/typemill/Controllers/ControllerApiSystemThemes.php index ba17545..646fefa 100644 --- a/system/typemill/Controllers/ControllerApiSystemThemes.php +++ b/system/typemill/Controllers/ControllerApiSystemThemes.php @@ -6,7 +6,7 @@ use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ResponseInterface as Response; use Typemill\Models\Validation; use Typemill\Models\Extension; -use Typemill\Static\Settings; +use Typemill\Models\Settings; class ControllerApiSystemThemes extends Controller { @@ -36,7 +36,8 @@ class ControllerApiSystemThemes extends Controller $themedata['themes'][$themename] = $validatedOutput; # store updated settings here - $updatedSettings = Settings::updateSettings($themedata); + $settings = new Settings(); + $updatedSettings = $settings->updateSettings($themedata); $response->getBody()->write(json_encode([ 'message' => 'settings have been saved', diff --git a/system/typemill/Controllers/ControllerWebAuth.php b/system/typemill/Controllers/ControllerWebAuth.php index 481c38f..68f076c 100644 --- a/system/typemill/Controllers/ControllerWebAuth.php +++ b/system/typemill/Controllers/ControllerWebAuth.php @@ -22,6 +22,7 @@ class ControllerWebAuth extends Controller if( ( null !== $request->getattribute('csrf_result') ) OR ( $request->getattribute('csrf_result') === false ) ) { $this->c->flash->addMessage('error', 'The form has a timeout, please try again.'); + return $response->withHeader('Location', $this->routeParser->urlFor('auth.show')); } @@ -53,9 +54,8 @@ class ControllerWebAuth extends Controller $user->login(); -return $response->withHeader('Location', $this->routeParser->urlFor('settings.show'))->withStatus(302); +# return $response->withHeader('Location', $this->routeParser->urlFor('settings.show'))->withStatus(302); -/* # if user is allowed to view content-area $acl = $this->c->get('acl'); if($acl->hasRole($userdata['userrole']) && $acl->isAllowed($userdata['userrole'], 'content', 'view')) @@ -66,7 +66,6 @@ return $response->withHeader('Location', $this->routeParser->urlFor('settings.sh } return $response->withHeader('Location', $this->routeParser->urlFor('user.account'))->withStatus(302); -*/ } } diff --git a/system/typemill/Controllers/ControllerWebFrontend.php b/system/typemill/Controllers/ControllerWebFrontend.php index e372e81..06613d2 100644 --- a/system/typemill/Controllers/ControllerWebFrontend.php +++ b/system/typemill/Controllers/ControllerWebFrontend.php @@ -19,12 +19,6 @@ use Typemill\Events\OnHtmlLoaded; use Typemill\Events\OnRestrictionsLoaded; -/* -use Typemill\Models\Folder; -use Typemill\Models\WriteMeta; -use Typemill\Extensions\ParsedownExtension; -*/ - class ControllerWebFrontend extends Controller { public function index(Request $request, Response $response, $args) @@ -131,7 +125,7 @@ class ControllerWebFrontend extends Controller if($restrictions['defaultContent']) { # cut the restricted content - $shortenedPage = $this->cutRestrictedContent($markdownBlocks); + $shortenedPage = $this->cutRestrictedContent($markdownArray); # check if there is customized content $restrictionnotice = $this->prepareRestrictionNotice(); diff --git a/system/typemill/Controllers/ControllerWebRecover.php b/system/typemill/Controllers/ControllerWebRecover.php new file mode 100644 index 0000000..d3732b2 --- /dev/null +++ b/system/typemill/Controllers/ControllerWebRecover.php @@ -0,0 +1,338 @@ +c->get('view')->render($response, '/auth/recover.twig', [ + + ]); + } + + public function recoverPassword(Request $request, Response $response) + { + $params = $request->getParsedBody(); + $settings = $this->c->get('settings'); + $urlinfo = $this->c->get('urlinfo'); + + if(!isset($params['email']) OR filter_var($params['email'], \FILTER_VALIDATE_EMAIL) === false ) + { + $this->c->get('flash')->addMessage('error', 'Please enter a valid email.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.recoverform'))->withStatus(302); + } + + $title = 'Please check your inbox'; + $message = 'Dear user, please check the inbox of your email account for more instructions.'; + + $user = new User(); + $requiredUser = $user->findUsersByEmail($params['email']); + + if($requiredUser) + { + $user->setUserWithPassword($requiredUser[0]); + + $requiredUser = $user->getUserData(); + $recoverdate = date("Y-m-d H:i:s"); + $recovertoken = bin2hex(random_bytes(32)); + + $url = $urlinfo['baseurl'] . '/tm/reset?username=' . $requiredUser['username'] . '&recovertoken=' . $recovertoken; + $link = '' . $url . ''; + + # define the headers + $headers = 'Content-Type: text/html; charset=utf-8' . "\r\n"; + $headers .= 'Content-Transfer-Encoding: base64' . "\r\n"; + if(isset($settings['recoverfrom']) && $settings['recoverfrom'] != '') + { + $headers .= 'From: ' . $settings['recoverfrom']; + } + + $subjectline = (isset($settings['recoversubject']) && ($settings['recoversubject'] != '') ) ? $settings['recoversubject'] : 'Recover your password'; + $subject = '=?UTF-8?B?' . base64_encode($subjectline) . '?='; + + $messagetext = "Dear user,

please use the following link to set a new password:"; + if(isset($settings['recovermessage']) && ($settings['recovermessage'] != '')) + { + $parsedown = new ParsedownExtension($urlinfo['baseurl']); + $parsedown->setSafeMode(true); + + $contentArray = $parsedown->text($settings['recovermessage']); + $messagetext = $parsedown->markup($contentArray); + } + + $message = base64_encode($messagetext . "

" . $link); + + # $send = mail($requiredUser['email'], $subject, $message, $headers); + + $send = false; + + if($send == 'delete') + { + $title = 'Error sending email'; + $message = 'Dear ' . $requiredUser['username'] . ', we could not send the email with the password instructions to your address. Please contact the website owner and ask for help.'; + } + else + { + # update user + $user->setValue('recoverdate', $recoverdate); + $user->setValue('recovertoken', $recovertoken); + $user->updateUser(); + + $title = 'Please check your inbox'; + $message = 'Dear ' . $requiredUser['username'] . ', please check the inbox of your email account for more instructions. Do not forget to check your spam-folder if your inbox is empty.'; + } + } + elseif(isset($settings['securitylog']) && $settings['securitylog']) + { + \Typemill\Static\Helpers::addLogEntry('wrong input for password recovery'); + } + + return $this->c->get('view')->render($response, '/auth/recoverconf.twig', [ + 'title' => $title, + 'message' => $message + ]); + } + + public function showPasswordResetForm(Request $request, Response $response, $args) + { + $params = $request->getQueryParams(); + $securitylog = ( isset($settings['securitylog']) && $settings['securitylog'] ) ? true : false; + + if(!isset($params['username']) OR !isset($params['recovertoken'])) + { + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('wrong password reset link'); + } + + $this->c->get('flash')->addMessage('error', 'You tried to open the password reset page but the link was invalid.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + + $user = new User(); + $requiredUser = $user->setUserWithPassword($params['username']); + + if(!$requiredUser) + { + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('password reset link user not found'); + } + + $this->c->get('flash')->addMessage('error', 'You tried to open the password reset page but the link was invalid.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + + $requiredUser = $user->getUserData(); + + if(!isset($requiredUser['recovertoken']) OR $requiredUser['recovertoken'] != $params['recovertoken'] ) + { + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('password reset link wrong token'); + } + + $this->c->get('flash')->addMessage('error', 'You tried to open the password reset page but the link was invalid.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + + $recoverdate = isset($requiredUser['recoverdate']) ? $requiredUser['recoverdate'] : false; + + if(!$recoverdate) + { + $user->unsetValue('recovertoken'); + $user->updateUser(); + + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('password reset link outdated'); + } + + $this->c->get('flash')->addMessage('error', 'The link to recover the password was too old. Please create a new one.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + + $now = new \DateTime('NOW'); + $recoverdate = new \DateTime($recoverdate); + + if(!$recoverdate) + { + $user->unsetValue('recovertoken'); + $user->unsetValue('recoverdate'); + $user->updateUser(); + + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('password reset link wrong date format'); + } + + $this->c->get('flash')->addMessage('error', 'The link to recover the password was too old. Please create a new one.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + +# here we should make the interval editable + $validDate = $recoverdate->add(new \DateInterval('P1D')); + + if($validDate <= $now) + { + $user->unsetValue('recovertoken'); + $user->unsetValue('recoverdate'); + $user->updateUser(); + + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('password reset link outdated'); + } + + $this->c->get('flash')->addMessage('error', 'The link to recover the password was too old. Please create a new one.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + + return $this->c->get('view')->render($response, '/auth/reset.twig', [ + 'recovertoken' => $params['recovertoken'], + 'username' => $requiredUser['username'] + ]); + } + + public function resetPassword(Request $request, Response $response, $args) + { + $params = $request->getParsedBody(); + $settings = $this->c->get('settings'); + $urlinfo = $this->c->get('urlinfo'); + + if(!isset($params['username']) OR !isset($params['recovertoken'])) + { + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('create reset password username or token missing'); + } + + $this->c->get('flash')->addMessage('error', 'You tried to set a new password but username or token was invalid.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + + $validation = new Validation(); + + if(!$validation->recoverPassword($params)) + { + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('create reset password wrong input'); + } + + $this->c->get('flash')->addMessage('error', 'Please correct your input.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.resetform', [], ['username' => $params['username'], 'recovertoken' => $params['recovertoken']]))->withStatus(302); + } + + $user = new User(); + $requiredUser = $user->setUserWithPassword($params['username']); + if(!$requiredUser) + { + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('create reset password user not found'); + } + + $this->c->get('flash')->addMessage('error', 'You tried to open the password reset page but the link was invalid.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + + $requiredUser = $user->getUserData(); + + if(!isset($requiredUser['recovertoken']) OR $requiredUser['recovertoken'] != $params['recovertoken'] ) + { + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('create reset password wrong token'); + } + + $this->c->get('flash')->addMessage('error', 'You tried to open the password reset page but the link was invalid.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + + $recoverdate = isset($requiredUser['recoverdate']) ? $requiredUser['recoverdate'] : false; + + if(!$recoverdate) + { + $user->unsetValue('recovertoken'); + $user->updateUser(); + + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('create reset password date outdated'); + } + + $this->c->get('flash')->addMessage('error', 'The link to recover the password was too old. Please create a new one.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + + $now = new \DateTime('NOW'); + $recoverdate = new \DateTime($recoverdate); + + if(!$recoverdate) + { + $user->unsetValue('recovertoken'); + $user->unsetValue('recoverdate'); + $user->updateUser(); + + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('create reset password wrong date format'); + } + + $this->c->get('flash')->addMessage('error', 'The link to recover the password was too old. Please create a new one.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + +# here we should make the interval editable + $validDate = $recoverdate->add(new \DateInterval('P1D')); + + if($validDate <= $now) + { + $user->unsetValue('recovertoken'); + $user->unsetValue('recoverdate'); + $user->updateUser(); + + if($securitylog) + { + \Typemill\Static\Helpers::addLogEntry('create reset password outdated'); + } + + $this->c->get('flash')->addMessage('error', 'The link to recover the password was too old. Please create a new one.'); + + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } + + $user->unsetValue('recovertoken'); + $user->unsetValue('recoverdate'); + $password = $user->generatePassword($params['password']); + $user->setValue('password', $password); + $user->updateUser(); + + unset($_SESSION['old']); + + $this->c->get('flash')->addMessage('info', 'Please login with your new password.'); + return $response->withHeader('Location', $this->routeParser->urlFor('auth.login'))->withStatus(302); + } +} \ No newline at end of file diff --git a/system/typemill/Controllers/ControllerWebSetup.php b/system/typemill/Controllers/ControllerWebSetup.php new file mode 100644 index 0000000..cf65c18 --- /dev/null +++ b/system/typemill/Controllers/ControllerWebSetup.php @@ -0,0 +1,99 @@ +checkFolder('settingsFolder')) + { + $systemerrors[] = $storage->getError(); + } + if( !$storage->checkFolder('settingsFolder', 'users')){ $systemerrors[] = $storage->getError(); } + if( !$storage->checkFolder('contentFolder')){ $systemerrors[] = $storage->getError(); } + if( !$storage->checkFolder('dataFolder')){ $systemerrors[] = $storage->getError(); } + if( !$storage->checkFolder('cacheFolder')){ $systemerrors[] = $storage->getError(); } + if( !$storage->checkFolder('tmpFolder')){ $systemerrors[] = $storage->getError(); } + if( !$storage->checkFolder('originalFolder')){ $systemerrors[] = $storage->getError(); } + if( !$storage->checkFolder('liveFolder')){ $systemerrors[] = $storage->getError(); } + if( !$storage->checkFolder('thumbsFolder')){ $systemerrors[] = $storage->getError(); } + if( !$storage->checkFolder('customFolder')){ $systemerrors[] = $storage->getError(); } + if( !$storage->checkFolder('fileFolder')){ $systemerrors[] = $storage->getError(); } + + # check php-version + if (version_compare(phpversion(), '8.3.0', '<')) + { + $systemerrors[] = 'The PHP-version of your server is ' . phpversion() . ' and Typemill needs at least 8.0.0'; + } + + # check if extensions are loaded + if(!extension_loaded('gd')){ $systemerrors[] = 'The php-extension GD for image manipulation is not enabled.'; } + if(!extension_loaded('mbstring')){ $systemerrors[] = 'The php-extension mbstring is not enabled.'; } + if(!extension_loaded('fileinfo')){ $systemerrors[] = 'The php-extension fileinfo is not enabled.'; } + if(!extension_loaded('session')){ $systemerrors[] = 'The php-extension session is not enabled.'; } + if(!extension_loaded('iconv')){ $systemerrors[] = 'The php-extension iconv is not enabled.'; } + + $systemerrors = empty($systemerrors) ? false : $systemerrors; + + return $this->c->get('view')->render($response, 'auth/setup.twig', [ + 'systemerrors' => $systemerrors + ]); + } + + public function create(Request $request, Response $response, $args) + { + $params = $request->getParsedBody(); + $params['userrole'] = 'administrator'; + $validate = new Validation(); + $user = new User(); + + # get userroles for validation + $userroles = $this->c->get('acl')->getRoles(); + + # validate user + if($validate->newUser($params, $userroles)) + { + $userdata = [ + 'username' => $params['username'], + 'email' => $params['email'], + 'userrole' => $params['userrole'], + 'password' => $params['password'] + ]; + + $user = new User(); + + # create initial user + $username = $user->createUser($userdata); + + if($username) + { + $user->setUser($username); + + $user->login(); + + # create initial settings file + $settingsModel = new Settings(); + $settingsModel->createSettings(); + + $urlinfo = $this->c->get('urlinfo'); + $route = $urlinfo['baseurl'] . '/tm/system'; + + return $response->withHeader('Location', $route)->withStatus(302); + } + } + } +} \ No newline at end of file diff --git a/system/typemill/Controllers/ControllerWebSystem.php b/system/typemill/Controllers/ControllerWebSystem.php index 8f371ce..e97de6e 100644 --- a/system/typemill/Controllers/ControllerWebSystem.php +++ b/system/typemill/Controllers/ControllerWebSystem.php @@ -6,7 +6,7 @@ use Typemill\Models\Navigation; use Typemill\Models\Extension; use Typemill\Models\User; use Typemill\Models\License; -use Typemill\Static\Settings; +use Typemill\Models\Settings; class ControllerWebSystem extends Controller { @@ -26,7 +26,9 @@ class ControllerWebSystem extends Controller $urlinfo = $this->c->get('urlinfo') ); - $systemfields = Settings::getSettingsDefinitions(); + $settingsModel = new Settings(); + $systemfields = $settingsModel->getSettingsDefinitions(); + $systemfields = $this->addDatasets($systemfields); # add full url for sitemap to settings $this->settings['sitemap'] = $this->c->get('urlinfo')['baseurl'] . '/cache/sitemap.xml'; diff --git a/system/typemill/Middleware/OldInputMiddleware.php b/system/typemill/Middleware/OldInputMiddleware.php new file mode 100644 index 0000000..fbbc619 --- /dev/null +++ b/system/typemill/Middleware/OldInputMiddleware.php @@ -0,0 +1,36 @@ +view = $view; + } + + public function __invoke(Request $request, RequestHandler $handler) + { + if(isset($_SESSION)) + { + if(isset($_SESSION['old'])) + { + $this->view->getEnvironment()->addGlobal('old', $_SESSION['old']); + } + if(!empty($request->getParsedBody())) + { + $_SESSION['old'] = $request->getParsedBody(); + } + } + + $response = $handler->handle($request); + + return $response; + } +} \ No newline at end of file diff --git a/system/typemill/Middleware/SessionMiddleware.php b/system/typemill/Middleware/SessionMiddleware.php index 4e2614a..32e45f1 100644 --- a/system/typemill/Middleware/SessionMiddleware.php +++ b/system/typemill/Middleware/SessionMiddleware.php @@ -24,7 +24,7 @@ class SessionMiddleware implements MiddlewareInterface public function process(Request $request, RequestHandler $handler) :response { - # start session for routes + # start session Session::startSessionForSegments($this->segments, $this->route); $authenticated = ( diff --git a/system/typemill/Middleware/ValidationErrorsMiddleware.php b/system/typemill/Middleware/ValidationErrorsMiddleware.php new file mode 100644 index 0000000..74c8a79 --- /dev/null +++ b/system/typemill/Middleware/ValidationErrorsMiddleware.php @@ -0,0 +1,38 @@ +view = $view; + } + + public function __invoke(Request $request, RequestHandler $handler) + { + if(isset($_SESSION['errors'])) + { + $this->view->getEnvironment()->addGlobal('errors', $_SESSION['errors']); + + unset($_SESSION['errors']); + } + + if(isset($_SESSION['phrase'])) + { + $this->view->getEnvironment()->addGlobal('errors', ['captcha' => 'the captcha is wrong, please try again']); + + unset($_SESSION['phrase']); + } + + $response = $handler->handle($request); + + return $response; + } +} \ No newline at end of file diff --git a/system/typemill/Models/Extension.php b/system/typemill/Models/Extension.php index 6ff17a4..dd90fa5 100644 --- a/system/typemill/Models/Extension.php +++ b/system/typemill/Models/Extension.php @@ -40,7 +40,7 @@ class Extension public function getThemes() { - $themeFolder = $this->storage->getFolderPath('themeFolder'); + $themeFolder = $this->storage->getFolderPath('themesFolder'); $themeFolderC = scandir($themeFolder); $themes = []; foreach ($themeFolderC as $key => $theme) @@ -59,7 +59,7 @@ class Extension public function getThemeDefinition($themeName) { - $themeSettings = $this->storage->getYaml('themeFolder', $themeName, $themeName . '.yaml'); + $themeSettings = $this->storage->getYaml('themesFolder', $themeName, $themeName . '.yaml'); # add standard-textarea for custom css $themeSettings['forms']['fields']['customcss'] = [ @@ -101,7 +101,7 @@ class Extension public function getPlugins() { - $pluginFolder = $this->storage->getFolderPath('pluginFolder'); + $pluginFolder = $this->storage->getFolderPath('pluginsFolder'); $pluginFolderC = scandir($pluginFolder); $plugins = []; foreach ($pluginFolderC as $key => $plugin) @@ -120,7 +120,7 @@ class Extension public function getPluginDefinition($pluginName) { - $pluginSettings = $this->storage->getYaml('pluginFolder', $pluginName, $pluginName . '.yaml'); + $pluginSettings = $this->storage->getYaml('pluginsFolder', $pluginName, $pluginName . '.yaml'); return $pluginSettings; } diff --git a/system/typemill/Models/Meta.php b/system/typemill/Models/Meta.php index cf62be2..4bfeaa8 100644 --- a/system/typemill/Models/Meta.php +++ b/system/typemill/Models/Meta.php @@ -4,6 +4,7 @@ namespace Typemill\Models; use Typemill\Models\StorageWrapper; use Typemill\Models\Content; +use Typemill\Models\Settings; class Meta { @@ -36,7 +37,8 @@ class Meta public function getMetaDefinitions($settings, $folder) { - $metadefinitions = $this->storage->getYaml('systemSettings', '', 'metatabs.yaml'); + $metadefinitions = $this->storage->getYaml('systemSettings', '', 'metatabs.yaml'); + $settingsModel = new Settings(); # loop through all plugins if(!empty($settings['plugins'])) @@ -45,7 +47,7 @@ class Meta { if($plugin['active']) { - $pluginSettings = \Typemill\Static\Settings::getObjectSettings('plugins', $name); + $pluginSettings = $settingsModel->getObjectSettings('pluginsFolder', $name); if($pluginSettings && isset($pluginSettings['metatabs'])) { $metadefinitions = array_merge_recursive($metadefinitions, $pluginSettings['metatabs']); @@ -55,7 +57,7 @@ class Meta } # add the meta from theme settings here - $themeSettings = \Typemill\Static\Settings::getObjectSettings('themes', $settings['theme']); + $themeSettings = $settingsModel->getObjectSettings('themesFolder', $settings['theme']); if($themeSettings && isset($themeSettings['metatabs'])) { diff --git a/system/typemill/Models/Settings.php b/system/typemill/Models/Settings.php new file mode 100644 index 0000000..290a63f --- /dev/null +++ b/system/typemill/Models/Settings.php @@ -0,0 +1,160 @@ +storage = new StorageWrapper('\Typemill\Models\Storage'); + } + + public function loadSettings() + { + $defaultsettings = $this->getDefaultSettings(); + $usersettings = $this->getUserSettings(); + + $settings = $defaultsettings; + $settings['setup'] = true; + + if($usersettings) + { + $settings = array_merge($defaultsettings, $usersettings); + + # make sure all image-size information are there + if(isset($usersettings['images'])) + { + $images = array_merge($defaultsettings['images'], $settings['images']); + $settings['images'] = $images; + } + } +#### + $settings['rootPath'] = getcwd(); +#### + $settings = self::addThemeSettings($settings); + + return $settings; + } + + public function addThemeSettings($settings) + { + # we have to check if the theme has been deleted + $themefolder = $this->storage->getFolderPath('themesFolder'); + + # if there is no theme in settings or theme has been deleted + if(!isset($settings['theme']) OR !file_exists($themefolder . $settings['theme'])) + { + # scan theme folder and get the first theme + $themes = array_filter(scandir($themefolder), function ($item) use($themefolder) + { + return is_dir($themefolder . $item) && strpos($item, '.') !== 0; + }); + + $firsttheme = reset($themes); + + # if there is a theme with an index.twig-file + if($firsttheme && file_exists($themefolder . $firsttheme . DIRECTORY_SEPARATOR . 'index.twig')) + { + $settings['theme'] = $firsttheme; + } + else + { + die('You need at least one theme with an index.twig-file in your theme-folder.'); + } + } + + # We have the theme so create the theme path +# $settings['themePath'] = $settings['rootPath'] . $settings['themeFolder'] . DIRECTORY_SEPARATOR . $settings['theme']; + + # if there are no theme settings yet (e.g. no setup yet) use default theme settings + if(!isset($settings['themes'])) + { + $themeSettings = $this->getObjectSettings('themes', $settings['theme']); + $settings['themes'][$settings['theme']] = isset($themeSettings['settings']) ? $themeSettings['settings'] : false; + } + + return $settings; + } + + public function getDefaultSettings() + { + $defaultSettings = $this->storage->getYaml('systemSettings', '', 'defaults.yaml'); + + if($defaultSettings) + { + $defaultSettings['systemSettingsPath'] = $this->storage->getFolderPath('systemSettings'); + + return $defaultSettings; + } + + return false; + } + + public function getUserSettings() + { + $userSettings = $this->storage->getYaml('settingsFolder', '', 'settings.yaml'); + + if($userSettings) + { + return $userSettings; + } + + return false; + } + + public function getObjectSettings($objectType, $objectName) + { + $objectSettings = $this->storage->getYaml($objectType, $objectName, $objectName . '.yaml'); + + if($objectSettings) + { + return $objectSettings; + } + + return false; + } + + public function updateSettings(array $newSettings) + { + $userSettings = $this->getUserSettings(); + + # only allow if usersettings already exists (setup has been done) + if($userSettings) + { + # merge usersettings with new settings + $settings = array_merge($userSettings, $newSettings); + + if($this->storage->updateYaml('settingsFolder', '', 'settings.yaml', $settings)) + { + return true; + } + } + + return false; + } + + public function getSettingsDefinitions() + { + return $this->storage->getYaml('systemSettings', '', 'system.yaml'); + } + + public function createSettings() + { + $language = Translations::whichLanguage(); + + $initialSettings = $this->storage->updateYaml('settingsFolder', '', 'settings.yaml', [ + 'language' => $language + ]); + + if($initialSettings) + { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/system/typemill/Models/Storage.php b/system/typemill/Models/Storage.php index 1cbb588..1eae04a 100644 --- a/system/typemill/Models/Storage.php +++ b/system/typemill/Models/Storage.php @@ -8,7 +8,7 @@ class Storage { public $error = false; - protected $basepath = false; + private $basepath = false; protected $tmpFolder = false; @@ -30,9 +30,9 @@ class Storage protected $settingsFolder = false; - protected $themeFolder = false; + protected $themesFolder = false; - protected $pluginFolder = false; + protected $pluginsFolder = false; protected $translationFolder = false; @@ -62,9 +62,9 @@ class Storage $this->settingsFolder = $this->basepath . 'settings'; - $this->pluginFolder = $this->basepath . 'plugins'; + $this->pluginsFolder = $this->basepath . 'plugins'; - $this->themeFolder = $this->basepath . 'themes'; + $this->themesFolder = $this->basepath . 'themes'; $this->translationFolder = $this->basepath . 'system' . DIRECTORY_SEPARATOR . 'typemill' . DIRECTORY_SEPARATOR . 'author' . DIRECTORY_SEPARATOR . 'translations' . DIRECTORY_SEPARATOR; @@ -78,6 +78,10 @@ class Storage public function getFolderPath($location, $folder = NULL) { + # security: remove ../ from location + # security: make sure user does not write into basepath + # security: write only into certain folders + if(isset($this->$location)) { $path = rtrim($this->$location, DIRECTORY_SEPARATOR); @@ -97,7 +101,7 @@ class Storage return false; } - public function checkFolder($location, $folder) + public function checkFolder($location, $folder = NULL) { $folderpath = $this->getFolderPath($location, $folder); @@ -367,6 +371,12 @@ class Storage $extension = isset($pathinfo['extension']) ? strtolower($pathinfo['extension']) : false; $imagename = isset($pathinfo['filename']) ? $pathinfo['filename'] : false; + if(!$extension OR !$imagename) + { + $this->error = "Extension or name for image is missing."; + return false; + } + $imagesInTmp = glob($this->tmpFolder . "*$imagename.*"); if(empty($imagesInTmp) OR !$imagesInTmp) { diff --git a/system/typemill/Models/User.php b/system/typemill/Models/User.php index b8bcdba..a7acf1e 100644 --- a/system/typemill/Models/User.php +++ b/system/typemill/Models/User.php @@ -116,7 +116,7 @@ class User } public function updateUser() - { + { if($this->storage->updateYaml('settingsFolder', 'users', $this->user['username'] . '.yaml', $this->user)) { $this->deleteUserIndex(); diff --git a/system/typemill/Models/Validation.php b/system/typemill/Models/Validation.php index 8ddd6b3..6369173 100644 --- a/system/typemill/Models/Validation.php +++ b/system/typemill/Models/Validation.php @@ -559,11 +559,11 @@ class Validation * @return obj $v the validation object passed to a result method. */ - public function recoverPasswordBREAK(array $params) + public function recoverPassword(array $params) { $v = new Validator($params); $v->rule('required', ['password', 'passwordrepeat']); - $v->rule('lengthBetween', 'password', 5, 20); + $v->rule('lengthBetween', 'password', 5, 50); $v->rule('equals', 'passwordrepeat', 'password'); return $this->validationResult($v); diff --git a/system/typemill/Static/Helpers.php b/system/typemill/Static/Helpers.php index b95cd6f..f3c443d 100644 --- a/system/typemill/Static/Helpers.php +++ b/system/typemill/Static/Helpers.php @@ -59,7 +59,7 @@ class Helpers{ $line .= ';' . $action; $storage = new StorageWrapper('\Typemill\Models\Storage'); - $logfile = $storage->getFile('basepath', 'cache', 'securitylog.txt'); + $logfile = $storage->getFile('dataFolder', 'security', 'securitylog.txt'); if($logfile) { @@ -70,7 +70,14 @@ class Helpers{ $logfile = $line . PHP_EOL; } - $storage->writeFile('basepath', 'cache', 'securitylog.txt', $logfile); + $result = $storage->writeFile('dataFolder', 'security', 'securitylog.txt', $logfile); + + if($result) + { + return true; + } + + return $storage->getError(); } public static function array_sort($array, $on, $order=SORT_ASC) diff --git a/system/typemill/Static/Settings.php b/system/typemill/Static/Settings.php index 09eaf3e..f767d19 100644 --- a/system/typemill/Static/Settings.php +++ b/system/typemill/Static/Settings.php @@ -3,15 +3,19 @@ namespace Typemill\Static; use Typemill\Models\StorageWrapper; +use Typemill\Static\Translations; class Settings { public static function loadSettings() { + echo debug_backtrace()[1]['function']; + die('use model load settings instead'); $defaultsettings = self::getDefaultSettings(); $usersettings = self::getUserSettings(); $settings = $defaultsettings; + $settings['setup'] = true; if($usersettings) { @@ -33,6 +37,9 @@ class Settings public static function addThemeSettings($settings) { + echo debug_backtrace()[1]['function']; + die('use model addThemeSettings instead'); + # we have to check if the theme has been deleted $rootpath = getcwd(); $themefolder = $rootpath . DIRECTORY_SEPARATOR . $settings['themeFolder'] . DIRECTORY_SEPARATOR; @@ -74,6 +81,9 @@ class Settings public static function getDefaultSettings() { + echo debug_backtrace()[1]['function']; + die('use model getDefaultSettings instead'); + $rootpath = getcwd(); $defaultsettingspath = $rootpath . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'typemill' . DIRECTORY_SEPARATOR . 'settings' . DIRECTORY_SEPARATOR; $defaultsettingsfile = $defaultsettingspath . 'defaults.yaml'; @@ -92,6 +102,9 @@ class Settings public static function getUserSettings() { + echo debug_backtrace()[1]['function']; + die('use model getUserSettings instead'); + $rootpath = getcwd(); $usersettingsfile = $rootpath . DIRECTORY_SEPARATOR . 'settings' . DIRECTORY_SEPARATOR . 'settings.yaml'; @@ -106,10 +119,22 @@ class Settings return false; } - public static function getObjectSettings($objectType, $objectName) + public static function getObjectSettings($objectType, $objectName, $storagepath = '\Typemill\Models\Storage') { -# $yaml = new Models\WriteYaml(); - + echo debug_backtrace()[1]['function']; + die('use model getObjectSettings instead'); + + $storage = new StorageWrapper($storagepath); + + $objectSettings = $storage->getYaml($objectType, $objectName, $objectName . '.yaml'); + + if($objectSettings) + { + return $objectSettings; + } + return false; + +/* $rootpath = getcwd(); $objectfile = $rootpath . DIRECTORY_SEPARATOR . $objectType . DIRECTORY_SEPARATOR . $objectName . DIRECTORY_SEPARATOR . $objectName . '.yaml'; @@ -122,116 +147,52 @@ class Settings } return false; + */ } - public static function updateSettings(array $newSettings) + public static function updateSettings(array $newSettings, $storagepath = '\Typemill\Models\Storage') { + echo debug_backtrace()[1]['function']; + die('use model updateSettings instead'); + + $storage = new StorageWrapper($storagepath); + # only allow if usersettings already exists (setup has been done) $userSettings = self::getUserSettings(); # merge usersettings with new settings $settings = array_merge($userSettings, $newSettings); - - $storage = new StorageWrapper('\Typemill\Models\Storage'); - - $storage->updateYaml('basepath', 'settings', 'settings.yaml', $settings); + + $storage->updateYaml('settingsFolder', '', 'settings.yaml', $settings); } - public static function getSettingsDefinitions() + public static function getSettingsDefinitions($storagepath = '\Typemill\Models\Storage') { - $storage = new StorageWrapper('\Typemill\Models\Storage'); + echo debug_backtrace()[1]['function']; + die('use model getSettingsDefinitions instead'); + + $storage = new StorageWrapper($storagepath); return $storage->getYaml('systemSettings', '', 'system.yaml'); } - - - - - - - -### refactor - - public static function createSettings() + public static function createSettings($storagepath = '\Typemill\Models\Storage') { - $yaml = new Models\WriteYaml(); + echo debug_backtrace()[1]['function']; + die('use model createSettings instead'); - $language = self::whichLanguage(); + $storage = new StorageWrapper($storagepath); + + $language = Translations::whichLanguage(); - # create initial settings file with only setup false - if($yaml->updateYaml('settings', 'settings.yaml', array('setup' => false, 'language' => $language))) + $initialSettings = $storage->updateYaml('settingsFolder', '', 'settings.yaml', [ + 'language' => $language + ]); + + if($initialSettings) { return true; } return false; - } - - public static function oldupdateSettings($settings) - { - # only allow if usersettings already exists (setup has been done) - $userSettings = self::getUserSettings(); - - if($userSettings) - { - # whitelist settings that can be stored in usersettings (values are not relevant here, only keys) - $allowedUserSettings = ['displayErrorDetails' => true, - 'title' => true, - 'copyright' => true, - 'language' => true, - 'langattr' => true, - 'startpage' => true, - 'author' => true, - 'year' => true, - 'access' => true, - 'pageaccess' => true, - 'hrdelimiter' => true, - 'restrictionnotice' => true, - 'wraprestrictionnotice' => true, - 'headlineanchors' => true, - 'theme' => true, - 'editor' => true, - 'formats' => true, - 'setup' => true, - 'welcome' => true, - 'images' => true, - 'live' => true, - 'width' => true, - 'height' => true, - 'plugins' => true, - 'themes' => true, - 'latestVersion' => true, - 'logo' => true, - 'favicon' => true, - 'twigcache' => true, - 'proxy' => true, - 'trustedproxies' => true, - 'headersoff' => true, - 'urlschemes' => true, - 'svg' => true, - 'recoverpw' => true, - 'recoversubject' => true, - 'recovermessage' => true, - 'recoverfrom' => true, - 'securitylog' => true, - 'oldslug' => true, - 'refreshcache' => true, - 'pingsitemap' => true, - ]; - - # cleanup the existing usersettings - $userSettings = array_intersect_key($userSettings, $allowedUserSettings); - - # cleanup the new settings passed as an argument - $settings = array_intersect_key($settings, $allowedUserSettings); - - # merge usersettings with new settings - $settings = array_merge($userSettings, $settings); - - # write settings to yaml - $yaml = new Models\WriteYaml(); - $yaml->updateYaml('settings', 'settings.yaml', $settings); - } - } - + } } \ No newline at end of file diff --git a/system/typemill/author/auth/login.twig b/system/typemill/author/auth/login.twig index 0a7de05..158b572 100644 --- a/system/typemill/author/auth/login.twig +++ b/system/typemill/author/auth/login.twig @@ -9,7 +9,7 @@
-

Login

+

Login

@@ -49,7 +49,7 @@ diff --git a/system/typemill/author/auth/recover.twig b/system/typemill/author/auth/recover.twig new file mode 100644 index 0000000..9d621d7 --- /dev/null +++ b/system/typemill/author/auth/recover.twig @@ -0,0 +1,54 @@ +{% extends 'layouts/layoutAuth.twig' %} + +{% block title %}Login{% endblock %} + +{% block content %} + +
+ +
+
+ + + +
+ +
+ + + {% if errors.email %} + {{ errors.email|first }} + {% endif %} +
+ + + + + +
+ + +
+
+ +
+
+

Forgot your password?

+

Enter the email of your user-account, click the recover-button and check your mailbox for further instructions.

+
+
+ +
+{% endblock %} \ No newline at end of file diff --git a/system/typemill/author/auth/recoverconf.twig b/system/typemill/author/auth/recoverconf.twig new file mode 100644 index 0000000..ddf884f --- /dev/null +++ b/system/typemill/author/auth/recoverconf.twig @@ -0,0 +1,32 @@ +{% extends 'layouts/layoutAuth.twig' %} + +{% block title %}Login{% endblock %} + +{% block content %} + +
+ +
+
+ +
+

Done!

+
+ +
+
+ +
+ +
+

{{ title }}

+

{{ message }}

+

{{ 'to the login page'|translate }}

+
+ +
+ +
+{% endblock %} \ No newline at end of file diff --git a/system/typemill/author/auth/reset.twig b/system/typemill/author/auth/reset.twig new file mode 100644 index 0000000..6baca8a --- /dev/null +++ b/system/typemill/author/auth/reset.twig @@ -0,0 +1,76 @@ +{% extends 'layouts/layoutAuth.twig' %} + +{% block title %}Login{% endblock %} + +{% block content %} + +
+ +
+
+ +
+ +
+ +
+ + + {% if errors.password %} + {{ errors.password|first }} + {% endif %} +
+ +
+ + + {% if errors.passwordrepeat %} + {{ errors.passwordrepeat|first }} + {% endif %} +
+ + + + + + + + +
+ +
+
+
+ +
+
+

Forgot your password?

+

No problem, simply create a new one on the left.

+

Want to stay save with your passwords? Here are some tips:

+
    +
  • Let the password manager of your browser remember your passwords.

    +
  • If you don't want to rely on your browser, use a separate password manager like keepass or others.
  • +
  • Last but not least: Do not use the same password for several applications.
  • +
+
+
+ +
+{% endblock %} \ No newline at end of file diff --git a/system/typemill/author/auth/setup.twig b/system/typemill/author/auth/setup.twig new file mode 100644 index 0000000..d99cd4a --- /dev/null +++ b/system/typemill/author/auth/setup.twig @@ -0,0 +1,94 @@ +{% extends 'layouts/layoutAuth.twig' %} + +{% block title %}{{ 'Setup'|translate }}{% endblock %} + +{% block content %} + +
+ +
+
+ +

{{ 'Setup'|translate }}

+ +
+ +
+ +
+ + + {% if errors.signup_username %} + {{ errors.username|first }} + {% endif %} +
+ +
+ + + {% if errors.signup_email %} + {{ errors.email|first }} + {% endif %} +
+ +
+ + + {% if errors.password %} + {{ errors.password|first }} + {% endif %} +
+ + + + + +
+ + + +
+
+
+ +
+
+ {% if systemerrors %} +

Systemcheck

+

The following requirements for the installation are missing:

+
    + {% for systemerror in systemerrors %} +
  • {{ systemerror }}
  • + {% endfor %} +
+ {% else %} +

Welcome to Typemill

+

some bla bla bla here

+ {% endif %} +
+
+ +
+{% endblock %} \ No newline at end of file diff --git a/system/typemill/author/css/output.css b/system/typemill/author/css/output.css index 31c5ac5..dd205e3 100644 --- a/system/typemill/author/css/output.css +++ b/system/typemill/author/css/output.css @@ -1144,6 +1144,10 @@ video { resize: both; } +.list-disc { + list-style-type: disc; +} + .flex-col { flex-direction: column; } @@ -1291,6 +1295,11 @@ video { border-color: rgb(209 213 219 / var(--tw-border-opacity)); } +.border-stone-50 { + --tw-border-opacity: 1; + border-color: rgb(250 250 249 / var(--tw-border-opacity)); +} + .border-stone-200 { --tw-border-opacity: 1; border-color: rgb(231 229 228 / var(--tw-border-opacity)); @@ -1306,11 +1315,6 @@ video { border-color: rgb(214 211 209 / var(--tw-border-opacity)); } -.border-stone-50 { - --tw-border-opacity: 1; - border-color: rgb(250 250 249 / var(--tw-border-opacity)); -} - .border-teal-500 { --tw-border-opacity: 1; border-color: rgb(20 184 166 / var(--tw-border-opacity)); @@ -1444,6 +1448,11 @@ video { background-color: rgb(234 179 8 / var(--tw-bg-opacity)); } +.bg-stone-800 { + --tw-bg-opacity: 1; + background-color: rgb(41 37 36 / var(--tw-bg-opacity)); +} + .bg-opacity-90 { --tw-bg-opacity: 0.9; } @@ -1762,9 +1771,9 @@ video { color: rgb(225 29 72 / var(--tw-text-opacity)); } -.text-stone-400 { +.text-stone-50 { --tw-text-opacity: 1; - color: rgb(168 162 158 / var(--tw-text-opacity)); + color: rgb(250 250 249 / var(--tw-text-opacity)); } .text-stone-700 { @@ -1772,6 +1781,11 @@ video { color: rgb(68 64 60 / var(--tw-text-opacity)); } +.text-stone-400 { + --tw-text-opacity: 1; + color: rgb(168 162 158 / var(--tw-text-opacity)); +} + .text-stone-500 { --tw-text-opacity: 1; color: rgb(120 113 108 / var(--tw-text-opacity)); @@ -1787,11 +1801,6 @@ video { color: rgb(214 211 209 / var(--tw-text-opacity)); } -.text-stone-50 { - --tw-text-opacity: 1; - color: rgb(250 250 249 / var(--tw-text-opacity)); -} - .text-red-500 { --tw-text-opacity: 1; color: rgb(239 68 68 / var(--tw-text-opacity)); @@ -1951,11 +1960,21 @@ video { border-color: rgb(20 184 166 / var(--tw-border-opacity)); } +.hover\:border-teal-800:hover { + --tw-border-opacity: 1; + border-color: rgb(17 94 89 / var(--tw-border-opacity)); +} + .hover\:bg-gray-200:hover { --tw-bg-opacity: 1; background-color: rgb(229 231 235 / var(--tw-bg-opacity)); } +.hover\:bg-stone-50:hover { + --tw-bg-opacity: 1; + background-color: rgb(250 250 249 / var(--tw-bg-opacity)); +} + .hover\:bg-teal-500:hover { --tw-bg-opacity: 1; background-color: rgb(20 184 166 / var(--tw-bg-opacity)); @@ -1996,11 +2015,6 @@ video { background-color: rgb(190 18 60 / var(--tw-bg-opacity)); } -.hover\:bg-stone-50:hover { - --tw-bg-opacity: 1; - background-color: rgb(250 250 249 / var(--tw-bg-opacity)); -} - .hover\:bg-teal-600:hover { --tw-bg-opacity: 1; background-color: rgb(13 148 136 / var(--tw-bg-opacity)); @@ -2036,6 +2050,16 @@ video { color: rgb(68 64 60 / var(--tw-text-opacity)); } +.hover\:text-stone-800:hover { + --tw-text-opacity: 1; + color: rgb(41 37 36 / var(--tw-text-opacity)); +} + +.hover\:text-teal-800:hover { + --tw-text-opacity: 1; + color: rgb(17 94 89 / var(--tw-text-opacity)); +} + .hover\:underline:hover { -webkit-text-decoration-line: underline; text-decoration-line: underline; diff --git a/system/typemill/author/js/vue-user.js b/system/typemill/author/js/vue-user.js index d03e2c9..76148ae 100644 --- a/system/typemill/author/js/vue-user.js +++ b/system/typemill/author/js/vue-user.js @@ -112,7 +112,8 @@ const app = Vue.createApp({ self.showModal = false; self.messageClass = 'bg-teal-500'; self.message = response.data.message; - /* redirect to userlist */ + + window.location.replace(data.urlinfo.baseurl + '/tm/users'); }) .catch(function (error) { diff --git a/system/typemill/author/partials/flash.twig b/system/typemill/author/partials/flash.twig index 48a36f7..654d188 100644 --- a/system/typemill/author/partials/flash.twig +++ b/system/typemill/author/partials/flash.twig @@ -1,6 +1,6 @@ {% if flash.info %} -
+
{{ flash.info | first }}
@@ -8,7 +8,7 @@ {% if flash.error %} -
+
{{ flash.error | first }}
diff --git a/system/typemill/routes/setup.php b/system/typemill/routes/setup.php new file mode 100644 index 0000000..edf4c86 --- /dev/null +++ b/system/typemill/routes/setup.php @@ -0,0 +1,10 @@ +redirect('/tm', $routeParser->urlFor('auth.show'), 302); + +$app->get('/tm/setup', ControllerWebSetup::class . ':show')->setName('setup.show'); +$app->post('/tm/setup', ControllerWebSetup::class . ':create')->setName('setup.create'); +$app->redirect('/[{params:.*}]', $routeParser->urlFor('setup.show'), 302); \ No newline at end of file diff --git a/system/typemill/routes/web.php b/system/typemill/routes/web.php index 34228f2..88f48fd 100644 --- a/system/typemill/routes/web.php +++ b/system/typemill/routes/web.php @@ -4,21 +4,31 @@ use Slim\Routing\RouteCollectorProxy; use Typemill\Middleware\WebRedirectIfAuthenticated; use Typemill\Middleware\WebRedirectIfUnauthenticated; use Typemill\Middleware\WebAuthorization; +use Typemill\Controllers\ControllerWebSetup; use Typemill\Controllers\ControllerWebAuth; +use Typemill\Controllers\ControllerWebRecover; use Typemill\Controllers\ControllerWebSystem; use Typemill\Controllers\ControllerWebAuthor; use Typemill\Controllers\ControllerWebFrontend; use Typemill\Controllers\ControllerWebDownload; -# login/register -$app->group('/tm', function (RouteCollectorProxy $group) { +# LOGIN / REGISTER / RECOVER +$app->group('/tm', function (RouteCollectorProxy $group) use ($settings) { $group->get('/login', ControllerWebAuth::class . ':show')->setName('auth.show'); $group->post('/login', ControllerWebAuth::class . ':login')->setName('auth.login'); + if(isset($settings['recoverpw']) && $settings['recoverpw']) + { + $group->get('/recover', ControllerWebRecover::class . ':showRecoverForm')->setName('auth.recoverform'); + $group->post('/recover', ControllerWebRecover::class . ':recoverPassword')->setName('auth.recover'); + $group->get('/reset', ControllerWebRecover::class . ':showPasswordResetForm')->setName('auth.resetform'); + $group->post('/reset', ControllerWebRecover::class . ':resetPassword')->setName('auth.reset'); + } + })->add(new WebRedirectIfAuthenticated($routeParser, $settings)); -# author and editor area, requires authentication +# AUTHOR AREA (requires authentication) $app->group('/tm', function (RouteCollectorProxy $group) use ($routeParser,$acl) { # Admin Area @@ -41,9 +51,6 @@ $app->group('/tm', function (RouteCollectorProxy $group) use ($routeParser,$acl) $app->redirect('/tm', $routeParser->urlFor('auth.show'), 302); $app->redirect('/tm/', $routeParser->urlFor('auth.show'), 302); -# same with setup redirect - - # downloads $app->get('/media/files[/{params:.*}]', ControllerWebDownload::class . ':download')->setName('download.file'); diff --git a/system/typemill/settings/defaults.yaml b/system/typemill/settings/defaults.yaml index be17ecc..53abdbc 100644 --- a/system/typemill/settings/defaults.yaml +++ b/system/typemill/settings/defaults.yaml @@ -1,17 +1,9 @@ version: '2.0.0' -setup: true -welcome: true title: 'Typemill' author: 'Unknown' -copyright: 'Copyright' +copyright: false language: 'en' langattr: 'en' -themeFolder: 'themes' -pluginFolder: 'plugins' -settingsFolder: 'settings' -contentFolder: 'content' -authorFolder: '/system/typemill/author' -adminSegment: 'tm' editor: 'visual' storage: '\Typemill\Models\Storage' formats: diff --git a/system/typemill/system.php b/system/typemill/system.php index 51ae6bf..777a4a5 100644 --- a/system/typemill/system.php +++ b/system/typemill/system.php @@ -1,22 +1,19 @@ loadSettings(); +# $settings = Settings::loadSettings(); /**************************** * HANDLE DISPLAY ERRORS * @@ -81,10 +81,10 @@ $timer['settings'] = microtime(true); $container = new Container(); AppFactory::setContainer($container); -$app = AppFactory::create(); +$app = AppFactory::create(); $container = $app->getContainer(); -$responseFactory = $app->getResponseFactory(); +$responseFactory = $app->getResponseFactory(); $routeParser = $app->getRouteCollector()->getRouteParser(); # add route parser to container to use named routes in controller @@ -164,7 +164,9 @@ foreach($plugins as $plugin) if(isset($updateSettings)) { # update stored settings file - Settings::updateSettings($settings); + $newPluginSettings = ['plugins' => $settings['plugins']]; + $settingsModel->updateSettings($newPluginSettings); + # Settings::updateSettings($settings); } # add final settings to the container @@ -184,11 +186,11 @@ $timer['plugins'] = microtime(true); ****************************/ # load roles and permissions and dispatch to plugins -$rolesAndPermissions = Permissions::loadRolesAndPermissions($settings['defaultSettingsPath']); +$rolesAndPermissions = Permissions::loadRolesAndPermissions($settings['systemSettingsPath']); $rolesAndPermissions = $dispatcher->dispatch(new OnRolesPermissionsLoaded($rolesAndPermissions), 'onRolesPermissionsLoaded')->getData(); # load resources and dispatch to plugins -$resources = Permissions::loadResources($settings['defaultSettingsPath']); +$resources = Permissions::loadResources($settings['systemSettingsPath']); $resources = $dispatcher->dispatch(new OnResourcesLoaded($resources), 'onResourcesLoaded')->getData(); # create acl-object @@ -212,7 +214,7 @@ if( ( isset($settings['access']) && $settings['access'] ) || ( isset($settings[' } else { - $session_segments = ['setup', 'tm/']; + $session_segments = ['setup', 'tm/', 'api/']; # let plugins add own segments for session, eg. to enable csrf for forms $client_segments = $dispatcher->dispatch(new OnSessionSegmentsLoaded([]), 'onSessionSegmentsLoaded')->getData(); @@ -277,12 +279,13 @@ $container->set('view', function() use ($settings, $csrf, $urlinfo, $translation [ # path to templates with namespaces $settings['rootPath'] . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $settings['theme'], - $settings['rootPath'] . $settings['authorFolder'], + $settings['rootPath'] . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'typemill' . DIRECTORY_SEPARATOR . 'author', ], [ # settings 'cache' => ( isset($settings['twigcache']) && $settings['twigcache'] ) ? $settings['rootPath'] . '/cache/twig' : false, 'debug' => isset($settings['displayErrorDetails']), + 'debug' => true, 'autoescape' => false ] ); @@ -318,6 +321,10 @@ $container->set('view', function() use ($settings, $csrf, $urlinfo, $translation $app->add(new AssetMiddleware($assets, $container->get('view'))); +$app->add(new ValidationErrorsMiddleware($container->get('view'))); + +$app->add(new OldInputMiddleware($container->get('view'))); + $app->add(new FlashMessages($container)); # Add Twig-View Middleware @@ -356,9 +363,15 @@ $timer['middleware'] = microtime(true); /************************ * ADD ROUTES * ************************/ - -require __DIR__ . '/routes/api.php'; -require __DIR__ . '/routes/web.php'; +if(isset($settings['setup']) && $settings['setup'] == true) +{ + require __DIR__ . '/routes/setup.php'; +} +else +{ + require __DIR__ . '/routes/api.php'; + require __DIR__ . '/routes/web.php'; +} $timer['routes'] = microtime(true);