diff --git a/content/00-welcome/00-markdown-test.yaml b/content/00-welcome/00-markdown-test.yaml index 4c7dbc1..f9ec15c 100644 --- a/content/00-welcome/00-markdown-test.yaml +++ b/content/00-welcome/00-markdown-test.yaml @@ -1,15 +1,8 @@ meta: - title: 'Markdown Reference and Test Page' - description: '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. ' - author: 'Sebastian Schürmanns' - manualdate: '2020-02-22' - modified: '2020-01-20' - created: null - time: 04-40-32 - navtitle: '' - hide: false -ebooks: - layout: electric - content: - - - name: 'markdown test' + navtitle: 'markdown test' + owner: Sebastian + 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' diff --git a/content/00-welcome/01-manage-access.yaml b/content/00-welcome/01-manage-access.yaml index 1fe46bd..6101b43 100644 --- a/content/00-welcome/01-manage-access.yaml +++ b/content/00-welcome/01-manage-access.yaml @@ -1,9 +1,8 @@ meta: - title: 'manage access' - description: '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.' - owner: trendschau - author: 'Sebastian Schürmanns' - created: '2021-05-27' - time: 20-59-06 navtitle: 'manage access' - modified: '2021-05-27' + owner: Sebastian + 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' diff --git a/content/00-welcome/02-get-help.md b/content/00-welcome/02-get-help.md index d6e256f..f909e83 100644 --- a/content/00-welcome/02-get-help.md +++ b/content/00-welcome/02-get-help.md @@ -6,6 +6,8 @@ If you found a bug or if you have a question, then please open a new issue on [G Do you need professional help, an individual theme or a special plugin? You can hire us at [Trendschau Digital](https://trendschau.net/typemill-development). +[recording 2023 05 11 at 235818 (GIF, 1.30 MB)](media/files/recording-2023-05-11-at-235818.gif){.tm-download file-gif} + [Contributions](https://github.com/typemill/typemill#contributors--supporters), [donations](https://www.paypal.me/typemill) and [feedback](https://github.com/typemill/typemill/issues) for this open source project are always welcome. ![alt](media/live/youtube-6i2-uv88gke.jpeg){.center loading="lazy"} diff --git a/content/00-welcome/03-setup-your-website.yaml b/content/00-welcome/03-setup-your-website.yaml index da485cd..7d9343d 100644 --- a/content/00-welcome/03-setup-your-website.yaml +++ b/content/00-welcome/03-setup-your-website.yaml @@ -1,15 +1,8 @@ meta: - title: 'Setup Your Website' - description: 'Typemill provides detailed settings, and you have access to nearly all settings in the author panel. Learn the basics in this short video:' - heroimage: '' - heroimagealt: null - owner: trendschau - author: 'Sebastian Schürmanns' - allowedrole: null - alloweduser: null - manualdate: null - modified: '2021-08-01' - created: '2021-05-27' - time: 21-02-24 navtitle: 'setup your website' - hide: false + 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:' + description: ' You will find all configurations and settings under the main navigation point settings with the following sub-navigation:' diff --git a/content/00-welcome/04-write-content.yaml b/content/00-welcome/04-write-content.yaml index e73df21..4538849 100644 --- a/content/00-welcome/04-write-content.yaml +++ b/content/00-welcome/04-write-content.yaml @@ -1,23 +1,8 @@ meta: + navtitle: 'write content' + owner: Sebastian + created: '2023-06-12' + time: 22-09-48 + modified: '2023-05-11' title: 'Write Content' - description: 'Typemill is a simple Flat File Content Management System (CMS). We (the community) work hard to provide the best author experience with easy and intuitive authoring tools. But Typemill is still in early development and it is likely that not everything will work perfectly out of the box.' - heroimage: '' - heroimagealt: 'Hero Alternative' - owner: null - author: 'Sebastian Schürmanns' - manualdate: null - modified: '2021-01-30' - created: null - time: null - navtitle: '' - hide: false - allowedrole: administrator - alloweduser: 'trendschau, testmember' -seo: - heroimage: media/live/bildschirmfoto-zu-2019-08-30-20-46-29.png - heroimagealt: 'My Alt-Text' -adamhall: - myfield: - - - key: b - value: 'Das muss eine schöne Sache sei' + description: 'Typemill provides easy and intuitive authoring tools and we work hard to create a good author experience. With the interactive navigation you can create pages' diff --git a/content/00-welcome/05-edit-test.txt b/content/00-welcome/05-edit-test.txt deleted file mode 100644 index b83d57c..0000000 --- a/content/00-welcome/05-edit-test.txt +++ /dev/null @@ -1 +0,0 @@ -["# Write Content","Typemill provides easy and intuitive authoring tools and we work hard to create a good author experience. With the interactive navigation you can create pages and structure your websites. The visual markdown editor will help you to create content in a wysiwyg mode. The publish bar gives you full control over the status of each page. Watch the following video tutorial to learn all the details.","* You can change the default mode in the system settings. \n* You can also switch each format button on and off in the system settings.","The publish bar of Typemill is pretty intuitiv and sticks at the bottom of the screen so that you have always full control of the status of each page. Simply play around with it and you will quickly understand how it works. In short:","* The green button \"online\" indicates, that your page is published and visible for your readers.\n* You can depublish a page by clicking the green \"online\" button. The button will turn gray with the label \"offline\" and the page is not visible for your readers anymore.\n* With the green button \"Publish\" you can either publish a page that is offline or you can publish some unpublished changes on your page.\n* The publish-button is gray and disabled, if the page is online and if there are no unpublished changes.\n* All buttons will change in real time, so you can always exactly see what is going on.\n* To provide an easy status-overview of the whole website, Typemill marks all pages in the navigation on the left side as published (green), changed (orange) and unpublished (red).","## Working with Drafts","Ever tried to revise a published article in WordPress? Yes, it works, but if you click on \"save\", then all your changes are directly live. Typemill is much more flexible here and allows you to keep your original version live while you work on a **drafted version** in the background. This is how Typemill handles it: ","* In **visual mode**: Typemill stores your changes in a new draft automatically as soon as you save any content-block.\n* In **raw mode**: To store changes in a new draft, simply click on the \"save draft\"-button in the publish controller.\n* You can work on a draft as long as you want without changing the live version. Your changes go live if you click the button \"publish\".\n* In visual mode, you can also use the discard-button and go back to the published version."] \ No newline at end of file diff --git a/content/00-welcome/05-edit-test.yaml b/content/00-welcome/05-edit-test.yaml deleted file mode 100644 index b6b5024..0000000 --- a/content/00-welcome/05-edit-test.yaml +++ /dev/null @@ -1,2 +0,0 @@ -meta: - navtitle: 'edit test' diff --git a/content/00-welcome/05-todos.md b/content/00-welcome/05-todos.md new file mode 100644 index 0000000..5a15e4e --- /dev/null +++ b/content/00-welcome/05-todos.md @@ -0,0 +1,55 @@ +# ToDos Version 2 + +[TOC] + +## Visual Editor + +* FIXED: File is not published from tmp to media/files if you save the block. + +## Raw Editor + +* DONE + +## Medialib + +bla + +## Posts + +* Setup + +## Plugins + +* Asset Class + +## Frontend + +* DONE +* DONE: Test restrictions + +## Else + +* Sitemap and ping +* Captcha +* Clear Cache +* Security Log +* Backend fields + +## Fixes + +* DONE: Session handling: csrf fail and session start error if restrictions are active +* Editor: Warn if open another block + +## Select userroles + +* Userroles for file restriction: in vue-blox-components loaded via api +* Userroles for userfields: in php model user getUserFields() +* Userroles for meta: in php controller apiAuthorMeta getMeta() +* Plugins and themes: in php model extension getThemeDefinitions() + +## License Check + +* On activation in apiControllerExtension. It checks the license in yaml. +* In plugin php code with setPremiumLicense +* In static plugins, it checks manual premium list and method setPremiumLicense and more + diff --git a/content/00-welcome/05-todos.txt b/content/00-welcome/05-todos.txt new file mode 100644 index 0000000..a66e7ef --- /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","## Medialib","bla","## Posts","* Setup","## Plugins","* Asset Class","## Frontend","* DONE\n* DONE: Test restrictions","## ToDos","* DONE: Session handling: csrf fail and session start error if restrictions are active\n* Editor: Warn if open another block\n* Media Library\n* Posts\n* Recover Password\n* events\n* error messages\n* translations\n* Sitemap and ping\n* Captcha\n* Clear Cache\n* Security Log\n* Backend fields\n* Proxy","## 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 new file mode 100644 index 0000000..48e3e65 --- /dev/null +++ b/content/00-welcome/05-todos.yaml @@ -0,0 +1,18 @@ +meta: + navtitle: 'To Dos' + title: 'A list of open tasks' + description: ' Visual Editor' + heroimage: null + heroimagealt: null + owner: Sebastian + author: null + allowedrole: contributor + alloweduser: null + manualdate: null + modified: '2023-06-19' + created: '2023-06-19' + time: 15-15-01 + reference: null + referencetype: null + hide: false + noindex: false diff --git a/content/00-welcome/06-new.txt b/content/00-welcome/06-new.txt deleted file mode 100644 index 1c1d1b4..0000000 --- a/content/00-welcome/06-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","* 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* save draft\n* switch to raw","## Raw Editor","* Setup","## Meta Tabs","* Setup","## Medialib","* Setup"] \ No newline at end of file diff --git a/content/00-welcome/06-new.yaml b/content/00-welcome/06-new.yaml deleted file mode 100644 index 194e33a..0000000 --- a/content/00-welcome/06-new.yaml +++ /dev/null @@ -1,2 +0,0 @@ -meta: - navtitle: new diff --git a/content/01-cyanine-theme/01-footer.yaml b/content/01-cyanine-theme/01-footer.yaml index ceae2f8..e54aa81 100644 --- a/content/01-cyanine-theme/01-footer.yaml +++ b/content/01-cyanine-theme/01-footer.yaml @@ -1,15 +1,8 @@ meta: - title: '3-Column Footer' - description: 'Cyanine provides a three column footer at the bottom of each page. You can use markdown for each column. Make sure that you use the correct headline-level (we suggest a headline level 3 or level 4 to keep the logical headline hierarchy in the document). You can, of course, also add link-lists or' - heroimage: '' - heroimagealt: null - owner: null - author: trendschau - manualdate: '2021-07-01' + navtitle: footer + owner: Sebastian + created: '2023-06-12' + time: 21-41-25 modified: '2021-05-18' - created: '2020-06-11' - time: 11-48-00 - navtitle: '' - hide: false - allowedrole: null - alloweduser: null + description: 'Cyanine provides a three column footer at the bottom of each page. You can use markdown for each column. Make sure that you use the correct headline-level (we' + title: '3-Column Footer' diff --git a/content/01-cyanine-theme/02-content-elements.yaml b/content/01-cyanine-theme/02-content-elements.yaml index 68e0c1a..b36aa81 100644 --- a/content/01-cyanine-theme/02-content-elements.yaml +++ b/content/01-cyanine-theme/02-content-elements.yaml @@ -1,16 +1,8 @@ meta: navtitle: 'content elements' - title: 'Content Elements' - description: "Cyanine provides a lot of other settings for your content area. For example: \nAdd an edit-button for github, gitlab or other plattforms.\nShow the author.\nShow the publish date.\nShow the chapter numbers in the navigation.\n" - heroimage: '' - heroimagealt: null - hide: false - noindex: false - owner: trendschau - author: 'Sebastian Schürmanns' - allowedrole: null - alloweduser: null - manualdate: null + owner: Sebastian + created: '2023-06-12' + time: 22-36-50 modified: '2021-11-24' - created: '2021-06-17' - time: 16-23-24 + title: 'Cyanine provides a lot of other settings for your content area. For example:' + description: "\nAdd an edit-button for github, gitlab or other plattforms.\nShow the author.\nShow the publish date.\nShow the chapter numbers in the navigation.\n" diff --git a/content/01-cyanine-theme/03-colors-and-fonts.yaml b/content/01-cyanine-theme/03-colors-and-fonts.yaml index 9432aef..dc483de 100644 --- a/content/01-cyanine-theme/03-colors-and-fonts.yaml +++ b/content/01-cyanine-theme/03-colors-and-fonts.yaml @@ -1,15 +1,8 @@ meta: navtitle: 'colors and fonts' - hide: false - title: 'Colors and Fonts' - description: 'First of all cyanine supports individual logos. If you want to use our logo, then please upload it in the system settings. Cyanine will automatically replace the title text with your logo. ' - heroimage: null - heroimagealt: null - owner: null - author: trendschau - allowedrole: null - alloweduser: null - manualdate: null - modified: '2021-10-04' - created: '2020-06-11' - time: 20-37-12 + owner: Sebastian + created: '2023-06-12' + time: 22-36-53 + modified: '2023-03-26' + title: 'First of all cyanine supports individual logos. If you want to use our logo, then please upload it in the system settings. Cyanine will automatically replace the title text with your logo. You can also upload your own favicon in the system settings.' + description: 'Cyanine allows you to change many colors. Please make sure that your color-combinations are readable and accessible. The following colors are editable: ' diff --git a/data/navigation/navi-draft.txt b/data/navigation/navi-draft.txt index b154045..34243e4 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:7:{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:16:"05-edit-test.txt";s:11:"elementType";s:4:"file";s:6:"status";s:11:"unpublished";s:8:"fileType";s:3:"txt";s:5:"order";s:2:"05";s:4:"name";s:9:"edit test";s:4:"slug";s:9:"edit-test";s:4:"path";s:28:"/00-welcome/05-edit-test.txt";s:15:"pathWithoutType";s:24:"/00-welcome/05-edit-test";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:18:"/welcome/edit-test";s:6:"urlRel";s:27:"/typemill/welcome/edit-test";s:6:"urlAbs";s:43:"http://localhost/typemill/welcome/edit-test";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:6;O:8:"stdClass":20:{s:12:"originalName";s:10:"06-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:"06";s:4:"name";s:3:"new";s:4:"slug";s:3:"new";s:4:"path";s:22:"/00-welcome/06-new.txt";s:15:"pathWithoutType";s:18:"/00-welcome/06-new";s:3:"key";i:6;s:7:"keyPath";s:3:"0.6";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"6";}s:7:"chapter";s:3:"1.7";s:9:"urlRelWoF";s:12:"/welcome/new";s:6:"urlRel";s:21:"/typemill/welcome/new";s:6:"urlAbs";s:37:"http://localhost/typemill/welcome/new";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:4:{i:0;O:8:"stdClass":20:{s:12:"originalName";s:17:"00-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:"00";s:4:"name";s:11:"landingpage";s:4:"slug";s:11:"landingpage";s:4:"path";s:35:"/01-cyanine-theme/00-landingpage.md";s:15:"pathWithoutType";s:32:"/01-cyanine-theme/00-landingpage";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: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:0;}i:1;O:8:"stdClass":20:{s:12:"originalName";s:12:"01-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:"01";s:4:"name";s:6:"footer";s:4:"slug";s:6:"footer";s:4:"path";s:30:"/01-cyanine-theme/01-footer.md";s:15:"pathWithoutType";s:27:"/01-cyanine-theme/01-footer";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: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;}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;}}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:4:{i:0;O:8:"stdClass":20:{s:12:"originalName";s:17:"00-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:"00";s:4:"name";s:11:"landingpage";s:4:"slug";s:11:"landingpage";s:4:"path";s:35:"/01-cyanine-theme/00-landingpage.md";s:15:"pathWithoutType";s:32:"/01-cyanine-theme/00-landingpage";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: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:1;O:8:"stdClass":20:{s:12:"originalName";s:12:"01-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:"01";s:4:"name";s:6:"footer";s:4:"slug";s:6:"footer";s:4:"path";s:30:"/01-cyanine-theme/01-footer.md";s:15:"pathWithoutType";s:27:"/01-cyanine-theme/01-footer";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: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;}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;}}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 01b3ec7..3e601cd 100644 --- a/data/navigation/navi-extended.txt +++ b/data/navigation/navi-extended.txt @@ -1,77 +1,71 @@ /welcome: - navtitle: '' + navtitle: welcome hide: false noindex: false path: /00-welcome keyPath: 0 /welcome/markdown-test: - navtitle: '' + navtitle: 'markdown test' hide: false noindex: false path: /00-welcome/00-markdown-test.md keyPath: '0.0' /welcome/manage-access: - navtitle: '' + navtitle: 'manage access' hide: false noindex: false path: /00-welcome/01-manage-access.md keyPath: '0.1' /welcome/get-help: - navtitle: '' + navtitle: 'get help' hide: false noindex: false path: /00-welcome/02-get-help.md keyPath: '0.2' /welcome/setup-your-website: - navtitle: '' + navtitle: 'setup your website' hide: false noindex: false path: /00-welcome/03-setup-your-website.md keyPath: '0.3' /welcome/write-content: - navtitle: '' + navtitle: 'write content' hide: false noindex: false path: /00-welcome/04-write-content.md keyPath: '0.4' -/welcome/edit-test: - navtitle: '' +/welcome/todos: + navtitle: 'To Dos' hide: false noindex: false - path: /00-welcome/05-edit-test.txt + path: /00-welcome/05-todos.txtmd keyPath: '0.5' -/welcome/new: - navtitle: '' - hide: false - noindex: false - path: /00-welcome/06-new.txt - keyPath: '0.6' /cyanine-theme: - navtitle: '' + navtitle: 'cyanine theme' hide: false noindex: false path: /01-cyanine-theme keyPath: 1 /cyanine-theme/landingpage: - navtitle: '' + navtitle: landingpage hide: false - noindex: false + noindex: true path: /01-cyanine-theme/00-landingpage.md keyPath: '1.0' /cyanine-theme/footer: - navtitle: '' + navtitle: footer hide: false noindex: false path: /01-cyanine-theme/01-footer.md keyPath: '1.1' /cyanine-theme/content-elements: - navtitle: '' + navtitle: 'content elements' hide: false noindex: false path: /01-cyanine-theme/02-content-elements.md keyPath: '1.2' /cyanine-theme/colors-and-fonts: - navtitle: '' + navtitle: 'colors and fonts' hide: false noindex: false path: /01-cyanine-theme/03-colors-and-fonts.md diff --git a/index.php b/index.php index 3dac4a4..d8b9dd9 100644 --- a/index.php +++ b/index.php @@ -2,6 +2,4 @@ require __DIR__ . '/system/vendor/autoload.php'; -require __DIR__ . '/system/typemill/system.php'; - -$app->run(); \ No newline at end of file +require __DIR__ . '/system/typemill/system.php'; \ No newline at end of file diff --git a/media/files/filerestrictions.yaml b/media/files/filerestrictions.yaml index c7fa688..2b22b99 100644 --- a/media/files/filerestrictions.yaml +++ b/media/files/filerestrictions.yaml @@ -1,2 +1 @@ -media/files/preview.pdf: member -media/files/ebook.epub: author +media/files/typemill-v2-navigation.gif: member diff --git a/media/live/don-quijote-1.webp b/media/live/don-quijote-1.webp deleted file mode 100644 index 7581eca..0000000 Binary files a/media/live/don-quijote-1.webp and /dev/null differ diff --git a/media/live/don-quijote-resized-1.webp b/media/live/don-quijote-resized-1.webp deleted file mode 100644 index 3dd5596..0000000 Binary files a/media/live/don-quijote-resized-1.webp and /dev/null differ diff --git a/media/live/don-quijote-resized-2.webp b/media/live/don-quijote-resized-2.webp deleted file mode 100644 index 3dd5596..0000000 Binary files a/media/live/don-quijote-resized-2.webp and /dev/null differ diff --git a/media/live/don-quijote-resized.webp b/media/live/don-quijote-resized.webp deleted file mode 100644 index 3dd5596..0000000 Binary files a/media/live/don-quijote-resized.webp and /dev/null differ diff --git a/media/live/don-quijote.webp b/media/live/don-quijote.webp deleted file mode 100644 index 7581eca..0000000 Binary files a/media/live/don-quijote.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-1.webp b/media/live/external-content-duckduckgo-com-th-1.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-1.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-10.webp b/media/live/external-content-duckduckgo-com-th-10.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-10.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-11.webp b/media/live/external-content-duckduckgo-com-th-11.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-11.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-12.webp b/media/live/external-content-duckduckgo-com-th-12.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-12.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-13.webp b/media/live/external-content-duckduckgo-com-th-13.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-13.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-2.webp b/media/live/external-content-duckduckgo-com-th-2.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-2.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-3.webp b/media/live/external-content-duckduckgo-com-th-3.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-3.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-4.webp b/media/live/external-content-duckduckgo-com-th-4.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-4.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-5.webp b/media/live/external-content-duckduckgo-com-th-5.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-5.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-6.webp b/media/live/external-content-duckduckgo-com-th-6.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-6.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-7.webp b/media/live/external-content-duckduckgo-com-th-7.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-7.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-8.webp b/media/live/external-content-duckduckgo-com-th-8.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-8.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th-9.webp b/media/live/external-content-duckduckgo-com-th-9.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th-9.webp and /dev/null differ diff --git a/media/live/external-content-duckduckgo-com-th.webp b/media/live/external-content-duckduckgo-com-th.webp deleted file mode 100644 index 6126640..0000000 Binary files a/media/live/external-content-duckduckgo-com-th.webp and /dev/null differ diff --git a/media/live/filterpage.webp b/media/live/filterpage.webp deleted file mode 100644 index 9f544da..0000000 Binary files a/media/live/filterpage.webp and /dev/null differ diff --git a/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1-1.webp b/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1-1.webp deleted file mode 100644 index 4b1013b..0000000 Binary files a/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1-1.webp and /dev/null differ diff --git a/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1-2.webp b/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1-2.webp deleted file mode 100644 index 4b1013b..0000000 Binary files a/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1-2.webp and /dev/null differ diff --git a/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1-3.webp b/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1-3.webp deleted file mode 100644 index 4b1013b..0000000 Binary files a/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1-3.webp and /dev/null differ diff --git a/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1.webp b/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1.webp deleted file mode 100644 index 4b1013b..0000000 Binary files a/media/live/kai-dahms-v0nbnxlwjzk-3unsp-1.webp and /dev/null differ diff --git a/media/live/nvhmq-pb-cs.webp b/media/live/nvhmq-pb-cs.webp deleted file mode 100644 index 0ad4d71..0000000 Binary files a/media/live/nvhmq-pb-cs.webp and /dev/null differ diff --git a/media/live/pxart-1.webp b/media/live/pxart-1.webp deleted file mode 100644 index e74f0ad..0000000 Binary files a/media/live/pxart-1.webp and /dev/null differ diff --git a/media/live/pxart-2.webp b/media/live/pxart-2.webp deleted file mode 100644 index e74f0ad..0000000 Binary files a/media/live/pxart-2.webp and /dev/null differ diff --git a/media/live/pxart.webp b/media/live/pxart.webp deleted file mode 100644 index e74f0ad..0000000 Binary files a/media/live/pxart.webp and /dev/null differ diff --git a/media/live/tagtab-1.webp b/media/live/tagtab-1.webp deleted file mode 100644 index 307b20c..0000000 Binary files a/media/live/tagtab-1.webp and /dev/null differ diff --git a/media/live/tagtab.webp b/media/live/tagtab.webp deleted file mode 100644 index 307b20c..0000000 Binary files a/media/live/tagtab.webp and /dev/null differ diff --git a/media/live/visual-shortcodes.webp b/media/live/visual-shortcodes.webp deleted file mode 100644 index 9698ac5..0000000 Binary files a/media/live/visual-shortcodes.webp and /dev/null differ diff --git a/media/live/yaml-editor.webp b/media/live/yaml-editor.webp deleted file mode 100644 index 97a1e53..0000000 Binary files a/media/live/yaml-editor.webp and /dev/null differ diff --git a/media/live/youtube-nvhmq-pb-cs-1.webp b/media/live/youtube-nvhmq-pb-cs-1.webp deleted file mode 100644 index 0ad4d71..0000000 Binary files a/media/live/youtube-nvhmq-pb-cs-1.webp and /dev/null differ diff --git a/media/live/youtube-nvhmq-pb-cs-2.webp b/media/live/youtube-nvhmq-pb-cs-2.webp deleted file mode 100644 index 0ad4d71..0000000 Binary files a/media/live/youtube-nvhmq-pb-cs-2.webp and /dev/null differ diff --git a/media/live/youtube-nvhmq-pb-cs.webp b/media/live/youtube-nvhmq-pb-cs.webp deleted file mode 100644 index 0ad4d71..0000000 Binary files a/media/live/youtube-nvhmq-pb-cs.webp and /dev/null differ diff --git a/media/original/don-quijote-1.png b/media/original/don-quijote-1.png deleted file mode 100644 index 0bdca53..0000000 Binary files a/media/original/don-quijote-1.png and /dev/null differ diff --git a/media/original/don-quijote-resized-1.png b/media/original/don-quijote-resized-1.png deleted file mode 100644 index f55ffa6..0000000 Binary files a/media/original/don-quijote-resized-1.png and /dev/null differ diff --git a/media/original/don-quijote-resized-2.png b/media/original/don-quijote-resized-2.png deleted file mode 100644 index f55ffa6..0000000 Binary files a/media/original/don-quijote-resized-2.png and /dev/null differ diff --git a/media/original/don-quijote-resized.png b/media/original/don-quijote-resized.png deleted file mode 100644 index f55ffa6..0000000 Binary files a/media/original/don-quijote-resized.png and /dev/null differ diff --git a/media/original/don-quijote.png b/media/original/don-quijote.png deleted file mode 100644 index 0bdca53..0000000 Binary files a/media/original/don-quijote.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-1.png b/media/original/external-content-duckduckgo-com-th-1.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-1.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-10.png b/media/original/external-content-duckduckgo-com-th-10.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-10.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-11.png b/media/original/external-content-duckduckgo-com-th-11.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-11.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-12.png b/media/original/external-content-duckduckgo-com-th-12.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-12.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-13.png b/media/original/external-content-duckduckgo-com-th-13.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-13.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-2.png b/media/original/external-content-duckduckgo-com-th-2.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-2.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-3.png b/media/original/external-content-duckduckgo-com-th-3.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-3.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-4.png b/media/original/external-content-duckduckgo-com-th-4.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-4.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-5.png b/media/original/external-content-duckduckgo-com-th-5.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-5.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-6.png b/media/original/external-content-duckduckgo-com-th-6.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-6.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-7.png b/media/original/external-content-duckduckgo-com-th-7.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-7.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-8.png b/media/original/external-content-duckduckgo-com-th-8.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-8.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th-9.png b/media/original/external-content-duckduckgo-com-th-9.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th-9.png and /dev/null differ diff --git a/media/original/external-content-duckduckgo-com-th.png b/media/original/external-content-duckduckgo-com-th.png deleted file mode 100644 index 219ecae..0000000 Binary files a/media/original/external-content-duckduckgo-com-th.png and /dev/null differ diff --git a/media/original/filterpage.png b/media/original/filterpage.png deleted file mode 100644 index 3ef4965..0000000 Binary files a/media/original/filterpage.png and /dev/null differ diff --git a/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1-1.webp b/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1-1.webp deleted file mode 100644 index 6f152ce..0000000 Binary files a/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1-1.webp and /dev/null differ diff --git a/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1-2.webp b/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1-2.webp deleted file mode 100644 index 6f152ce..0000000 Binary files a/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1-2.webp and /dev/null differ diff --git a/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1-3.webp b/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1-3.webp deleted file mode 100644 index 6f152ce..0000000 Binary files a/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1-3.webp and /dev/null differ diff --git a/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1.png b/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1.png deleted file mode 100644 index dd18361..0000000 Binary files a/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1.png and /dev/null differ diff --git a/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1.webp b/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1.webp deleted file mode 100644 index 6f152ce..0000000 Binary files a/media/original/kai-dahms-v0nbnxlwjzk-3unsp-1.webp and /dev/null differ diff --git a/media/original/nvhmq-pb-cs.jpg b/media/original/nvhmq-pb-cs.jpg deleted file mode 100644 index 3917c9e..0000000 Binary files a/media/original/nvhmq-pb-cs.jpg and /dev/null differ diff --git a/media/original/pxart-1.png b/media/original/pxart-1.png deleted file mode 100644 index f981285..0000000 Binary files a/media/original/pxart-1.png and /dev/null differ diff --git a/media/original/pxart-2.png b/media/original/pxart-2.png deleted file mode 100644 index f981285..0000000 Binary files a/media/original/pxart-2.png and /dev/null differ diff --git a/media/original/pxart.png b/media/original/pxart.png deleted file mode 100644 index f981285..0000000 Binary files a/media/original/pxart.png and /dev/null differ diff --git a/media/original/tagtab-1.png b/media/original/tagtab-1.png deleted file mode 100644 index 64f3a36..0000000 Binary files a/media/original/tagtab-1.png and /dev/null differ diff --git a/media/original/tagtab.png b/media/original/tagtab.png deleted file mode 100644 index 64f3a36..0000000 Binary files a/media/original/tagtab.png and /dev/null differ diff --git a/media/original/visual-shortcodes.png b/media/original/visual-shortcodes.png deleted file mode 100644 index b54e452..0000000 Binary files a/media/original/visual-shortcodes.png and /dev/null differ diff --git a/media/original/yaml-editor.png b/media/original/yaml-editor.png deleted file mode 100644 index f2ed70c..0000000 Binary files a/media/original/yaml-editor.png and /dev/null differ diff --git a/media/original/youtube-nvhmq-pb-cs-1.jpg b/media/original/youtube-nvhmq-pb-cs-1.jpg deleted file mode 100644 index 3917c9e..0000000 Binary files a/media/original/youtube-nvhmq-pb-cs-1.jpg and /dev/null differ diff --git a/media/original/youtube-nvhmq-pb-cs-2.jpg b/media/original/youtube-nvhmq-pb-cs-2.jpg deleted file mode 100644 index 3917c9e..0000000 Binary files a/media/original/youtube-nvhmq-pb-cs-2.jpg and /dev/null differ diff --git a/media/original/youtube-nvhmq-pb-cs.jpg b/media/original/youtube-nvhmq-pb-cs.jpg deleted file mode 100644 index 3917c9e..0000000 Binary files a/media/original/youtube-nvhmq-pb-cs.jpg and /dev/null differ diff --git a/media/thumbs/don-quijote-1.webp b/media/thumbs/don-quijote-1.webp deleted file mode 100644 index 7581eca..0000000 Binary files a/media/thumbs/don-quijote-1.webp and /dev/null differ diff --git a/media/thumbs/don-quijote-resized-1.webp b/media/thumbs/don-quijote-resized-1.webp deleted file mode 100644 index d0cce71..0000000 Binary files a/media/thumbs/don-quijote-resized-1.webp and /dev/null differ diff --git a/media/thumbs/don-quijote-resized-2.webp b/media/thumbs/don-quijote-resized-2.webp deleted file mode 100644 index d0cce71..0000000 Binary files a/media/thumbs/don-quijote-resized-2.webp and /dev/null differ diff --git a/media/thumbs/don-quijote-resized.webp b/media/thumbs/don-quijote-resized.webp deleted file mode 100644 index d0cce71..0000000 Binary files a/media/thumbs/don-quijote-resized.webp and /dev/null differ diff --git a/media/thumbs/don-quijote.webp b/media/thumbs/don-quijote.webp deleted file mode 100644 index 7581eca..0000000 Binary files a/media/thumbs/don-quijote.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-1.webp b/media/thumbs/external-content-duckduckgo-com-th-1.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-1.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-10.webp b/media/thumbs/external-content-duckduckgo-com-th-10.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-10.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-11.webp b/media/thumbs/external-content-duckduckgo-com-th-11.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-11.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-12.webp b/media/thumbs/external-content-duckduckgo-com-th-12.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-12.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-13.webp b/media/thumbs/external-content-duckduckgo-com-th-13.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-13.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-2.webp b/media/thumbs/external-content-duckduckgo-com-th-2.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-2.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-3.webp b/media/thumbs/external-content-duckduckgo-com-th-3.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-3.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-4.webp b/media/thumbs/external-content-duckduckgo-com-th-4.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-4.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-5.webp b/media/thumbs/external-content-duckduckgo-com-th-5.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-5.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-6.webp b/media/thumbs/external-content-duckduckgo-com-th-6.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-6.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-7.webp b/media/thumbs/external-content-duckduckgo-com-th-7.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-7.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-8.webp b/media/thumbs/external-content-duckduckgo-com-th-8.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-8.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th-9.webp b/media/thumbs/external-content-duckduckgo-com-th-9.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th-9.webp and /dev/null differ diff --git a/media/thumbs/external-content-duckduckgo-com-th.webp b/media/thumbs/external-content-duckduckgo-com-th.webp deleted file mode 100644 index aba76b1..0000000 Binary files a/media/thumbs/external-content-duckduckgo-com-th.webp and /dev/null differ diff --git a/media/thumbs/filterpage.webp b/media/thumbs/filterpage.webp deleted file mode 100644 index 0c6197b..0000000 Binary files a/media/thumbs/filterpage.webp and /dev/null differ diff --git a/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1-1.webp b/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1-1.webp deleted file mode 100644 index 06c9bee..0000000 Binary files a/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1-1.webp and /dev/null differ diff --git a/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1-2.webp b/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1-2.webp deleted file mode 100644 index 06c9bee..0000000 Binary files a/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1-2.webp and /dev/null differ diff --git a/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1-3.webp b/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1-3.webp deleted file mode 100644 index 06c9bee..0000000 Binary files a/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1-3.webp and /dev/null differ diff --git a/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1.webp b/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1.webp deleted file mode 100644 index 06c9bee..0000000 Binary files a/media/thumbs/kai-dahms-v0nbnxlwjzk-3unsp-1.webp and /dev/null differ diff --git a/media/thumbs/nvhmq-pb-cs.webp b/media/thumbs/nvhmq-pb-cs.webp deleted file mode 100644 index c68a879..0000000 Binary files a/media/thumbs/nvhmq-pb-cs.webp and /dev/null differ diff --git a/media/thumbs/pxart-1.webp b/media/thumbs/pxart-1.webp deleted file mode 100644 index 1b144c8..0000000 Binary files a/media/thumbs/pxart-1.webp and /dev/null differ diff --git a/media/thumbs/pxart-2.webp b/media/thumbs/pxart-2.webp deleted file mode 100644 index 1b144c8..0000000 Binary files a/media/thumbs/pxart-2.webp and /dev/null differ diff --git a/media/thumbs/pxart.webp b/media/thumbs/pxart.webp deleted file mode 100644 index 1b144c8..0000000 Binary files a/media/thumbs/pxart.webp and /dev/null differ diff --git a/media/thumbs/tagtab-1.webp b/media/thumbs/tagtab-1.webp deleted file mode 100644 index bbff56d..0000000 Binary files a/media/thumbs/tagtab-1.webp and /dev/null differ diff --git a/media/thumbs/tagtab.webp b/media/thumbs/tagtab.webp deleted file mode 100644 index bbff56d..0000000 Binary files a/media/thumbs/tagtab.webp and /dev/null differ diff --git a/media/thumbs/visual-shortcodes.webp b/media/thumbs/visual-shortcodes.webp deleted file mode 100644 index cd083ac..0000000 Binary files a/media/thumbs/visual-shortcodes.webp and /dev/null differ diff --git a/media/thumbs/yaml-editor.webp b/media/thumbs/yaml-editor.webp deleted file mode 100644 index 712aec3..0000000 Binary files a/media/thumbs/yaml-editor.webp and /dev/null differ diff --git a/media/thumbs/youtube-nvhmq-pb-cs-1.webp b/media/thumbs/youtube-nvhmq-pb-cs-1.webp deleted file mode 100644 index c68a879..0000000 Binary files a/media/thumbs/youtube-nvhmq-pb-cs-1.webp and /dev/null differ diff --git a/media/thumbs/youtube-nvhmq-pb-cs-2.webp b/media/thumbs/youtube-nvhmq-pb-cs-2.webp deleted file mode 100644 index c68a879..0000000 Binary files a/media/thumbs/youtube-nvhmq-pb-cs-2.webp and /dev/null differ diff --git a/media/thumbs/youtube-nvhmq-pb-cs.webp b/media/thumbs/youtube-nvhmq-pb-cs.webp deleted file mode 100644 index c68a879..0000000 Binary files a/media/thumbs/youtube-nvhmq-pb-cs.webp and /dev/null differ diff --git a/system/typemill/Assets.php b/system/typemill/Assets.php new file mode 100644 index 0000000..ed4b0d1 --- /dev/null +++ b/system/typemill/Assets.php @@ -0,0 +1,304 @@ +baseUrl = $baseUrl; + $this->JS = array(); + $this->CSS = array(); + $this->inlineJS = array(); + $this->inlineCSS = array(); + $this->editorJS = array(); + $this->editorCSS = array(); + $this->editorInlineJS = array(); + $this->svgSymbols = array(); + $this->meta = array(); + $this->imageUrl = false; + $this->imageFolder = 'original'; + } + + public function setUri($uri) + { + $this->uri = $uri; + } + + public function setBaseUrl($baseUrl) + { + $this->baseUrl = $baseUrl; + } + + public function image($url) + { + $this->imageUrl = $url; + return $this; + } + + public function resize($width,$height) + { + $pathinfo = pathinfo($this->imageUrl); + $extension = strtolower($pathinfo['extension']); + $imageName = $pathinfo['filename']; + + $desiredSizes = ['custom' => []]; + + $resize = '-'; + + if(is_int($width) && $width < 10000) + { + $resize .= $width; + $desiredSizes['custom']['width'] = $width; + } + + $resize .= 'x'; + + if(is_int($height) && $height < 10000) + { + $resize .= $height; + $desiredSizes['custom']['height'] = $height; + } + + $processImage = new ProcessImage($desiredSizes); + + $processImage->checkFolders('images'); + + $imageNameResized = $imageName . $resize; + $imagePathResized = $processImage->customFolder . $imageNameResized . '.' . $extension; + $imageUrlResized = 'media/custom/' . $imageNameResized . '.' . $extension; + + if(!file_exists( $imagePathResized )) + { + # if custom version does not exist, use original version for resizing + $imageFolder = ($this->imageFolder == 'original') ? $processImage->originalFolder : $processImage->customFolder; + + $imagePath = $imageFolder . $pathinfo['basename']; + + $resizedImage = $processImage->generateSizesFromImageFile($imageUrlResized, $imagePath); + + $savedImage = $processImage->saveImage($processImage->customFolder, $resizedImage['custom'], $imageNameResized, $extension); + + if(!$savedImage) + { + # return old image url without resize + return $this; + } + } + # set folder to custom, so that the next method uses the correct (resized) version + $this->imageFolder = 'custom'; + + $this->imageUrl = $imageUrlResized; + return $this; + } + + public function grayscale() + { + $pathinfo = pathinfo($this->imageUrl); + $extension = strtolower($pathinfo['extension']); + $imageName = $pathinfo['filename']; + + $processImage = new ProcessImage([]); + + $processImage->checkFolders('images'); + + $imageNameGrayscale = $imageName . '-grayscale'; + $imagePathGrayscale = $processImage->customFolder . $imageNameGrayscale . '.' . $extension; + $imageUrlGrayscale = 'media/custom/' . $imageNameGrayscale . '.' . $extension; + + if(!file_exists( $imagePathGrayscale )) + { + # if custom-version does not exist, use live-version for grayscale-manipulation. + $imageFolder = ($this->imageFolder == 'original') ? $processImage->liveFolder : $processImage->customFolder; + + $imagePath = $imageFolder . $pathinfo['basename']; + + $grayscaleImage = $processImage->grayscale($imagePath, $extension); + + $savedImage = $processImage->saveImage($processImage->customFolder, $grayscaleImage, $imageNameGrayscale, $extension); + + if(!$savedImage) + { + # return old image url without resize + return $this; + } + } + + # set folder to custom, so that the next method uses the correct (resized) version + $this->imageFolder = 'custom'; + + $this->imageUrl = $imageUrlGrayscale; + return $this; + } + + public function src() + { + # when we finish it, we shoud reset all settings + $imagePath = $this->baseUrl . '/' . $this->imageUrl; + $this->imageUrl = false; + $this->imageFolder = 'original'; + + return $imagePath; + } + + public function addCSS($CSS) + { + $CSSfile = $this->getFileUrl($CSS); + + if($CSSfile) + { + $this->CSS[] = ''; + } + } + + public function addInlineCSS($CSS) + { + $this->inlineCSS[] = ''; + } + + public function addJS($JS) + { + $JSfile = $this->getFileUrl($JS); + + if($JSfile) + { + $this->JS[] = ''; + } + } + + public function addInlineJS($JS) + { + $this->inlineJS[] = ''; + } + + public function activateVue() + { + $vueUrl = ''; + if(!in_array($vueUrl, $this->JS)) + { + $this->JS[] = $vueUrl; + } + } + + public function activateAxios() + { + $axiosUrl = ''; + if(!in_array($axiosUrl, $this->JS)) + { + $this->JS[] = $axiosUrl; + + $axios = ''; + $this->JS[] = $axios; + } + } + + public function activateTachyons() + { + $tachyonsUrl = ''; + if(!in_array($tachyonsUrl, $this->CSS)) + { + $this->CSS[] = $tachyonsUrl; + } + } + + public function addSvgSymbol($symbol) + { + $this->svgSymbols[] = $symbol; + } + + # add JS to enhance the blox-editor in author area + public function addEditorJS($JS) + { + $JSfile = $this->getFileUrl($JS); + + if($JSfile) + { + $this->editorJS[] = ''; + } + } + + public function addEditorInlineJS($JS) + { + $this->editorInlineJS[] = ''; + } + + public function addEditorCSS($CSS) + { + $CSSfile = $this->getFileUrl($CSS); + + if($CSSfile) + { + $this->editorCSS[] = ''; + } + } + + public function addMeta($key,$meta) + { + $this->meta[$key] = $meta; + } + + public function renderEditorJS() + { + return implode("\n", $this->editorJS) . implode("\n", $this->editorInlineJS); + } + + public function renderEditorCSS() + { + return implode("\n", $this->editorCSS); + } + + public function renderCSS() + { + return implode("\n", $this->CSS) . implode("\n", $this->inlineCSS); + } + + public function renderJS() + { + return implode("\n", $this->JS) . implode("\n", $this->inlineJS); + } + + public function renderSvg() + { + return implode('', $this->svgSymbols); + } + + public function renderMeta() + { + $metaLines = ''; + foreach($this->meta as $meta) + { + $metaLines .= "\n"; + $metaLines .= $meta; + } + return $metaLines; + } + /** + * Checks, if a string is a valid internal or external ressource like js-file or css-file + * @params $path string + * @return string or false + */ + public function getFileUrl($path) + { + # check system path of file without parameter for fingerprinting + $internalFile = __DIR__ . '/../../plugins' . strtok($path, "?"); + + if(file_exists($internalFile)) + { + return $this->baseUrl . '/plugins' . $path; + } + + return $path; + + if(fopen($path, "r")) + { + return $path; + } + + return false; + } +} \ No newline at end of file diff --git a/system/typemill/Controllers/Controller.php b/system/typemill/Controllers/Controller.php index 9c10441..60d546e 100644 --- a/system/typemill/Controllers/Controller.php +++ b/system/typemill/Controllers/Controller.php @@ -5,6 +5,7 @@ namespace Typemill\Controllers; use DI\Container; use Slim\Routing\RouteContext; use Typemill\Models\StorageWrapper; +use Typemill\Events\OnTwigLoaded; # use Psr\Container\ContainerInterface; # use Typemill\Models\Folder; @@ -12,7 +13,6 @@ use Typemill\Models\StorageWrapper; # use Typemill\Models\WriteYaml; # use Typemill\Events\OnPageReady; # use Typemill\Events\OnPagetreeLoaded; -use Typemill\Events\OnTwigLoaded; abstract class Controller { @@ -22,15 +22,32 @@ abstract class Controller # holds the settings protected $settings; + protected $routeParser; + public function __construct(Container $container) { $this->c = $container; + $this->settings = $container->get('settings'); + $this->routeParser = $container->get('routeParser'); - $this->settings = $container->get('settings'); +# $this->csrf = $container->get('csrf'); + + $this->c->get('dispatcher')->dispatch(new OnTwigLoaded(false), 'onTwigLoaded'); } + public function getCsrfData($request) + { + $nameKey = $this->csrf->getTokenNameKey(); + $valueKey = $this->csrf->getTokenValueKey(); + + return [ + $nameKey => $request->getAttribute($nameKey), + $valueKey => $request->getAttribute($valueKey) + ]; + } + protected function settingActive($setting) { if(isset($this->settings[$setting]) && $this->settings[$setting]) @@ -85,6 +102,30 @@ abstract class Controller return '/' . $url; } + protected function addDatasets(array $formDefinitions) + { + foreach($formDefinitions as $fieldname => $field) + { + if(isset($field['type']) && $field['type'] == 'fieldset') + { + $formDefinitions[$fieldname]['fields'] = $this->addDatasets($field['fields']); + } + + if(isset($field['type']) && ($field['type'] == 'select' ) && isset($field['dataset']) && ($field['dataset'] == 'userroles' ) ) + { + $userroles = [null => null]; + foreach($this->c->get('acl')->getRoles() as $userrole) + { + $userroles[$userrole] = $userrole; + } + + $formDefinitions[$fieldname]['options'] = $userroles; + } + } + + return $formDefinitions; + } + protected function validateRights($userrole, $resource, $action) { $acl = $this->c->get('acl'); diff --git a/system/typemill/Controllers/ControllerApiAuthorArticle.php b/system/typemill/Controllers/ControllerApiAuthorArticle.php index e475be8..3f025f9 100644 --- a/system/typemill/Controllers/ControllerApiAuthorArticle.php +++ b/system/typemill/Controllers/ControllerApiAuthorArticle.php @@ -154,6 +154,132 @@ class ControllerApiAuthorArticle extends Controller return $response->withHeader('Content-Type', 'application/json'); } + 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' => 'You do not have enough rights.', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(422); + } + + $params = $request->getParsedBody(); + $validate = new Validation(); + $validInput = $validate->articleUpdate($params); + if($validInput !== true) + { + $errors = $validate->returnFirstValidationErrors($validInput); + $response->getBody()->write(json_encode([ + 'message' => reset($errors), + 'errors' => $errors + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(400); + } + + $navigation = new Navigation(); + $urlinfo = $this->c->get('urlinfo'); + $item = $this->getItem($navigation, $params['url'], $urlinfo); + if(!$item) + { + $response->getBody()->write(json_encode([ + 'message' => 'page not found', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(404); + } + + # save draft content + $content = new Content($urlinfo['baseurl']); + $markdown = $params['title'] . PHP_EOL . PHP_EOL . $params['body']; + $markdownArray = $content->markdownTextToArray($markdown); + $content->saveDraftMarkdown($item, $markdownArray); + + # refresh navigation and item + $navigation->clearNavigation(); + $draftNavigation = $navigation->getDraftNavigation($urlinfo, $this->settings['langattr']); + $draftNavigation = $navigation->setActiveNaviItems($draftNavigation, $item->keyPathArray); + $item = $navigation->getItemWithKeyPath($draftNavigation, $item->keyPathArray); + + # refresh content + $draftMarkdown = $content->getDraftMarkdown($item); + $draftMarkdownHtml = $content->addDraftHtml($draftMarkdown); + + $response->getBody()->write(json_encode([ + 'item' => $item, + 'navigation' => $draftNavigation, + 'content' => $draftMarkdownHtml + ])); + + return $response->withHeader('Content-Type', 'application/json'); + } + + 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' => 'You do not have enough rights.', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(422); + } + + $params = $request->getParsedBody(); + $validate = new Validation(); + $validInput = $validate->articleUpdate($params); + if($validInput !== true) + { + $errors = $validate->returnFirstValidationErrors($validInput); + $response->getBody()->write(json_encode([ + 'message' => reset($errors), + 'errors' => $errors + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(400); + } + + $navigation = new Navigation(); + $urlinfo = $this->c->get('urlinfo'); + $item = $this->getItem($navigation, $params['url'], $urlinfo); + if(!$item) + { + $response->getBody()->write(json_encode([ + 'message' => 'page not found', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(404); + } + + # save draft content + $content = new Content($urlinfo['baseurl']); + $markdown = $params['title'] . PHP_EOL . PHP_EOL . $params['body']; + $markdownArray = $content->markdownTextToArray($markdown); + $content->publishMarkdown($item, $markdownArray); + + # refresh navigation and item + $navigation->clearNavigation(); + $draftNavigation = $navigation->getDraftNavigation($urlinfo, $this->settings['langattr']); + $draftNavigation = $navigation->setActiveNaviItems($draftNavigation, $item->keyPathArray); + $item = $navigation->getItemWithKeyPath($draftNavigation, $item->keyPathArray); + + # refresh content + $draftMarkdown = $content->getDraftMarkdown($item); + $draftMarkdownHtml = $content->addDraftHtml($draftMarkdown); + + $response->getBody()->write(json_encode([ + 'item' => $item, + 'navigation' => $draftNavigation, + 'content' => $draftMarkdownHtml + ])); + + return $response->withHeader('Content-Type', 'application/json'); + } + public function discardArticleChanges(Request $request, Response $response, $args) { $validRights = $this->validateRights($request->getAttribute('c_userrole'), 'mycontent', 'edit'); @@ -355,6 +481,87 @@ class ControllerApiAuthorArticle extends Controller return $response->withHeader('Content-Type', 'application/json'); } + 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' => 'You do not have enough rights.', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(422); + } + + $params = $request->getParsedBody(); + $validate = new Validation(); + $validInput = $validate->articleRename($params); + if($validInput !== true) + { + $errors = $validate->returnFirstValidationErrors($validInput); + $response->getBody()->write(json_encode([ + 'message' => reset($errors), + 'errors' => $errors + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(400); + } + + $navigation = new Navigation(); + $urlinfo = $this->c->get('urlinfo'); + $item = $this->getItem($navigation, $params['url'], $urlinfo); + if(!$item) + { + $response->getBody()->write(json_encode([ + 'message' => 'page not found', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(404); + } + + # check if name exists + $parentUrl = str_replace($item->slug, '', $item->urlRelWoF); + if($parentUrl == '/') + { + $parentItem = new \stdClass; + $parentItem->folderContent = $navigation->getDraftNavigation($urlinfo, $this->settings['langattr']); + } + else + { + $parentItem = $this->getItem($navigation, $parentUrl, $urlinfo); + } + + foreach($parentItem->folderContent as $sibling) + { + if($sibling->slug == $params['slug']) + { + $response->getBody()->write(json_encode([ + 'message' => 'There is already a page with that slug', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(404); + } + } + + $navigation->renameItem($item, $params['slug']); + + $navigation->clearNavigation(); +# $this->updateSitemap($ping = true); + + # create the new url for redirects + $newUrlRel = str_replace($item->slug, $params['slug'], $item->urlRelWoF); + $url = $urlinfo['baseurl'] . '/tm/content/' . $this->settings['editor'] . $newUrlRel; + + $response->getBody()->write(json_encode([ + 'navigation' => $navigation->getDraftNavigation($urlinfo, $this->settings['langattr']), + 'message' => '', + 'url' => $url + ])); + + return $response->withHeader('Content-Type', 'application/json'); + } + + public function sortArticle(Request $request, Response $response, $args) { $validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update'); @@ -381,6 +588,14 @@ class ControllerApiAuthorArticle extends Controller return $response->withHeader('Content-Type', 'application/json')->withStatus(400); } + # set variables + $urlinfo = $this->c->get('urlinfo'); + $langattr = $this->settings['langattr'] ?? 'en'; + + $itemKeyPath = explode('.', $params['item_id']); + $parentKeyFrom = explode('.', $params['parent_id_from']); + $parentKeyTo = explode('.', $params['parent_id_to']); + # get navigation $navigation = new Navigation(); $draftNavigation = $navigation->getDraftNavigation($urlinfo, $langattr); @@ -396,10 +611,6 @@ class ControllerApiAuthorArticle extends Controller return $response->withHeader('Content-Type', 'application/json')->withStatus(404); } - $itemKeyPath = explode('.', $params['item_id']); - $parentKeyFrom = explode('.', $params['parent_id_from']); - $parentKeyTo = explode('.', $params['parent_id_to']); - # if an item is moved to the first level if($params['parent_id_to'] == '') { diff --git a/system/typemill/Controllers/ControllerApiAuthorBlock.php b/system/typemill/Controllers/ControllerApiAuthorBlock.php index 3d9f3f7..4e187b0 100644 --- a/system/typemill/Controllers/ControllerApiAuthorBlock.php +++ b/system/typemill/Controllers/ControllerApiAuthorBlock.php @@ -24,6 +24,7 @@ class ControllerApiAuthorBlock extends Controller } $params = $request->getParsedBody(); + $validate = new Validation(); $validInput = $validate->blockInput($params); if($validInput !== true) diff --git a/system/typemill/Controllers/ControllerApiAuthorMeta.php b/system/typemill/Controllers/ControllerApiAuthorMeta.php new file mode 100644 index 0000000..ec4dd07 --- /dev/null +++ b/system/typemill/Controllers/ControllerApiAuthorMeta.php @@ -0,0 +1,516 @@ +validateRights($request->getAttribute('c_userrole'), 'content', 'update'); + if(!$validRights) + { + $response->getBody()->write(json_encode([ + 'message' => 'You do not have enough rights.', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(422); + } + + $url = $request->getQueryParams()['url'] ?? false; + + $navigation = new Navigation(); + $urlinfo = $this->c->get('urlinfo'); + $item = $this->getItem($navigation, $url, $urlinfo); + if(!$item) + { + $response->getBody()->write(json_encode([ + 'message' => 'page not found', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(404); + } + + $meta = new Meta(); + + $metadata = $meta->getMetaData($item); + + if(!$metadata) + { + die('no page meta'); +# $pagemeta = $writeMeta->getPageMetaBlank($this->content, $this->settings, $this->item); + } + + # if item is a folder + if($item->elementType == "folder" && isset($item->contains)) + { + $metadata['meta']['contains'] = isset($pagemeta['meta']['contains']) ? $pagemeta['meta']['contains'] : $item->contains; + + # get global metadefinitions + $metadefinitions = $meta->getMetaDefinitions($this->settings, $folder = true); + } + else + { + # get global metadefinitions + $metadefinitions = $meta->getMetaDefinitions($this->settings, $folder = false); + } + + # cleanup metadata to the current metadefinitions (e.g. strip out deactivated plugins) + $metacleared = []; + + # store the metadata-scheme for frontend, so frontend does not use obsolete data + $metascheme = []; + + foreach($metadefinitions as $tabname => $tabfields ) + { + # add userroles and other datasets + $metadefinitions[$tabname]['fields'] = $this->addDatasets($tabfields['fields']); + + $tabfields = $this->flattenTabFields($tabfields['fields'],[]); + + $metacleared[$tabname] = []; + + foreach($tabfields as $fieldname => $fielddefinitions) + { + $metascheme[$tabname][$fieldname] = true; + + $metacleared[$tabname][$fieldname] = isset($metadata[$tabname][$fieldname]) ? $metadata[$tabname][$fieldname] : null; + } + } + + # store the metascheme in cache for frontend +# $writeMeta->updateYaml('cache', 'metatabs.yaml', $metascheme); + + $response->getBody()->write(json_encode([ + 'metadata' => $metacleared, + 'metadefinitions' => $metadefinitions, + ])); + + return $response->withHeader('Content-Type', 'application/json'); + } + + public function updateMetaData(Request $request, Response $response, $args) + { + $validRights = $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update'); + if(!$validRights) + { + $response->getBody()->write(json_encode([ + 'message' => 'You do not have enough rights.', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(422); + } + + $params = $request->getParsedBody(); + $validate = new Validation(); + $validInput = $validate->metaInput($params); + if($validInput !== true) + { + $errors = $validate->returnFirstValidationErrors($validInput); + $response->getBody()->write(json_encode([ + 'message' => reset($errors), + 'errors' => $errors + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(400); + } + + $navigation = new Navigation(); + $urlinfo = $this->c->get('urlinfo'); + $item = $this->getItem($navigation, $params['url'], $urlinfo); + if(!$item) + { + $response->getBody()->write(json_encode([ + 'message' => 'page not found', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(404); + } + + $meta = new Meta(); + + # if item is a folder + if($item->elementType == "folder" && isset($item->contains)) + { + $metadata['meta']['contains'] = isset($pagemeta['meta']['contains']) ? $pagemeta['meta']['contains'] : $item->contains; + + # get global metadefinitions + $metadefinitions = $meta->getMetaDefinitions($this->settings, $folder = true); + } + else + { + # get global metadefinitions + $metadefinitions = $meta->getMetaDefinitions($this->settings, $folder = false); + } + + $tabdefinitions = $metadefinitions[$params['tab']] ?? false; + if(!$tabdefinitions) + { + $response->getBody()->write(json_encode([ + 'message' => 'Tab not found', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(404); + } + $tabdefinitions['fields'] = $this->addDatasets($tabdefinitions['fields']); + $tabdefinitions = $this->flattenTabFields($tabdefinitions['fields'], []); + + # create validation object + $errors = false; + + # take the user input data and iterate over all fields and values + foreach($params['data'] as $fieldname => $fieldvalue) + { + # get the corresponding field definition from original plugin settings + $fielddefinition = $tabdefinitions[$fieldname] ?? false; + + if(!$fielddefinition) + { + $errors[$tab][$fieldname] = 'This field is not defined'; + } + else + { + # validate user input for this field + $result = $validate->field($fieldname, $fieldvalue, $fielddefinition); + + if($result !== true) + { + $errors[$params['tab']][$fieldname] = $result[$fieldname][0]; + } + } + } + + # return validation errors + if($errors) + { + $response->getBody()->write(json_encode([ + 'message' => 'Please correct the errors.', + 'errors' => $errors + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(422); + } + + $pageMeta = $meta->getMetaData($item); + + # extended + $navigation = new Navigation(); + + $extended = $navigation->getExtendedNavigation($urlinfo, $this->settings['langattr']); + + if($params['tab'] == 'meta') + { + # if manual date has been modified + if( $this->hasChanged($params['data'], $pageMeta['meta'], 'manualdate')) + { + # update the time + $params['data']['time'] = date('H-i-s', time()); + + # if it is a post, then rename the post + if($item->elementType == "file" && strlen($item->order) == 12) + { + # create file-prefix with date + $metadate = $params['data']['manualdate']; + if($metadate == '') + { + $metadate = $pageMeta['meta']['created']; + } + $datetime = $metadate . '-' . $params['data']['time']; + $datetime = implode(explode('-', $datetime)); + $datetime = substr($datetime,0,12); + + # create the new filename + $pathWithoutFile = str_replace($item->originalName, "", $item->path); + $newPathWithoutType = $pathWithoutFile . $datetime . '-' . $item->slug; + + $meta->renamePost($item->pathWithoutType, $newPathWithoutType); + + $navigation->clearNavigation(); + } + } + + # 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($meta->folderContainsFolders($item)) + { + $response->getBody()->write(json_encode([ + 'message' => 'The folder contains another folder so we cannot transform it. Please make sure there are only files in this folder.', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(422); + } + + if($params['data']['contains'] == "posts" && !$meta->transformPagesToPosts($item)) + { + $response->getBody()->write(json_encode([ + 'message' => 'One or more files could not be transformed.', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(422); + } + + if($params['data']['contains'] == "pages" && !$meta->transformPostsToPages($item)) + { + $response->getBody()->write(json_encode([ + 'message' => 'One or more files could not be transformed.', + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(422); + } + + $navigation->clearNavigation(); + } + + # 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; + + # input values are empty but entry in structure exists + if( + !$params['data']['hide'] + && $params['data']['navtitle'] == "" + && isset($extended[$item->urlRelWoF]) + ) + { + $navigation->clearNavigation(); + } + elseif( + # check if navtitle or hide-value has been changed + ($this->hasChanged($params['data'], $pageMeta['meta'], 'navtitle')) + OR + ($this->hasChanged($params['data'], $pageMeta['meta'], 'hide')) + OR + ($this->hasChanged($params['data'], $pageMeta['meta'], 'noindex')) + ) + { + $navigation->clearNavigation(); + } + } + + # add the new/edited metadata + $pageMeta[$params['tab']] = $params['data']; + + # store the metadata + $store = $meta->updateMeta($pageMeta, $item); + + if($store === true) + { + $draftNavigation = $navigation->getDraftNavigation($urlinfo, $this->settings['langattr']); + $draftNavigation = $navigation->setActiveNaviItems($draftNavigation, $item->keyPathArray); + $item = $navigation->getItemWithKeyPath($draftNavigation, $item->keyPathArray); + + $response->getBody()->write(json_encode([ + 'navigation' => $draftNavigation, + 'item' => $item + ])); + + return $response->withHeader('Content-Type', 'application/json'); + } + + $response->getBody()->write(json_encode([ + 'message' => $store, + ])); + + return $response->withHeader('Content-Type', 'application/json')->withStatus(422); + } + + + + + + + + + + + + + + + + + + + + +/* + # get the standard meta-definitions and the meta-definitions from plugins (same for all sites) + public function aggregateMetaDefinitions($folder = null) + { + $metatabs = $this->meta->getMetaDefinitions(); + + # the fields for user or role based access + if(!isset($this->settings['pageaccess']) || $this->settings['pageaccess'] === NULL ) + { + unset($metatabs['meta']['fields']['fieldsetrights']); + } + + # add radio buttons to choose posts or pages for folder. + if(!$folder) + { + unset($metatabs['meta']['fields']['contains']); + } + + echo '
';
+		print_r($metatabs);
+		die();
+
+		# loop through all plugins
+		if(!empty($this->settings['plugins']))
+		{
+			foreach($this->settings['plugins'] as $name => $plugin)
+			{
+				if($plugin['active'])
+				{
+					$pluginSettings = \Typemill\Settings::getObjectSettings('plugins', $name);
+					if($pluginSettings && isset($pluginSettings['metatabs']))
+					{
+						$metatabs = array_merge_recursive($metatabs, $pluginSettings['metatabs']);
+					}
+				}
+			}
+		}
+		
+		# add the meta from theme settings here
+		$themeSettings = \Typemill\Settings::getObjectSettings('themes', $this->settings['theme']);
+		
+		if($themeSettings && isset($themeSettings['metatabs']))
+		{
+			$metatabs = array_merge_recursive($metatabs, $themeSettings['metatabs']);
+		}
+
+		# dispatch meta 
+#		$metatabs 		= $this->c->dispatcher->dispatch('onMetaDefinitionsLoaded', new OnMetaDefinitionsLoaded($metatabs))->getData();
+
+		return $metatabs;
+	}
+*/
+
+
+	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' 	=> 'You do not have enough rights.',
+			]));
+
+			return $response->withHeader('Content-Type', 'application/json')->withStatus(422);			
+		}
+
+		$params 			= $request->getParsedBody();
+		$validate			= new Validation();
+		$validInput 		= $validate->articlePublish($params);
+		if($validInput !== true)
+		{
+			$errors 		= $validate->returnFirstValidationErrors($validInput);
+			$response->getBody()->write(json_encode([
+				'message' 	=> reset($errors),
+				'errors' 	=> $errors
+			]));
+
+			return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+		}
+
+		$navigation 		= new Navigation();
+		$urlinfo 			= $this->c->get('urlinfo');
+		$item 				= $this->getItem($navigation, $params['url'], $urlinfo);
+		if(!$item)
+		{
+			$response->getBody()->write(json_encode([
+				'message' => 'page not found',
+			]));
+
+			return $response->withHeader('Content-Type', 'application/json')->withStatus(404);
+		}
+
+	    # publish content
+		$content 			= new Content($urlinfo['baseurl']);
+		$draftMarkdown  	= $content->getDraftMarkdown($item);
+		$content->publishMarkdown($item, $draftMarkdown);
+
+		# refresh navigation and item
+	    $navigation->clearNavigation();
+		$draftNavigation 	= $navigation->getDraftNavigation($urlinfo, $this->settings['langattr']);
+		$draftNavigation 	= $navigation->setActiveNaviItems($draftNavigation, $item->keyPathArray);
+		$item 				= $navigation->getItemWithKeyPath($draftNavigation, $item->keyPathArray);
+
+		$response->getBody()->write(json_encode([
+			'navigation'	=> $draftNavigation,
+			'item'			=> $item
+		]));
+
+		return $response->withHeader('Content-Type', 'application/json');
+	}
+
+	# get the standard meta-definitions and the meta-definitions from plugins (same for all sites)
+	public function getMetaDefinitions(Request $request, Response $response, $args)
+	{
+		$validRights		= $this->validateRights($request->getAttribute('c_userrole'), 'content', 'update');
+		if(!$validRights)
+		{
+			$response->getBody()->write(json_encode([
+				'message' 	=> 'You do not have enough rights.',
+			]));
+
+			return $response->withHeader('Content-Type', 'application/json')->withStatus(422);			
+		}
+
+		$metatabs = $this->aggregateMetaDefinitions();
+
+		$response->getBody()->write(json_encode([
+			'definitions'	=> $metatabs
+		]));
+
+		return $response->withHeader('Content-Type', 'application/json');
+	}
+
+
+
+
+	# we have to flatten field definitions for tabs if there are fieldsets in it
+	public function flattenTabFields($tabfields, $flattab, $fieldset = null)
+	{
+		foreach($tabfields as $name => $field)
+		{
+			if($field['type'] == 'fieldset')
+			{
+				$flattab = $this->flattenTabFields($field['fields'], $flattab, $name);
+			}
+			else
+			{
+				# add the name of the fieldset so we know to which fieldset it belongs for references
+				if($fieldset)
+				{
+					$field['fieldset'] = $fieldset;
+				}
+				$flattab[$name] = $field;
+			}
+		}
+		return $flattab;
+	}
+
+	protected function hasChanged($input, $page, $field)
+	{
+		if(isset($input[$field]) && isset($page[$field]) && $input[$field] == $page[$field])
+		{
+			return false;
+		}
+		if(!isset($input[$field]) && !isset($input[$field]))
+		{
+			return false;
+		}
+		return true;
+	}
+}
\ No newline at end of file
diff --git a/system/typemill/Controllers/ControllerApiFile.php b/system/typemill/Controllers/ControllerApiFile.php
index 2afc5af..08ce8dc 100644
--- a/system/typemill/Controllers/ControllerApiFile.php
+++ b/system/typemill/Controllers/ControllerApiFile.php
@@ -11,7 +11,7 @@ class ControllerApiFile extends Controller
 {
 	public function getFileRestrictions(Request $request, Response $response, $args)
 	{
-		$params = $request->getParsedBody();
+		$params = $request->getQueryParams();
 
 		$restriction 	= 'all';
 
@@ -70,6 +70,9 @@ class ControllerApiFile extends Controller
 			$restrictions = [];
 		}
 
+		# make sure you always add live path to the restriction registry, not temporary path
+		$filename = str_replace('media/tmp', 'media/files', $filename);
+
 		if($role == 'all')
 		{
 			unset($restrictions[$filename]);
@@ -88,7 +91,6 @@ class ControllerApiFile extends Controller
 		return $response->withHeader('Content-Type', 'application/json');
 	}
 
-
 	public function uploadFile(Request $request, Response $response, $args)
 	{
 		$params 	= $request->getParsedBody();
@@ -154,13 +156,13 @@ class ControllerApiFile extends Controller
 		$fileProcessor	= new ProcessFile();
 
 		$fileinfo = $fileProcessor->storeFile($params['file'], $params['name']);
+		$filePath = str_replace('media/files', 'media/tmp', $fileinfo['url']);
 
 		if($fileinfo)
 		{
 			# if the previous check of the mtype with the base64 string failed, then do it now again with the temporary file
 			if(!$mtype)
 			{
-				$filePath 	= str_replace('media/files', 'media/tmp', $fileinfo['url']);
 				$fullPath 	= $this->settings['rootPath'] . $filePath;
 				$finfo 		= finfo_open( FILEINFO_MIME_TYPE );
 				$mtype 		= @finfo_file( $finfo, $fullPath );
@@ -188,7 +190,8 @@ class ControllerApiFile extends Controller
 
 			$response->getBody()->write(json_encode([
 				'message' => 'File has been stored',
-				'fileinfo' => $fileinfo
+				'fileinfo' => $fileinfo,
+				'filepath' => $filePath
 			]));
 
 			return $response->withHeader('Content-Type', 'application/json');
@@ -201,7 +204,39 @@ class ControllerApiFile extends Controller
 		return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
 	}
 
+	public function publishFile(Request $request, Response $response, $args)
+	{
+		$params = $request->getParsedBody();
 
+		if(!isset($params['file']) OR !$params['file'])
+		{
+			$response->getBody()->write(json_encode([
+				'message' 		=> 'filename is missing.',
+			]));
+
+			return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+		}
+
+		$storage 	= new StorageWrapper('\Typemill\Models\Storage');
+
+		$result 	= $storage->publishFile($params['file']);
+
+		if(!$result)
+		{
+			$response->getBody()->write(json_encode([
+				'message' 		=> $storage->getError()
+			]));
+
+			return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+		}
+
+		$response->getBody()->write(json_encode([
+			'message' => 'File saved successfully',
+			'path' => $result,
+		]));
+
+		return $response->withHeader('Content-Type', 'application/json');		
+	}
 
 
 
@@ -266,31 +301,6 @@ class ControllerApiFile extends Controller
 
 
 
-	public function publishFile(Request $request, Response $response, $args)
-	{
-		$params 		= $request->getParsedBody();
-
-		$fileProcessor	= new ProcessFile();
-		if(!$fileProcessor->checkFolders())
-		{
-			return $response->withJson(['errors' => 'Please check if your media-folder exists and all folders inside are writable.'], 500);
-		}
-		
-		if($fileProcessor->publishFile())
-		{
-			$request 	= $request->withParsedBody($params);
-		
-			$block = new ControllerAuthorBlockApi($this->c);
-			if($params['new'])
-			{
-				return $block->addBlock($request, $response, $args);
-			}
-			return $block->updateBlock($request, $response, $args);
-		}
-
-		return $response->withJson(['errors' => 'could not store file to media folder'],500);	
-	}
-
 	public function deleteFile(Request $request, Response $response, $args)
 	{
 		# get params from call 
diff --git a/system/typemill/Controllers/ControllerApiImage.php b/system/typemill/Controllers/ControllerApiImage.php
index f11248d..db0b12b 100644
--- a/system/typemill/Controllers/ControllerApiImage.php
+++ b/system/typemill/Controllers/ControllerApiImage.php
@@ -20,8 +20,6 @@ class ControllerApiImage extends Controller
 	# solution for logo
 	# return error messages and display in image component
 	# check if resized is bigger than original, then use original
-	# use original size checkbox
-
 
 	public function saveImage(Request $request, Response $response, $args)
 	{
@@ -133,6 +131,7 @@ class ControllerApiImage extends Controller
 	public function publishImage(Request $request, Response $response, $args)
 	{
 		$params = $request->getParsedBody();
+		$noresize = (isset($params['noresize']) && $params['noresize'] == true) ? true : false;
 
 		if(!isset($params['imgfile']) OR !$params['imgfile'])
 		{
@@ -145,7 +144,7 @@ class ControllerApiImage extends Controller
 
 		$storage 	= new StorageWrapper('\Typemill\Models\Storage');
 
-		$result 	= $storage->publishImage($params['imgfile']);
+		$result 	= $storage->publishImage($params['imgfile'], $noresize);
 
 		if(!$result)
 		{
diff --git a/system/typemill/Controllers/ControllerWebAuth.php b/system/typemill/Controllers/ControllerWebAuth.php
index 2e639de..481c38f 100644
--- a/system/typemill/Controllers/ControllerWebAuth.php
+++ b/system/typemill/Controllers/ControllerWebAuth.php
@@ -90,30 +90,8 @@ return $response->withHeader('Location', $this->routeParser->urlFor('settings.sh
 	
 	public function logout(Request $request, Response $response)
 	{
-		# check https://www.php.net/session_destroy
-		if(isset($_SESSION))
-		{
-			# Unset all of the session variables.
-			$_SESSION = array();
+		\Typemill\Static\Session::stopSession();
 
-			# If it's desired to kill the session, also delete the session cookie. This will destroy the session, and not just the session data!
-			if (ini_get("session.use_cookies"))
-			{
-				$params = session_get_cookie_params();
-			
-				setcookie(
-					session_name(), 
-					'', 
-					time() - 42000,
-					$params["path"], $params["domain"],
-					$params["secure"], $params["httponly"]
-				);
-			}
-
-			# Finally, destroy the session.
-			session_destroy();
-		}
-		
 		return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
 	}
 }
\ No newline at end of file
diff --git a/system/typemill/Controllers/ControllerWebAuthor.php b/system/typemill/Controllers/ControllerWebAuthor.php
index c0ae408..7ee0ee8 100644
--- a/system/typemill/Controllers/ControllerWebAuthor.php
+++ b/system/typemill/Controllers/ControllerWebAuthor.php
@@ -7,11 +7,6 @@ use Psr\Http\Message\ResponseInterface as Response;
 use Slim\Routing\RouteContext;
 use Typemill\Models\Navigation;
 use Typemill\Models\Content;
-# use Typemill\Models\Folder;
-# use Typemill\Extensions\ParsedownExtension;
-# use Typemill\Models\StorageWrapper;
-# use Typemill\Models\User;
-# use Typemill\Models\License;
 
 class ControllerWebAuthor extends Controller
 {
@@ -27,10 +22,76 @@ class ControllerWebAuthor extends Controller
 		$draftNavigation 	= $navigation->getDraftNavigation($urlinfo, $langattr);
 	    $home 				= $navigation->getHomepageItem($urlinfo['baseurl']);
 
+		if($url == '/')
+		{
+			$item 				= $home;
+			$item->active 		= true;
+		}
+		else
+		{
+		    $extendedNavigation	= $navigation->getExtendedNavigation($urlinfo, $langattr);
+
+		    $pageinfo 			= $extendedNavigation[$url] ?? false;
+		    if(!$pageinfo)
+		    {
+			    return $this->c->get('view')->render($response->withStatus(404), '404.twig', [
+					'title'			=> 'Typemill Author Area',
+					'description'	=> 'Typemill Version 2 wird noch besser als Version 1.'
+			    ]);
+		    }
+
+			$keyPathArray 		= explode(".", $pageinfo['keyPath']);
+
+		    # extend : $request->getAttribute('c_userrole')
+#		    $draftNavigation 	= $navigation->getDraftNavigation($urlinfo, $langattr);
+
+			$draftNavigation 	= $navigation->setActiveNaviItems($draftNavigation, $keyPathArray);
+
+			$item 				= $navigation->getItemWithKeyPath($draftNavigation, $keyPathArray);
+		}
+
+	#	$item->modified		= ($item->published OR $item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
+
+		$mainNavigation 	= $navigation->getMainNavigation($request->getAttribute('c_userrole'), $this->c->get('acl'), $urlinfo, $this->settings['editor']);
+
+		$content 			= new Content($urlinfo['baseurl']);
+
+		$draftMarkdown  	= $content->getDraftMarkdown($item);
+
+		$draftMarkdownHtml	= $content->addDraftHtml($draftMarkdown);
+
+	    return $this->c->get('view')->render($response, 'content/blox-editor.twig', [
+			'settings' 			=> $this->settings,
+			'mainnavi'			=> $mainNavigation,
+			'content' 			=> $draftMarkdownHtml,
+			'jsdata' 			=> [
+										'settings' 		=> $this->settings,
+										'urlinfo'		=> $urlinfo,
+										'labels'		=> $this->c->get('translations'),
+										'navigation'	=> $draftNavigation,
+										'item'			=> $item,
+										'home' 			=> $home,
+										'content' 		=> $draftMarkdownHtml
+									]
+		]);
+	}
+
+	public function showRaw(Request $request, Response $response, $args)
+	{
+		# get url for requested page
+		$url 				= isset($args['route']) ? '/' . $args['route'] : '/';
+		$urlinfo 			= $this->c->get('urlinfo');
+		$fullUrl  			= $urlinfo['baseurl'] . $url;
+		$langattr 			= $this->settings['langattr'];
+
+	    $navigation 		= new Navigation();
+		$draftNavigation 	= $navigation->getDraftNavigation($urlinfo, $langattr);
+	    $home 				= $navigation->getHomepageItem($urlinfo['baseurl']);
 
 		if($url == '/')
 		{
 			$item 				= $home;
+			$item->active 		= true;
 		}
 		else
 		{
@@ -65,100 +126,25 @@ class ControllerWebAuthor extends Controller
 
 		$draftMarkdownHtml	= $content->addDraftHtml($draftMarkdown);
 
-/*
-		if(isset($draftHtml[0]))
-		{
-			$title = $draftHtml[0];
-			unset($draftHtml[0]);
-		}
-
-		echo '
';
-		print_r($draftHtml);
-		die();
-*/
-	    return $this->c->get('view')->render($response, 'content/blox-editor.twig', [
+	    return $this->c->get('view')->render($response, 'content/raw-editor.twig', [
 			'settings' 			=> $this->settings,
 			'mainnavi'			=> $mainNavigation,
 			'content' 			=> $draftMarkdownHtml,
 			'jsdata' 			=> [
 										'settings' 		=> $this->settings,
 										'urlinfo'		=> $urlinfo,
+										'labels'		=> $this->c->get('translations'),
 										'navigation'	=> $draftNavigation,
 										'item'			=> $item,
 										'home' 			=> $home,
-										'content' 		=> $draftMarkdownHtml
+										'content' 		=> $draftMarkdownHtml,
 									]
 		]);
 
 
 
 
-		# set information for homepage
-		$this->setHomepage($args);
-		# we have to check ownership here to use it for permission-check in templates
-		$this->checkContentOwnership();
-		# set the status for published and drafted
-		$this->setPublishStatus();		
-		# set path
-		$this->setItemPath($this->item->fileType);
 
-		# read content from file
-		if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
-
-		$content = $this->content;
-
-		if($content == '')
-		{
-			$content = [];
-		}
-		
-		# initialize parsedown extension
-		$parsedown = new ParsedownExtension($this->uri->getBaseUrl());
-
-		# to fix footnote-logic in parsedown, set visual mode to true
-		$parsedown->setVisualMode();
-
-		# if content is not an array, then transform it
-		if(!is_array($content))
-		{
-			# turn markdown into an array of markdown-blocks
-			$content = $parsedown->markdownToArrayBlocks($content);
-		}
-		
-		# needed for ToC links
-		$relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
-		
-		foreach($content as $key => $block)
-		{
-			/* parse markdown-file to content-array */
-			$contentArray 	= $parsedown->text($block);
-
-			/* parse markdown-content-array to content-string */
-			$content[$key]	= $parsedown->markup($contentArray);
-		}
-
-		# extract title and delete from content array, array will start at 1 after that.
-		$title = '# add title';
-		if(isset($content[0]))
-		{
-			$title = $content[0];
-			unset($content[0]);			
-		}
-
-		return $this->renderIntern($response, 'editor/editor-blox.twig', array(
-			'acl'			=> $this->c->acl, 
-			'mycontent'		=> $this->mycontent,
-			'navigation' 	=> $this->structureDraft,
-			'homepage' 		=> $this->homepage,
-			'title' 		=> $title, 
-			'content' 		=> $content, 
-			'item' 			=> $this->item,
-			'settings' 		=> $this->settings 
-		));
-	}
-
-	public function showContent(Request $request, Response $response, $args)
-	{
 		# get params from call
 #		$this->uri 		= $request->getUri()->withUserInfo('');
 #		$this->params	= isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
diff --git a/system/typemill/Controllers/ControllerWebDownload.php b/system/typemill/Controllers/ControllerWebDownload.php
new file mode 100644
index 0000000..ee2a5a3
--- /dev/null
+++ b/system/typemill/Controllers/ControllerWebDownload.php
@@ -0,0 +1,149 @@
+getYaml('fileFolder', '', 'filerestrictions.yaml');
+
+		$filepath 		= $storage->getFolderPath('fileFolder');
+		$filefolder 	= 'media/files/';
+
+		# validate
+		$allowedFiletypes = [];
+		if(!$this->validate($filepath, $filename, $allowedFiletypes))
+		{
+			die('the requested filetype is not allowed.');
+		}
+
+		if($restrictions && isset($restrictions[$filefolder . $filename]))
+		{
+			$userrole 			= $request->getAttribute('c_userrole');
+			$allowedrole 		= $restrictions[$filefolder . $filename];
+
+			if(!$userrole)
+			{
+				$this->c->get('flash')->addMessage('error', "You have to be an authenticated $allowedrole to download this file.");
+				
+				return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
+			}
+			elseif(
+				$userrole != 'administrator'
+				AND $userrole != $allowedrole 
+				AND !$this->c->get('acl')->inheritsRole($userrole, $allowedrole)
+			)
+			{
+				$this->c->get('flash')->addMessage('error', "You have to be a $allowedrole to download this file.");
+
+				return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
+			}
+		}
+
+		$file = $filepath . $filename;
+
+		# for now we only allow one download
+		$this->sendDownload($file);
+		exit;
+	}
+	
+	/**
+	 * Validate if the file exists and if
+	 * there is a permission (download dir) to download this file
+	 *
+	 * You should ALWAYS call this method if you don't want
+	 * somebody to download files not intended to be for the public.
+	 *
+	 * @param string $file GET parameter
+	 * @param array $allowedFiletypes (defined in the head of this file)
+	 * @return bool true if validation was successfull
+	 */
+	private function validate($path, $filename, $allowedFiletypes) 
+	{
+		$filepath = $path . $filename;
+
+		# check if file exists
+		if (!isset($filepath)  || empty($filepath)  || !file_exists($filepath) )
+		{
+			return false;
+		}
+
+		# check allowed filetypes
+		if(!empty($allowedFiletypes))
+		{
+			$fileAllowed = false;
+			foreach ($allowedFiletypes as $filetype) 
+			{
+				if (strpos($filename, $filetype) === (strlen($filename) - strlen($filetype))) 
+				{
+					$fileAllowed = true; //ends with $filetype
+				}
+			}
+			
+			if (!$fileAllowed) return false;
+		}
+
+		# check download directory
+		if (strpos($filename, '..') !== false)
+		{
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Download function.
+	 * Sets the HTTP header and supplies the given file
+	 * as a download to the browser.
+	 *
+	 * @param string $file path to file
+	 */
+	private function sendDownload($file) 
+	{
+		# Parse information
+		$pathinfo 	= pathinfo($file);
+		$extension 	= strtolower($pathinfo['extension']);
+		$mimetype 	= null;
+		
+		# Get mimetype for extension
+		# This list can be extended as you need it.
+		# A good start to find mimetypes is the apache mime.types list
+		# http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
+		switch ($extension) {
+			case 'zip':     $mimetype = "application/zip"; break;
+			default:        $mimetype = "application/force-download";
+		}
+		
+		# Required for some browsers like Safari and IE
+		if (ini_get('zlib.output_compression'))
+		{
+			ini_set('zlib.output_compression', 'Off');
+		}
+
+		header('Pragma: public');
+		header('Content-Encoding: none');
+		header('Accept-Ranges: bytes');  # Allow support for download resume
+		header('Expires: 0');
+		header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT');
+		header_remove("Last-Modified");
+		header('Cache-Control: max-age=0, no-cache, no-store, must-revalidate');
+		header('Cache-Control: private', false); # required for some browsers
+		header('Content-Type: ' . $mimetype);
+		header('Content-Disposition: attachment; filename="'.basename($file).'";'); # Make the browser display the Save As dialog
+		header('Content-Transfer-Encoding: binary');
+		header('Content-Length: '.filesize($file));
+		ob_end_flush();
+		readfile($file); # This is necessary in order to get it to actually download the file, otherwise it will be 0Kb
+	}
+}
\ No newline at end of file
diff --git a/system/typemill/Controllers/ControllerWebFrontend.php b/system/typemill/Controllers/ControllerWebFrontend.php
index fd0370a..e372e81 100644
--- a/system/typemill/Controllers/ControllerWebFrontend.php
+++ b/system/typemill/Controllers/ControllerWebFrontend.php
@@ -5,11 +5,9 @@ namespace Typemill\Controllers;
 use Psr\Http\Message\ServerRequestInterface as Request;
 use Psr\Http\Message\ResponseInterface as Response;
 use Slim\Routing\RouteContext;
-
-/*
-use Typemill\Models\Folder;
-use Typemill\Models\WriteMeta;
-use Typemill\Extensions\ParsedownExtension;
+use Typemill\Models\Navigation;
+use Typemill\Models\Content;
+use Typemill\Models\Meta;
 use Typemill\Events\OnPagetreeLoaded;
 use Typemill\Events\OnBreadcrumbLoaded;
 use Typemill\Events\OnItemLoaded;
@@ -19,17 +17,368 @@ use Typemill\Events\OnMarkdownLoaded;
 use Typemill\Events\OnContentArrayLoaded;
 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)
+	public function index(Request $request, Response $response, $args)
 	{
-	    return $this->c->get('view')->render($response, 'home.twig', [
-			'title'			=> 'Typemill Version 2',
-			'description'	=> 'Typemill Version 2 wird noch besser als Version 1.'
-	    ]);
+		$url 				= isset($args['route']) ? '/' . $args['route'] : '/';
+		$urlinfo 			= $this->c->get('urlinfo');
+		$langattr 			= $this->settings['langattr'];
+		$userrole 			= $request->getAttribute('c_userrole');
+		$username 			= $request->getAttribute('c_username');
 
+
+		# GET THE NAVIGATION
+	    $navigation 		= new Navigation();
+		$liveNavigation 	= $navigation->getLiveNavigation($urlinfo, $langattr);
+	    $home 				= false;
+
+
+		# GET THE PAGINATION
+		$currentpage 		= $navigation->getCurrentPage($args);
+		if($currentpage)
+		{
+			$url 			= str_replace("/p/" . $currentpage, "", $url);
+		}
+		$fullUrl  			= $urlinfo['baseurl'] . $url;
+
+
+	    # FIND THE PAGE/ITEM IN NAVIGATION
+		if($url == '/')
+		{
+			$item 				= $navigation->getHomepageItem($urlinfo['baseurl']);
+			$item->active 		= true;
+		}
+		else
+		{
+		    $extendedNavigation	= $navigation->getExtendedNavigation($urlinfo, $langattr);
+
+		    $pageinfo 			= $extendedNavigation[$url] ?? false;
+		    if(!$pageinfo)
+		    {
+			    return $this->c->get('view')->render($response->withStatus(404), '404.twig', [
+					'title'			=> 'Typemill Author Area',
+					'description'	=> 'Typemill Version 2 wird noch besser als Version 1.'
+			    ]);
+		    }
+
+			$keyPathArray 		= explode(".", $pageinfo['keyPath']);
+
+			$liveNavigation 	= $navigation->setActiveNaviItems($liveNavigation, $keyPathArray);
+
+			$item 				= $navigation->getItemWithKeyPath($liveNavigation, $keyPathArray);
+		}
+
+		$liveNavigation = $this->c->get('dispatcher')->dispatch(new OnPagetreeLoaded($liveNavigation), 'onPagetreeLoaded')->getData();
+
+
+		# CREATE THE BREADCRUMB
+		$breadcrumb = $navigation->getBreadcrumb($liveNavigation, $item->keyPathArray);
+		$breadcrumb = $this->c->get('dispatcher')->dispatch(new OnBreadcrumbLoaded($breadcrumb), 'onBreadcrumbLoaded')->getData();
+
+
+		# STRIP OUT HIDDEN PAGES
+		$liveNavigation = $navigation->removeHiddenPages($liveNavigation);
+		# we could cache navigation without hidden pages??
+
+
+		# ADD BACKWARD-/FORWARD PAGINATION
+		$item = $navigation->getPagingForItem($liveNavigation, $item);
+		$item = $this->c->get('dispatcher')->dispatch(new OnItemLoaded($item), 'onItemLoaded')->getData();
+
+
+		# GET THE CONTENT
+		$content 			= new Content($urlinfo['baseurl']);
+		$liveMarkdown		= $content->getLiveMarkdown($item);
+		$liveMarkdown 		= $this->c->get('dispatcher')->dispatch(new OnMarkdownLoaded($liveMarkdown), 'onMarkdownLoaded')->getData();
+		$markdownArray 		= $content->markdownTextToArray($liveMarkdown);
+
+
+		# GET THE META
+		$meta 				= new Meta();
+		$metadata  			= $meta->getMetaData($item);
+		$metadata 			= $meta->addMetaDefaults($metadata, $item, $this->settings['author']);
+		$metadata 			= $meta->addMetaTitleDescription($metadata, $item, $markdownArray);
+		$metadata 			= $this->c->get('dispatcher')->dispatch(new OnMetaLoaded($metadata),'onMetaLoaded')->getData();
+
+
+		# CHECK ACCESS RESTRICTIONS
+		$restricted 		= $this->checkRestrictions($metadata['meta'], $username, $userrole);
+		if($restricted)
+		{
+			# infos that plugins need to add restriction content
+			$restrictions = [
+				'restricted' 		=> $restricted,
+				'defaultContent' 	=> true,
+				'markdownBlocks'	=> $markdownArray,
+			];
+
+			# dispatch the data
+			$restrictions 	= $this->c->get('dispatcher')->dispatch(new OnRestrictionsLoaded( $restrictions ), 'onRestrictionsLoaded')->getData();
+
+			# use the returned markdown
+			$markdownArray = $restrictions['markdownBlocks'];
+
+			# if no plugin has disabled the default behavior
+			if($restrictions['defaultContent'])
+			{
+				# cut the restricted content
+				$shortenedPage = $this->cutRestrictedContent($markdownBlocks);
+
+				# check if there is customized content
+				$restrictionnotice = $this->prepareRestrictionNotice();
+
+				# add notice to shortened content
+				$shortenedPage[] = $restrictionnotice;
+
+				# Use the shortened page
+				$markdownArray = $shortenedPage;
+			}
+		}
+
+
+		# EXTRACT THE ARTICLE TITLE/HEADLINE
+		$title 				= trim(array_shift($markdownArray), "# ");
+
+
+		# TRANSFORM THE ARTICLE BODY TO HTML
+		$body 				= $content->markdownArrayToText($markdownArray);
+		$contentArray 		= $content->getContentArray($body);
+		$contentArray 		= $this->c->get('dispatcher')->dispatch(new OnContentArrayLoaded($contentArray), 'onContentArrayLoaded')->getData();
+		$contentHtml  		= $content->getContentHtml($contentArray);
+		$contentHtml 		= $this->c->get('dispatcher')->dispatch(new OnHtmlLoaded($contentHtml), 'onHtmlLoaded')->getData();
+
+
+		# ADD LOGO
+		$logo = false;
+		if(isset($this->settings['logo']) && $this->settings['logo'] != '' && $content->checkLogoFile($this->settings['logo']))
+		{
+			$logo = $this->settings['logo'];
+		}
+
+
+		# ADD ASSETS
+		$assets 			= $this->c->get('assets'); 
+
+
+		# ADD CUSTOM THEME CSS
+		$theme 				= $this->settings['theme'];
+		$customcss 			= $content->checkCustomCSS($theme);
+		if($customcss)
+		{
+			$assets->addCSS($urlinfo['baseurl'] . '/cache/' . $theme . '-custom.css');
+		}
+
+
+		# ADD FAVICON
+		$favicon = false;
+		if(isset($this->settings['favicon']) && $this->settings['favicon'] != '')
+		{
+			$favicon = true;
+			$assets->addMeta('tilecolor','');
+			$assets->addMeta('tileimage','');
+			$assets->addMeta('icon16','');
+			$assets->addMeta('icon32','');
+			$assets->addMeta('icon72','');
+			$assets->addMeta('icon114','');
+			$assets->addMeta('icon144','');
+			$assets->addMeta('icon180','');
+		}
+
+
+		# ADD META TAGS
+		if(isset($metadata['meta']['noindex']) && $metadata['meta']['noindex'])
+		{
+			$assets->addMeta('noindex','');
+		}
+		$assets->addMeta('og_site_name','');
+		$assets->addMeta('og_title','');
+		$assets->addMeta('og_description','');
+		$assets->addMeta('og_type','');
+		$assets->addMeta('og_url','');
+
+
+		# meta image
+		$metaImageUrl = $metadata['meta']['heroimage'] ?? false;
+		$metaImageAlt = $metadata['meta']['heroimagealt'] ?? false;
+		if(!$metaImageUrl OR $metaImageUrl == '')
+		{
+			# extract first image from content
+			$firstImageMD = $content->getFirstImage($contentArray);
+			if($firstImageMD)
+			{
+				preg_match('#\((.*?)\)#', $firstImageMD, $img_url_result);
+				$metaImageUrl = isset($img_url_result[1]) ? $img_url_result[1] : false;
+				if($metaImageUrl)
+				{
+					preg_match('#\[(.*?)\]#', $firstImageMD, $img_alt_result);
+					$metaImageAlt = isset($img_alt_result[1]) ? $img_alt_result[1] : false;
+				}
+			}
+			elseif($logo)
+			{
+				$metaImageUrl = $logo;
+				$pathinfo = pathinfo($this->settings['logo']);
+				$metaImageAlt = $pathinfo['filename'];
+			}
+		}
+		if($metaImageUrl)
+		{
+			$assets->addMeta('og_image','');
+			$assets->addMeta('twitter_image_alt','');
+			$assets->addMeta('twitter_card','');
+		}
+
+
+		$route = empty($args) && isset($this->settings['themes'][$theme]['cover']) ? 'cover.twig' : 'index.twig';
+
+	    return $this->c->get('view')->render($response, $route, [
+			'home'			=> false,
+			'navigation' 	=> $liveNavigation,
+			'title' 		=> $title,
+			'content' 		=> $contentHtml, 
+			'item' 			=> $item,
+			'breadcrumb' 	=> $breadcrumb, 
+			'settings' 		=> $this->settings,
+			'base_url' 		=> $urlinfo['baseurl'], 
+			'metatabs'		=> $metadata,
+			'logo'			=> $logo,
+			'favicon'		=> $favicon,
+			'currentpage'	=> $currentpage
+	    ]);
+	}
+
+
+	# checks if a page has a restriction in meta and if the current user is blocked by that restriction
+	public function checkRestrictions($meta, $username, $userrole)
+	{
+		# check if content restrictions are active
+		if(isset($this->settings['pageaccess']) && $this->settings['pageaccess'])
+		{
+
+			# check if page is restricted to certain user
+			if(isset($meta['alloweduser']) && $meta['alloweduser'] && $meta['alloweduser'] !== '' )
+			{
+				$alloweduser = array_map('trim', explode(",", $meta['alloweduser']));
+				if(isset($username) && in_array($username, $alloweduser))
+				{
+					# user has access to the page, so there are no restrictions
+					return false;
+				}
+
+				# otherwise return array with type of restriction and allowed username
+				return [ 'alloweduser' => $meta['alloweduser'] ];
+			}
+
+			# check if page is restricted to certain userrole
+			if(isset($meta['allowedrole']) && $meta['allowedrole'] && $meta['allowedrole'] !== '' )
+			{
+				if(
+					$userrole
+					AND ( 
+						$userrole == 'administrator' 
+						OR $userrole == $meta['allowedrole'] 
+						OR $this->c->get('acl')->inheritsRole($userrole, $meta['allowedrole']) 
+					)
+				)
+				{
+					# role has access to page, so there are no restrictions 
+					return false;
+				}
+				
+				return [ 'allowedrole' => $meta['allowedrole'] ];
+			}
+
+		}
+		
+		return false;
+
+	}
+
+	protected function cutRestrictedContent($markdown)
+	{
+		#initially add only the title of the page.
+		$restrictedMarkdown = [$markdown[0]];
+		unset($markdown[0]);
+
+		if(isset($this->settings['hrdelimiter']) && $this->settings['hrdelimiter'] !== NULL )
+		{
+			foreach ($markdown as $block)
+			{
+				$firstCharacters = substr($block, 0, 3);
+				if($firstCharacters == '---' OR $firstCharacters == '***')
+				{
+					return $restrictedMarkdown;
+				}
+				$restrictedMarkdown[] = $block;
+			}
+
+			# no delimiter found, so use the title only
+			$restrictedMarkdown = [$restrictedMarkdown[0]];
+		}
+
+		return $restrictedMarkdown;
+	}
+
+	protected function prepareRestrictionNotice()
+	{
+		if( isset($this->settings['restrictionnotice']) && $this->settings['restrictionnotice'] != '' )
+		{
+			$restrictionNotice = $this->settings['restrictionnotice'];
+		}
+		else
+		{
+			$restrictionNotice = 'You are not allowed to access this content.';
+		}
+
+		if( isset($this->settings['wraprestrictionnotice']) && $this->settings['wraprestrictionnotice'] )
+		{
+	        # standardize line breaks
+	        $text = str_replace(array("\r\n", "\r"), "\n", $restrictionNotice);
+
+	        # remove surrounding line breaks
+	        $text = trim($text, "\n");
+
+	        # split text into lines
+	        $lines = explode("\n", $text);
+
+	        $restrictionNotice = '';
+
+	        foreach($lines as $key => $line)
+	        {
+	        	$restrictionNotice .= "!!!! " . $line . "\n";
+	        }
+		}
+
+		return $restrictionNotice;
+	}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
 		# Initiate Variables
 		$contentHTML			= false;
 		$item					= false;
@@ -109,7 +458,8 @@ class ControllerWebFrontend extends Controller
 		}		
 
 		# the navigation is a copy of the structure without the hidden pages
-		# hint: if the navigation has been deleted from the cache, then we do not recreate it here to save performace. Instead you have to recreate cache in admin or change a page (publish/unpublish/delete/move)
+		# hint: if the navigation has been deleted from the cache, then we do not recreate it here to save performace. 
+		# Instead you have to recreate cache in admin or change a page (publish/unpublish/delete/move)
 		$navigation = $this->writeCache->getCache('cache', 'navigation.txt');
 		if(!$navigation)
 		{
@@ -189,6 +539,10 @@ class ControllerWebFrontend extends Controller
 			}
 		}
 
+
+
+
+###
 		if(isset($item->hide)) 
 		{
 			# if it is a hidden page
@@ -213,6 +567,9 @@ class ControllerWebFrontend extends Controller
 				$item = Folder::getPagingForItem($navigation, $item);
 			}
 		}
+###
+
+
 
 		# dispatch the item
 		$item 			= $this->c->dispatcher->dispatch('onItemLoaded', new OnItemLoaded($item))->getData();
@@ -258,12 +615,15 @@ class ControllerWebFrontend extends Controller
 
 		$itemUrl 		= isset($item->urlRel) ? $item->urlRel : false;
 
-		/* initialize parsedown */
+		/* initialize parsedown 
 		$parsedown 		= new ParsedownExtension($this->base_url, $this->settings, $this->c->dispatcher);
 		
-		/* set safe mode to escape javascript and html in markdown */
+		/* set safe mode to escape javascript and html in markdown
 		$parsedown->setSafeMode(true);
 
+
+
+####
 		# check access restriction here
 		$restricted 	= $this->checkRestrictions($metatabs['meta']);
 		if($restricted)
@@ -303,26 +663,29 @@ class ControllerWebFrontend extends Controller
 			# finally transform the markdown blocks back to pure markdown text
 			$contentMD = $parsedown->arrayBlocksToMarkdown($markdownBlocks);
 		}
+###
 
-		/* parse markdown-file to content-array */
+
+
+		/* parse markdown-file to content-array 
 		$contentArray 	= $parsedown->text($contentMD);
 		$contentArray 	= $this->c->dispatcher->dispatch('onContentArrayLoaded', new OnContentArrayLoaded($contentArray))->getData();
 
-		/* parse markdown-content-array to content-string */
+		/* parse markdown-content-array to content-string
 		$contentHTML	= $parsedown->markup($contentArray);
 		$contentHTML 	= $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData();
 		
-		/* extract the h1 headline*/
+		/* extract the h1 headline
 		$contentParts	= explode("", $contentHTML, 2);
 		$title			= isset($contentParts[0]) ? strip_tags($contentParts[0]) : $this->settings['title'];
 		
 		$contentHTML	= isset($contentParts[1]) ? $contentParts[1] : $contentHTML;
 
-		# get the first image from content array */
+		# get the first image from content array 
 		$img_url		= isset($metatabs['meta']['heroimage']) ? $metatabs['meta']['heroimage'] : false;
 		$img_alt		= isset($metatabs['meta']['heroimagealt']) ? $metatabs['meta']['heroimagealt'] : false;
 
-		# get url and alt-tag for first image, if exists */
+		# get url and alt-tag for first image, if exists 
 		if(!$img_url OR $img_url == '')
 		{
 			# extract first image from content
@@ -376,127 +739,6 @@ class ControllerWebFrontend extends Controller
 		]);
 	}
 
+	*/
 
-	protected function getFirstImage(array $contentBlocks)
-	{
-		foreach($contentBlocks as $block)
-		{
-			/* is it a paragraph? */
-			if(isset($block['name']) && $block['name'] == 'p')
-			{
-				if(isset($block['handler']['argument']) && substr($block['handler']['argument'], 0, 2) == '![' )
-				{
-					return $block['handler']['argument'];	
-				}
-			}
-		}
-		
-		return false;
-	}
-
-	# checks if a page has a restriction in meta and if the current user is blocked by that restriction
-	protected function checkRestrictions($meta)
-	{
-		# check if content restrictions are active
-		if(isset($this->settings['pageaccess']) && $this->settings['pageaccess'])
-		{
-
-			# check if page is restricted to certain user
-			if(isset($meta['alloweduser']) && $meta['alloweduser'] && $meta['alloweduser'] !== '' )
-			{
-				$alloweduser = array_map('trim', explode(",", $meta['alloweduser']));
-				if(isset($_SESSION['user']) && in_array($_SESSION['user'], $alloweduser))
-				{
-					# user has access to the page, so there are no restrictions
-					return false;
-				}
-
-				# otherwise return array with type of restriction and allowed username
-				return [ 'alloweduser' => $meta['alloweduser'] ];
-			}
-
-			# check if page is restricted to certain userrole
-			if(isset($meta['allowedrole']) && $meta['allowedrole'] && $meta['allowedrole'] !== '' )
-			{
-				# var_dump($this->c->acl->inheritsRole('editor', 'member'));
-				# die();
-				if(
-					isset($_SESSION['role']) 
-					AND ( 
-						$_SESSION['role'] == 'administrator' 
-						OR $_SESSION['role'] == $meta['allowedrole'] 
-						OR $this->c->acl->inheritsRole($_SESSION['role'], $meta['allowedrole']) 
-					)
-				)
-				{
-					# role has access to page, so there are no restrictions 
-					return false;
-				}
-				
-				return [ 'allowedrole' => $meta['allowedrole'] ];
-			}
-
-		}
-		
-		return false;
-
-	}
-
-	protected function cutRestrictedContent($markdown)
-	{
-		#initially add only the title of the page.
-		$restrictedMarkdown = [$markdown[0]];
-		unset($markdown[0]);
-
-		if(isset($this->settings['hrdelimiter']) && $this->settings['hrdelimiter'] !== NULL )
-		{
-			foreach ($markdown as $block)
-			{
-				$firstCharacters = substr($block, 0, 3);
-				if($firstCharacters == '---' OR $firstCharacters == '***')
-				{
-					return $restrictedMarkdown;
-				}
-				$restrictedMarkdown[] = $block;
-			}
-
-			# no delimiter found, so use the title only
-			$restrictedMarkdown = [$restrictedMarkdown[0]];
-		}
-
-		return $restrictedMarkdown;
-	}
-
-	protected function prepareRestrictionNotice()
-	{
-		if( isset($this->settings['restrictionnotice']) && $this->settings['restrictionnotice'] != '' )
-		{
-			$restrictionNotice = $this->settings['restrictionnotice'];
-		}
-		else
-		{
-			$restrictionNotice = 'You are not allowed to access this content.';
-		}
-
-		if( isset($this->settings['wraprestrictionnotice']) && $this->settings['wraprestrictionnotice'] )
-		{
-	        # standardize line breaks
-	        $text = str_replace(array("\r\n", "\r"), "\n", $restrictionNotice);
-
-	        # remove surrounding line breaks
-	        $text = trim($text, "\n");
-
-	        # split text into lines
-	        $lines = explode("\n", $text);
-
-	        $restrictionNotice = '';
-
-	        foreach($lines as $key => $line)
-	        {
-	        	$restrictionNotice .= "!!!! " . $line . "\n";
-	        }
-		}
-
-		return $restrictionNotice;
-	}
 }
\ No newline at end of file
diff --git a/system/typemill/Controllers/ControllerWebSystem.php b/system/typemill/Controllers/ControllerWebSystem.php
index 4bb9532..8f371ce 100644
--- a/system/typemill/Controllers/ControllerWebSystem.php
+++ b/system/typemill/Controllers/ControllerWebSystem.php
@@ -64,6 +64,16 @@ class ControllerWebSystem extends Controller
 
 		$extension 			= new Extension();
 		$themeDefinitions 	= $extension->getThemeDetails();
+
+		# add userroles and other datasets
+		foreach($themeDefinitions as $name => $definitions)
+		{
+			if(isset($definitions['forms']['fields']))
+			{
+				$themeDefinitions[$name]['forms']['fields'] = $this->addDatasets($definitions['forms']['fields']);
+			}
+		}
+
 		$themeSettings 		= $extension->getThemeSettings($this->settings['themes']);
 
 		$license = [];
@@ -105,6 +115,16 @@ class ControllerWebSystem extends Controller
 
 		$extension 			= new Extension();
 		$pluginDefinitions 	= $extension->getPluginDetails();
+
+		# add userroles and other datasets
+		foreach($pluginDefinitions as $name => $definitions)
+		{
+			if(isset($definitions['forms']['fields']))
+			{
+				$pluginDefinitions[$name]['forms']['fields'] = $this->addDatasets($definitions['forms']['fields']);
+			}
+		}
+
 		$pluginSettings 	= $extension->getPluginSettings($this->settings['plugins']);
 
 		$license = [];
diff --git a/system/typemill/Extensions/TwigLanguageExtension.php b/system/typemill/Extensions/TwigLanguageExtension.php
index 58f7ab3..c583a35 100644
--- a/system/typemill/Extensions/TwigLanguageExtension.php
+++ b/system/typemill/Extensions/TwigLanguageExtension.php
@@ -5,7 +5,6 @@ namespace Typemill\Extensions;
 use Twig\Extension\AbstractExtension;
 use Twig\TwigFilter;
 use Twig\TwigFunction;
-# use Typemill\Models\WriteYaml;
 
 class TwigLanguageExtension extends AbstractExtension
 {
diff --git a/system/typemill/Extensions/TwigMarkdownExtension.php b/system/typemill/Extensions/TwigMarkdownExtension.php
new file mode 100644
index 0000000..f025b66
--- /dev/null
+++ b/system/typemill/Extensions/TwigMarkdownExtension.php
@@ -0,0 +1,26 @@
+text($markdown);
+		
+		return $parsedown->markup($markdownArray);
+	}
+}
\ No newline at end of file
diff --git a/system/typemill/Middleware/ApiAuthentication.php b/system/typemill/Middleware/ApiAuthentication.php
index 30247f6..3e24b6e 100644
--- a/system/typemill/Middleware/ApiAuthentication.php
+++ b/system/typemill/Middleware/ApiAuthentication.php
@@ -7,6 +7,7 @@ use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
 use Slim\Routing\RouteContext;
 use Slim\Psr7\Response;
 use Typemill\Models\User;
+use Typemill\Static\Session;
 
 class ApiAuthentication
 {
@@ -18,30 +19,11 @@ class ApiAuthentication
 	    # check if it is a session based authentication
 		if ($request->hasHeader('X-Session-Auth'))
 		{
-			session_start();
-
-			$authenticated = ( 
-					(isset($_SESSION['username'])) && 
-					(isset($_SESSION['login'])) 
-				)
-				? true : false;
-
-			if($authenticated)
+			if($request->getAttribute('c_username') && $request->getAttribute('c_userrole'))
 			{
-				# here we have to load userdata and pass them through request or response
-				$user = new User();
+				$response = $handler->handle($request);
 
-				if($user->setUser($_SESSION['username']))
-				{
-					$userdata = $user->getUserData();
-
-					$request = $request->withAttribute('c_username', $userdata['username']);
-					$request = $request->withAttribute('c_userrole', $userdata['userrole']);
-
-					$response = $handler->handle($request);
-
-					return $response;
-				}
+				return $response;
 			}
 			else
 			{
diff --git a/system/typemill/Middleware/AssetMiddleware.php b/system/typemill/Middleware/AssetMiddleware.php
new file mode 100644
index 0000000..23f73e3
--- /dev/null
+++ b/system/typemill/Middleware/AssetMiddleware.php
@@ -0,0 +1,41 @@
+assets = $assets;
+
+        $this->view = $view;
+    }
+    
+	public function process(Request $request, RequestHandler $handler) :response
+    {
+    	# get url from request
+
+		# update the asset object in the container (for plugins) with the new url
+#		$this->container->assets->setBaseUrl($uri->getBaseUrl());
+
+        # add the asset object to twig-frontend for themes
+		$this->view->getEnvironment()->addGlobal('assets', $this->assets);
+
+        # use {{ base_url() }} in twig templates
+#		$this->container['view']['base_url']	 	= $uri->getBaseUrl();
+#		$this->container['view']['current_url'] 	= $uri->getPath();
+
+		$response = $handler->handle($request);
+	
+		return $response;
+    }
+}
\ No newline at end of file
diff --git a/system/typemill/Middleware/FlashMessages.php b/system/typemill/Middleware/FlashMessages.php
index d3f0439..53113de 100644
--- a/system/typemill/Middleware/FlashMessages.php
+++ b/system/typemill/Middleware/FlashMessages.php
@@ -4,20 +4,26 @@ namespace Typemill\Middleware;
 
 use Psr\Http\Message\ServerRequestInterface as Request;
 use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
-use Slim\Views\Twig;
+use Slim\Flash\Messages;
+# use Slim\Views\Twig;
 
 class FlashMessages
 {	
-	public function __construct(Twig $view)
+	public function __construct($container)
 	{
-		$this->view = $view;
+		$this->container = $container;
 	}
 	
 	public function __invoke(Request $request, RequestHandler $handler)
 	{
+		if(isset($_SESSION))
+		{
+			$this->container->set('flash', function(){ return new Messages(); });			
+		}
+
 		if(isset($_SESSION['slimFlash']) && is_array($_SESSION['slimFlash']))
 		{
-			$this->view->getEnvironment()->addGlobal('flash', $_SESSION['slimFlash']);
+			$this->container->get('view')->getEnvironment()->addGlobal('flash', $_SESSION['slimFlash']);
 			
 			unset($_SESSION['slimFlash']);
 		}
diff --git a/system/typemill/Middleware/SessionMiddleware.php b/system/typemill/Middleware/SessionMiddleware.php
new file mode 100644
index 0000000..4e2614a
--- /dev/null
+++ b/system/typemill/Middleware/SessionMiddleware.php
@@ -0,0 +1,55 @@
+segments = $segments;
+
+        $this->route = $route;
+    }
+    
+	public function process(Request $request, RequestHandler $handler) :response
+    {
+        # start session for routes
+        Session::startSessionForSegments($this->segments, $this->route);
+
+        $authenticated = ( 
+                (isset($_SESSION['username'])) && 
+                (isset($_SESSION['login'])) 
+            )
+            ? true : false;
+
+        if($authenticated)
+        {
+            # add userdata to the request for later use
+            $user = new User();
+
+            if($user->setUser($_SESSION['username']))
+            {
+                $userdata = $user->getUserData();
+
+                $request = $request->withAttribute('c_username', $userdata['username']);
+                $request = $request->withAttribute('c_userrole', $userdata['userrole']);
+            }
+        }
+
+
+		$response = $handler->handle($request);
+	
+		return $response;
+    }
+}
\ No newline at end of file
diff --git a/system/typemill/Middleware/WebRedirectIfUnauthenticated.php b/system/typemill/Middleware/WebRedirectIfUnauthenticated.php
index 2328992..74c099b 100644
--- a/system/typemill/Middleware/WebRedirectIfUnauthenticated.php
+++ b/system/typemill/Middleware/WebRedirectIfUnauthenticated.php
@@ -29,7 +29,6 @@ class WebRedirectIfUnauthenticated implements MiddlewareInterface
 
 			if($user->setUser($_SESSION['username']))
 			{
-
  				# pass username and userrole
 				$userdata = $user->getUserData();
 
diff --git a/system/typemill/Models/Content.php b/system/typemill/Models/Content.php
index ee7f1de..3f55a05 100644
--- a/system/typemill/Models/Content.php
+++ b/system/typemill/Models/Content.php
@@ -54,9 +54,18 @@ class Content
 		return $markdownArray;
 	}
 
-	public function saveDraftMarkdown($item, $markdown)
+	public function getLiveMarkdown($item)
 	{
-		$markdown = json_encode($markdown);
+		$filetype = '.md';
+
+		$markdown = $this->storage->getFile('contentFolder', '', $item->pathWithoutType . $filetype);
+
+		return $markdown;
+	}
+
+	public function saveDraftMarkdown($item, array $markdownArray)
+	{
+		$markdown = json_encode($markdownArray);
 
 		if($this->storage->writeFile('contentFolder', '', $item->pathWithoutType . '.txt', $markdown))
 		{
@@ -171,8 +180,20 @@ class Content
 		return $content;
 	}
 
+	public function getContentArray($markdown)
+	{
+		return $this->parsedown->text($markdown);
+	}
+
+	public function getContentHtml($contentArray)
+	{
+		return $this->parsedown->markup($contentArray);
+	}
+
 	public function arrayBlocksToMarkdown($arrayBlocks)
 	{
+		die("please use markdownToArrayText in content model");
+
         $markdown = '';
         
         foreach($arrayBlocks as $block)
@@ -183,8 +204,41 @@ class Content
         return $markdown;
 	}
 
+
+	public function markdownArrayToText(array $markdownArray)
+	{
+		return $this->parsedown->arrayBlocksToMarkdown($markdownArray);
+	}
+
+
+	public function markdownTextToArray(string $markdown)
+	{
+		return $this->parsedown->markdownToArrayBlocks($markdown);
+	}
+
+
+	public function getFirstImage(array $contentArray)
+	{
+		foreach($contentArray as $block)
+		{
+			if(isset($block['name']) && $block['name'] == 'p')
+			{
+				if(isset($block['handler']['argument']) && substr($block['handler']['argument'], 0, 2) == '![' )
+				{
+					return $block['handler']['argument'];	
+				}
+			}
+		}
+		
+		return false;
+	}
+
+
+########## FIX
 	public function generateToc($content, $relurl)
 	{
+		die('Please fix generateToc in content.php');
+
 		# we assume that page has no table of content
 		$toc = false;
 		
@@ -219,4 +273,59 @@ class Content
 
 		return $toc;
 	}
+
+	# MOVE SOMEWHERE ELSE
+	public function checkCustomCSS($theme)
+	{
+		return $this->storage->checkFile('cacheFolder', '', $theme . '-custom.css');
+	}
+
+	public function checkLogoFile($logo)
+	{
+		return $this->storage->checkFile('basepath', '', $logo);
+	}
+
+	public function getTitle(array $markdown)
+	{
+		if(!is_array($markdown))
+		{
+			$markdown = $this->markdownTextToArray($markdown);
+		}
+
+		return trim($markdown[0], "# ");
+	}
+
+	public function getDescription(array $markdown)
+	{
+		if(!is_array($markdown))
+		{
+			$markdown = $this->markdownTextToArray($markdown);
+		}
+
+		$description = isset($markdown[1]) ? $markdown[1] : '';
+
+		# create description or abstract from content
+		if($description !== '')
+		{
+			$firstLineArray = $this->parsedown->text($description);
+			$description 	= strip_tags($this->parsedown->markup($firstLineArray));
+
+			# if description is very short
+			if(strlen($description) < 100 && isset($markdown[2]))
+			{
+				$secondLineArray = $this->parsedown->text($markdown[2]);
+				$description 	.= ' ' . strip_tags($this->parsedown->markup($secondLineArray));
+			}
+
+			# if description is too long
+			if(strlen($description) > 160)
+			{
+				$description	= substr($description, 0, 160);
+				$lastSpace 		= strrpos($description, ' ');
+				$description 	= substr($description, 0, $lastSpace);
+			}
+		}
+
+		return $description;
+	}
 }
\ No newline at end of file
diff --git a/system/typemill/Models/Extension.php b/system/typemill/Models/Extension.php
index a2a8aa4..6ff17a4 100644
--- a/system/typemill/Models/Extension.php
+++ b/system/typemill/Models/Extension.php
@@ -10,7 +10,7 @@ class Extension
 
 	public function __construct()
 	{
-		$this->storage 				= new StorageWrapper('\Typemill\Models\Storage');
+		$this->storage 	= new StorageWrapper('\Typemill\Models\Storage');
 	}
 
 	public function getThemeDetails()
diff --git a/system/typemill/Models/Folder.php b/system/typemill/Models/Folder.php
index 307b49d..f85be2a 100644
--- a/system/typemill/Models/Folder.php
+++ b/system/typemill/Models/Folder.php
@@ -253,6 +253,7 @@ class Folder
 	public function getHomepageItem($baseUrl)
 	{
 		die('folder model: getHomepageItem moved to navigation model');
+
 		# return a standard item-object
 		$item 					= new \stdClass;
 
@@ -309,6 +310,8 @@ class Folder
 
 	public function getItemForUrlFrontend($folderContentDetails, $url, $result = NULL)
 	{
+		die('folder: called function getItemForUrlFrontend');
+
 		foreach($folderContentDetails as $key => $item)
 		{
 			# set item active, needed to move item in navigation
@@ -326,126 +329,6 @@ class Folder
 		return $result;
 	}	
 
-	public function getPagingForItem($content, $item)
-	{
-		# if page is home
-		if(trim($item->pathWithoutType, DIRECTORY_SEPARATOR) == 'index')
-		{
-			return $item;
-		}
-
-		$keyPos 			= count($item->keyPathArray)-1;
-		$thisChapArray		= $item->keyPathArray;
-		$nextItemArray 		= $item->keyPathArray;
-		$prevItemArray 		= $item->keyPathArray;
-		
-		$item->thisChapter 	= false;
-		$item->prevItem 	= false;
-		$item->nextItem 	= false;
-		
-		
-		/************************
-		* 	ADD THIS CHAPTER 	*
-		************************/
-
-		if($keyPos > 0)
-		{
-			array_pop($thisChapArray);
-			$item->thisChapter = $this->getItemWithKeyPath($content, $thisChapArray);
-		}
-		
-		/************************
-		* 	ADD NEXT ITEM	 	*
-		************************/
-				
-		if($item->elementType == 'folder')
-		{
-			# get the first element in the folder
-			$item->nextItem = isset($item->folderContent[0]) ? clone($item->folderContent[0]) : false;
-		}
-		
-		# the item is a file or an empty folder
-		if(!$item->nextItem)
-		{
-			# walk to the next file in the same hierarchy
-			$nextItemArray[$keyPos]++;
-
-			# get the key of the last element in this hierarchy level
-			# if there is no chapter, then it is probably an empty first-level-folder. Count content to get the number of first level items
-			$lastKey = $item->thisChapter ? array_key_last($item->thisChapter->folderContent) : count($content);
-
-			# as long as the nextItemArray is smaller than the last key in this hierarchy level, search for the next item
-			# this ensures that it does not stop if key is missing (e.g. if the next page is hidden)
-			while( ($nextItemArray[$keyPos] <= $lastKey) && !$item->nextItem = $this->getItemWithKeyPath($content, $nextItemArray) )
-			{
-				$nextItemArray[$keyPos]++;
-			}
-		}
-		
-		# there is no next file or folder in this level, so walk up the hierarchy to the next folder or file
-		while(!$item->nextItem)
-		{
-			# delete the array level with the current item, so you are in the parent folder
-			array_pop($nextItemArray);
-
-			# if the array is empty now, then you where in the base level already, so break
-			if(empty($nextItemArray)) break; 
-
-			# define the key position where you are right now
-			$newKeyPos = count($nextItemArray)-1;
-
-			# go to the next position
-			$nextItemArray[$newKeyPos]++;
-
-			# search for 5 items in case there are some hidden elements
-			$maxlength = $nextItemArray[$newKeyPos]+5;
-			while( ($nextItemArray[$newKeyPos] <= $maxlength) && !$item->nextItem = $this->getItemWithKeyPath($content, $nextItemArray) )
-			{
-				$nextItemArray[$newKeyPos]++;
-			}
-		}
-
-		/************************
-		* 	ADD PREVIOUS ITEM	*
-		************************/
-		
-		# check if element is the first in the array
-		$first = ($prevItemArray[$keyPos] == 0) ? true : false;
-
-		if(!$first)
-		{
-			$prevItemArray[$keyPos]--;
-			
-			while($prevItemArray[$keyPos] >= 0 && !$item->prevItem = $this->getItemWithKeyPath($content, $prevItemArray))
-			{
-				$prevItemArray[$keyPos]--;
-			}
-			
-			# if no item is found, then all previous items are hidden, so set first item to true and it will walk up the array later
-			if(!$item->prevItem)
-			{
-				$first = true;
-			}
-			elseif($item->prevItem && $item->prevItem->elementType == 'folder' && !empty($item->prevItem->folderContent))
-			{
-				# if the previous item is a folder, the get the last item of that folder
-				$item->prevItem = $this->getLastItemOfFolder($item->prevItem);
-			}
-		}
-
-		# if it is the first item in the folder (or all other files are hidden)
-		if($first)
-		{
-			# then the previous item is the containing chapter
-			$item->prevItem = $item->thisChapter;
-		}
-		
-		if($item->prevItem && $item->prevItem->elementType == 'folder'){ unset($item->prevItem->folderContent); }
-		if($item->nextItem && $item->nextItem->elementType == 'folder'){ unset($item->nextItem->folderContent); }
-		if($item->thisChapter){unset($item->thisChapter->folderContent); }
-		
-		return $item;
-	}
 
 	/*
 	 * Gets a copy of an item with a key
@@ -460,6 +343,8 @@ class Folder
 	 
 	public function getItemWithKeyPath($content, array $searchArray)
 	{
+		die('folder: called function getItemWithKeyPath');
+
 		$item = false;
 
 		foreach($searchArray as $key => $itemKey)
@@ -479,6 +364,9 @@ class Folder
 	# NOT IN USE
 	public function getItemWithKeyPathNew($array, array $keys)
 	{
+
+		die('folder: called function getItemWithKeyPathNew');
+
 		$item = $array;
 		
         foreach ($keys as $key)
@@ -499,6 +387,8 @@ class Folder
 	 
 	public function extractItemWithKeyPath($structure, array $keys)
 	{
+		die('folder: called function extractItemWithKeyPath');
+
 		$result = &$structure;
 		$last = array_pop($keys);
 
@@ -522,6 +412,8 @@ class Folder
 	# NOT IN USE
 	public function deleteItemWithKeyPathNOTINUSE($structure, array $keys)
 	{
+		die('folder: called function deleteItemWithKeyPathNOTINUSE');
+
 		$result = &$structure;
 		$last = array_pop($keys);
 
@@ -542,55 +434,11 @@ class Folder
 		
 		return $structure;
 	}
-	
-	# get breadcrumb as copied array, 
-	# set elements active in original 
-	# mark parent element in original
-	public function getBreadcrumb($content, $searchArray, $i = NULL, $breadcrumb = NULL)
-	{
-		# if it is the first round, create an empty array
-		if(!$i){ $i = 0; $breadcrumb = array();}
-
-		if(!$searchArray){ return $breadcrumb;}
-
-		while($i < count($searchArray))
-		{
-			if(!isset($content[$searchArray[$i]])){ return false; }
-			$item = $content[$searchArray[$i]];
-
-			if($i == count($searchArray)-1)
-			{
-				$item->active = true;
-			}
-			else
-			{
-				$item->activeParent = true;
-			}
-
-			/*
-			$item->active = true;
-			if($i == count($searchArray)-2)
-			{
-				$item->activeParent = true; 
-			}
-			*/
-
-			$copy = clone($item);
-			if($copy->elementType == 'folder')
-			{
-				unset($copy->folderContent);
-				$content = $item->folderContent;
-			}
-			$breadcrumb[] = $copy;
-			
-			$i++;
-			return $this->getBreadcrumb($content, $searchArray, $i++, $breadcrumb);
-		}
-		return $breadcrumb;
-	}
-	
+		
 	public function getParentItem($content, $searchArray, $iteration = NULL)
 	{
+		die('folder: called function getParentItem');
+		
 		if(!$iteration){ $iteration = 0; }
 		while($iteration < count($searchArray)-2)
 		{
@@ -600,17 +448,7 @@ class Folder
 		}
 		return $content[$searchArray[$iteration]];
 	}
-	
-	private function getLastItemOfFolder($folder)
-	{	
-		$lastItem = end($folder->folderContent);
-		if(is_object($lastItem) && $lastItem->elementType == 'folder' && !empty($lastItem->folderContent))
-		{
-			return $this->getLastItemOfFolder($lastItem);
-		}
-		return $lastItem;
-	}
-		
+			
 	public function getStringParts($name)
 	{
 		return preg_split('/[\-\.\_\=\+\?\!\*\#\(\)\/ ]/',$name);
diff --git a/system/typemill/Models/Meta.php b/system/typemill/Models/Meta.php
new file mode 100644
index 0000000..51da75d
--- /dev/null
+++ b/system/typemill/Models/Meta.php
@@ -0,0 +1,404 @@
+storage = new StorageWrapper('\Typemill\Models\Storage');
+	}
+
+	# used by contentApiController (backend) and pageController (frontend) and TwigMetaExtension (list pages)
+	public function getMetaData($item)
+	{
+		$metadata = $this->storage->getYaml('contentFolder', '', $item->pathWithoutType . '.yaml');
+	
+		return $metadata;
+
+
+
+		# compare with meta that are in use right now (e.g. changed theme, disabled plugin)
+		$metascheme = $this->getYaml('cache', 'metatabs.yaml');
+
+		if($metascheme)
+		{
+			$meta = $this->whitelistMeta($meta,$metascheme);
+		}
+
+		return $meta;
+	}
+
+	public function getMetaDefinitions($settings, $folder)
+	{
+		$metadefinitions = $this->storage->getYaml('systemSettings', '', 'metatabs.yaml');
+
+		# loop through all plugins
+		if(!empty($settings['plugins']))
+		{
+			foreach($settings['plugins'] as $name => $plugin)
+			{
+				if($plugin['active'])
+				{
+					$pluginSettings = \Typemill\Static\Settings::getObjectSettings('plugins', $name);
+					if($pluginSettings && isset($pluginSettings['metatabs']))
+					{
+						$metadefinitions = array_merge_recursive($metadefinitions, $pluginSettings['metatabs']);
+					}
+				}
+			}
+		}
+		
+		# add the meta from theme settings here
+		$themeSettings = \Typemill\Static\Settings::getObjectSettings('themes', $settings['theme']);
+		
+		if($themeSettings && isset($themeSettings['metatabs']))
+		{
+			$metadefinitions = array_merge_recursive($metadefinitions, $themeSettings['metatabs']);
+		}
+
+		# conditional fieldset for user or role based access
+		if(!isset($settings['pageaccess']) || $settings['pageaccess'] === NULL )
+		{
+			unset($metadefinitions['meta']['fields']['fieldsetrights']);
+		}
+
+		# conditional fieldset for folders
+		if(!$folder)
+		{
+			unset($metadefinitions['meta']['fields']['fieldsetfolder']);
+		}
+
+		# dispatch meta 
+#		$metatabs 		= $this->c->dispatcher->dispatch('onMetaDefinitionsLoaded', new OnMetaDefinitionsLoaded($metatabs))->getData();
+
+		return $metadefinitions;
+	}
+
+	public function updateMeta($meta, $item)
+	{
+		$filename 	= $item->pathWithoutType . '.yaml';
+
+		if($this->storage->updateYaml('contentFolder', '', $filename, $meta))
+		{
+			return true;
+		}
+
+		return $this->storage->getError();
+	}
+
+	public function addMetaDefaults($meta, $item, $authorFromSettings, $currentuser = false)
+	{
+		$modified = false;
+
+		if(!isset($meta['meta']['owner']))
+		{
+			$meta['meta']['owner'] = $currentuser ? $currentuser : false;
+			$modified = true;
+		}
+
+		if(!isset($meta['meta']['author']))
+		{
+			$meta['meta']['owner'] = $currentuser ? $currentuser : $authorFromSettings;
+			$modified = true;
+		}
+
+		if(!isset($meta['meta']['created']))
+		{
+			$meta['meta']['created'] = date("Y-m-d");
+			$modified = true;
+		}
+
+		if(!isset($meta['meta']['time']))
+		{
+			$meta['meta']['time'] = date("H-i-s");
+			$modified = true;
+		}
+
+		if(!isset($meta['meta']['navtitle']))
+		{
+			$meta['meta']['navtitle'] = $item->name;
+			$modified = true;
+		}
+
+		if($modified)
+		{
+			$this->updateMeta($meta, $item);
+		}
+
+		$filePath = $item->path;		
+		if($item->elementType == 'folder')
+		{
+			$filePath 	= $item->path . DIRECTORY_SEPARATOR . 'index.md';
+		}
+		$meta['meta']['modified'] = $this->storage->getFileTime('contentFolder', '', $filePath);
+
+		return $meta;
+	}
+
+	public function addMetaTitleDescription(array $meta, $item, array $markdown)
+	{
+		$title 			= (isset($meta['meta']['title']) && $meta['meta']['title'] != '') ? $meta['meta']['title'] : false;
+		$description 	= (isset($meta['meta']['description']) && $meta['meta']['description'] != '') ? $meta['meta']['description'] : false;
+
+		if(!$title OR !$description)
+		{
+			$content 	= new Content();
+
+			if(!$title)
+			{
+				$meta['meta']['title'] = $content->getTitle($markdown);
+			}
+
+			if(!$description)
+			{
+				$meta['meta']['description'] = $content->getDescription($markdown);
+			}
+
+			$this->updateMeta($meta, $item);
+		}
+
+		return $meta;
+	}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+	public function getNavtitle($url)
+	{
+		# get the extended structure where the navigation title is stored
+		$extended = $this->getYaml('cache', 'structure-extended.yaml');
+		
+		if(isset($extended[$url]['navtitle']))
+		{ 
+			return $extended[$url]['navtitle'];
+		}
+		return '';
+	}
+
+	# used by articleApiController and pageController to add title and description if an article is published
+	public function completePageMeta($content, $settings, $item)
+	{
+		$meta = $this->getPageMeta($settings, $item);
+
+		if(!$meta)
+		{
+			return $this->getPageMetaDefaults($content, $settings, $item);
+		}
+
+		$title = (isset($meta['meta']['title']) AND $meta['meta']['title'] !== '') ? true : false;
+		$description = (isset($meta['meta']['description']) AND $meta['meta']['description'] !== '') ? true : false;
+
+		if($title && $description)
+		{
+			return $meta;
+		}
+
+		# initialize parsedown extension
+		$parsedown = new ParsedownExtension();
+
+		# if content is not an array, then transform it
+		if(!is_array($content))
+		{
+			# turn markdown into an array of markdown-blocks
+			$content = $parsedown->markdownToArrayBlocks($content);
+		}
+
+		# delete markdown from title
+		if(!$title && isset($content[0]))
+		{
+			$meta['meta']['title'] = trim($content[0], "# ");
+		}
+
+		if(!$description && isset($content[1]))
+		{
+			$meta['meta']['description'] = $this->generateDescription($content, $parsedown, $item);
+		}
+
+		$this->updateYaml($settings['contentFolder'], $item->pathWithoutType . '.yaml', $meta);
+		
+		return $meta;
+	}
+
+	private function whitelistMeta($meta, $metascheme)
+	{
+		# we have only 2 dimensions, so no recursive needed
+		foreach($meta as $tab => $values)
+		{
+			if(!isset($metascheme[$tab]))
+			{
+				unset($meta[$tab]);
+			}
+			foreach($values as $key => $value)
+			{
+				if(!isset($metascheme[$tab][$key]))
+				{
+					unset($meta[$tab][$key]);
+				}
+			}
+		}
+		return $meta;
+	}
+
+	public function generateDescription($content, $parsedown, $item)
+	{
+		$description = isset($content[1]) ? $content[1] : '';
+
+		# create description or abstract from content
+		if($description !== '')
+		{
+			$firstLineArray = $parsedown->text($description);
+			$description 	= strip_tags($parsedown->markup($firstLineArray, $item->urlAbs));
+
+			# if description is very short
+			if(strlen($description) < 100 && isset($content[2]))
+			{
+				$secondLineArray = $parsedown->text($content[2]);
+				$description 	.= ' ' . strip_tags($parsedown->markup($secondLineArray, $item->urlAbs));
+			}
+
+			# if description is too long
+			if(strlen($description) > 300)
+			{
+				$description	= substr($description, 0, 300);
+				$lastSpace 		= strrpos($description, ' ');
+				$description 	= substr($description, 0, $lastSpace);
+			}
+		}
+		return $description;
+	}
+
+	public function transformPagesToPosts($folder)
+	{		
+		$filetypes			= array('md', 'txt', 'yaml');
+		$result 			= true;
+
+		foreach($folder->folderContent as $page)
+		{
+			# create old filename without filetype
+			$oldFile 	= $this->basePath . 'content' . $page->pathWithoutType;
+
+			# set default date
+			$date 		= date('Y-m-d', time());
+			$time		= date('H-i', time());
+
+			$meta 		= $this->getYaml('content', $page->pathWithoutType . '.yaml');
+
+			if($meta)
+			{
+				# get dates from meta
+				if(isset($meta['meta']['manualdate'])){ $date = $meta['meta']['manualdate']; }
+				elseif(isset($meta['meta']['created'])){ $date = $meta['meta']['created']; }
+				elseif(isset($meta['meta']['modified'])){ $date = $meta['meta']['modified']; }
+
+				# set time
+				if(isset($meta['meta']['time']))
+				{
+					$time = $meta['meta']['time'];
+				}
+			}
+
+			$datetime 	= $date . '-' . $time;
+			$datetime 	= implode(explode('-', $datetime));
+			$datetime	= substr($datetime,0,12);
+
+			# create new file-name without filetype
+			$newFile 	= $this->basePath . 'content' . $folder->path . DIRECTORY_SEPARATOR . $datetime . '-' . $page->slug;
+
+			foreach($filetypes as $filetype)
+			{
+				$oldFilePath = $oldFile . '.' . $filetype;
+				$newFilePath = $newFile . '.' . $filetype;
+							
+				#check if file with filetype exists and rename
+				if($oldFilePath != $newFilePath && file_exists($oldFilePath))
+				{
+					if(@rename($oldFilePath, $newFilePath))
+					{
+						$result = $result;
+					}
+					else
+					{
+						$result = false;
+					}
+				}
+			}
+		}
+
+		return $result;
+	}
+
+	public function transformPostsToPages($folder)
+	{
+		$filetypes			= array('md', 'txt', 'yaml');
+		$index				= 0;
+		$result 			= true;
+
+		foreach($folder->folderContent as $page)
+		{
+			# create old filename without filetype
+			$oldFile 	= $this->basePath . 'content' . $page->pathWithoutType;
+
+			$order 		= $index;
+
+			if($index < 10)
+			{
+				$order = '0' . $index;
+			}
+
+			# create new file-name without filetype
+			$newFile 	= $this->basePath . 'content' . $folder->path . DIRECTORY_SEPARATOR . $order . '-' . $page->slug;
+
+			foreach($filetypes as $filetype)
+			{
+				$oldFilePath = $oldFile . '.' . $filetype;
+				$newFilePath = $newFile . '.' . $filetype;
+				
+				#check if file with filetype exists and rename
+				if($oldFilePath != $newFilePath && file_exists($oldFilePath))
+				{
+					if(@rename($oldFilePath, $newFilePath))
+					{
+						$result = $result;
+					}
+					else
+					{
+						$result = false;
+					}
+				}
+			}
+
+			$index++;
+		}
+
+		return $result;
+	}
+
+	public function folderContainsFolders($folder)
+	{
+		foreach($folder->folderContent as $page)
+		{
+			if($page->elementType == 'folder')
+			{
+				return true;
+			}
+		}
+
+		return false;
+	}
+}
\ No newline at end of file
diff --git a/system/typemill/Models/Navigation.php b/system/typemill/Models/Navigation.php
index d733729..658e5d4 100644
--- a/system/typemill/Models/Navigation.php
+++ b/system/typemill/Models/Navigation.php
@@ -139,19 +139,8 @@ class Navigation extends Folder
 	{
 		# todo: filter for userrole or username 
 
-#		$this->clearNavigation(['extended' => true, 'draft' => true, 'live' => true]);
-
 		$this->draftNavigation = $this->storage->getFile('dataFolder', $this->naviFolder, $this->draftNaviName, 'unserialize');
 
-
-
-/*		echo '
';
-		$draftContentTree = $this->scanFolder($this->storage->getFolderPath('contentFolder'), true);
-		$draftNavigation = $this->getFolderContentDetails($draftContentTree, $language, $urlinfo['baseurl'], $urlinfo['basepath']);
-		print_r($draftNavigation);
-		die();
-*/
-
 		if($this->draftNavigation)
 		{
 			return $this->draftNavigation;
@@ -198,6 +187,59 @@ class Navigation extends Folder
 		return false;
 	}
 
+	# get the navigation with draft files for author environment
+	public function getLiveNavigation($urlinfo, $language, $userrole = null, $username = null)
+	{
+		# todo: filter for userrole or username 
+
+		$this->liveNavigation = $this->storage->getFile('dataFolder', $this->naviFolder, $this->liveNaviName, 'unserialize');
+
+		if($this->liveNavigation)
+		{
+			return $this->liveNavigation;
+		}
+
+		# if there is no cached navi, create a basic new draft navi
+		$basicLiveNavigation = $this->getBasicLiveNavigation($urlinfo, $language);
+
+		# get the extended navigation with additional infos from the meta-files like title or hidden pages
+		$extendedNavigation = $this->getExtendedNavigation($urlinfo, $language);
+
+		# merge the basic draft navi with the extended infos from meta-files
+		$liveNavigation = $this->mergeNavigationWithExtended($basicLiveNavigation, $extendedNavigation);
+
+		# cache it
+		$this->storage->writeFile('dataFolder', $this->naviFolder, $this->liveNaviName, $liveNavigation, 'serialize');
+
+		return $liveNavigation;
+	}
+
+	public function getBasicLiveNavigation($urlinfo, $language)
+	{
+		if(!$this->basicLiveNavigation)
+		{
+			$this->basicLiveNavigation = $this->createBasicLiveNavigation($urlinfo, $language);
+		}
+		return $this->basicLiveNavigation;
+	}
+
+	# creates a fresh structure with published and non-published pages for the author
+	public function createBasicLiveNavigation($urlinfo, $language)
+	{
+		# scan the content of the folder
+		$liveContentTree = $this->scanFolder($this->storage->getFolderPath('contentFolder'), $draft = false);
+
+		# if there is content, then get the content details
+		if(count($liveContentTree) > 0)
+		{
+			$liveNavigation = $this->getFolderContentDetails($liveContentTree, $language, $urlinfo['baseurl'], $urlinfo['basepath']);
+			
+			return $liveNavigation;
+		}
+
+		return false;
+	}
+
 	# get the extended navigation with additional infos from the meta-files like title or hidden pages
 	public function getExtendedNavigation($urlinfo, $language)
 	{
@@ -228,19 +270,15 @@ class Navigation extends Folder
 			$extended = [];
 		}
 
-		$contentFolder = $this->storage->getFolderPath('contentFolder');
-
 		foreach ($navigation as $key => $item)
 		{
 			# $filename = ($item->elementType == 'folder') ? DIRECTORY_SEPARATOR . 'index.yaml' : $item->pathWithoutType . '.yaml';
 			$filename = $item->pathWithoutType . '.yaml';
 
-			if(file_exists($contentFolder . $filename))
-			{
-				# read file
-				$meta = $this->storage->getYaml($contentFolder, '', $filename);
-			}
-			else
+			# read file
+			$meta = $this->storage->getYaml('contentFolder', '', $filename);
+
+			if(!$meta)
 			{
 				# create initial yaml
 				$meta = [];
@@ -365,122 +403,247 @@ class Navigation extends Folder
 		$item->urlRel			= '/';
 		$item->urlRelWoF		= '/';
 		$item->urlAbs			= $baseUrl;
-		$item->active			= true;
+		$item->active			= false;
 		$item->activeParent		= false;
 		$item->hide 			= false;
 
 		return $item;
 	}
 
-
-
-
-
-
-############################## TODO
-	# reads the cached structure with published pages
-	public function getLiveNavigation()
+	public function renameItem($item, $newslug)
 	{
-		# get the cached navi
-		$liveNavi = $this->storage->getFile('naviFolder', $this->naviFolder, $this->liveNaviName, 'unserialize');
+		$folder 	= str_replace($item->originalName, '', $item->path);
+		$oldname 	= $item->order . '-' . $item->slug;
+		$newname 	= $item->order . '-' . $newslug;
+		$result 	= true;
 
-		# if there is no cached structure
-		if(!$liveNavi)
+		if($item->elementType == 'folder')
 		{
-			return $this->createNewLiveNavigation();
+			$result = $this->storage->renameFile('contentFolder', $folder, $oldname, $newname);
 		}
 
-		return $liveNavi;
-	}
-
-
-################################## TODO
-	# creates a fresh structure with published pages
-	private function createNewLiveNavigation($urlinfo, $language)
-	{
-		# scan the content of the folder
-		$draftNavi = $this->scanFolder($this->storage->contentFolder, $draft = false);
-
-		# if there is content, then get the content details
-		if($draftNavi && count($draftNavi) > 0)
+		if($item->elementType == 'file')
 		{
-			# get the extended structure files with changes like navigation title or hidden pages
-			$extended = $this->getExtendedNavi();
-
-			# create an array of object with the whole content of the folder and changes from extended file
-			$liveNavi = $folder->getFolderContentDetails($liveNavi, $extended, $this->settings, $this->uri->getBaseUrl(), $this->uri->getBasePath());
-			
-			# cache structure live
-			$this->storage->writeFile($this->naviFolder, $this->liveNaviName, $liveNavi, 'serialize');
-
-			return $liveNavi;
-		}
-
-		return false;
-	}
-
-
-
-
-
-
-
-
-
-
-	# only backoffice
-	protected function renameExtended($item, $newFolder)
-	{
-		# get the extended structure files with changes like navigation title or hidden pages
-		$yaml = new writeYaml();
-		$extended = $yaml->getYaml('cacheFolder', '', 'structure-extended.yaml');
-
-		if(isset($extended[$item->urlRelWoF]))
-		{
-			$newUrl = $newFolder->urlRelWoF . '/' . $item->slug;
-
-			$entry = $extended[$item->urlRelWoF];
-			
-			unset($extended[$item->urlRelWoF]);
-			
-			$extended[$newUrl] = $entry;
-			$yaml->updateYaml('cacheFolder', '', 'structure-extended.yaml', $extended);
-		}
-
-		return true;
-	}
-
-	# only backoffice
-	protected function deleteFromExtended()
-	{
-		# get the extended structure files with changes like navigation title or hidden pages
-		$yaml = new writeYaml();
-		$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
-
-		if($this->item->elementType == "file" && isset($extended[$this->item->urlRelWoF]))
-		{
-			unset($extended[$this->item->urlRelWoF]);
-			$yaml->updateYaml('cacheFolder', '', 'structure-extended.yaml', $extended);
-		}
-
-		if($this->item->elementType == "folder")
-		{
-			$changed = false;
-
-			# delete all entries with that folder url
-			foreach($extended as $url => $entries)
+			$filetypes 	= array('md', 'txt', 'yaml');
+			$result 	= true;
+			foreach($filetypes as $filetype)
 			{
-				if( strpos($url, $this->item->urlRelWoF) !== false )
+				$oldfilename = $oldname . '.' . $filetype;
+				$newfilename = $newname . '.' . $filetype;
+
+				$result = $this->storage->renameFile('contentFolder', $folder, $oldfilename, $newfilename);
+			}
+		}
+		
+		return $result;
+	}
+
+	public function getCurrentPage($args)
+	{
+		if(isset($args['route']))
+		{
+			$argSegments = explode("/", $args['route']);
+
+			# check if the last url segment is a number
+			$pageNumber = array_pop($argSegments);
+			if(is_numeric($pageNumber) && $pageNumber < 10000)
+			{
+				# then check if the segment before the page is a "p" that indicates a paginator
+				$pageIndicator = array_pop($argSegments);
+				if($pageIndicator == "p")
 				{
-					$changed = true;
-					unset($extended[$url]);
+					return $pageNumber;
 				}
 			}
+		}
 
-			if($changed)
+		return false;		
+	}
+
+
+	public function removeHiddenPages($liveNavigation)
+	{
+		foreach($liveNavigation as $key => $item)
+		{
+			if(isset($item->hide) && $item->hide == true)
 			{
-				$yaml->updateYaml('cacheFolder', '', 'structure-extended.yaml', $extended);
+				unset($liveNavigation[$key]);
+			}
+			elseif($item->elementType == 'folder' && !empty($item->folderContent))
+			{
+				$item->folderContent = $this->removeHiddenPages($item->folderContent);
 			}
 		}
+
+		return $liveNavigation;
 	}
+
+
+	public function getBreadcrumb($navigation, $searchArray, $i = NULL, $breadcrumb = NULL)
+	{
+		# if it is the first round, create an empty array
+		if(!$i){ $i = 0; $breadcrumb = array();}
+
+		if(!$searchArray){ return $breadcrumb;}
+
+		while($i < count($searchArray))
+		{
+			if(!isset($navigation[$searchArray[$i]])){ return false; }
+			$item = $navigation[$searchArray[$i]];
+
+			if($i == count($searchArray)-1)
+			{
+				$item->active = true;
+			}
+			else
+			{
+				$item->activeParent = true;
+			}
+
+			$copy = clone($item);
+			if($copy->elementType == 'folder')
+			{
+				unset($copy->folderContent);
+				$navigation = $item->folderContent;
+			}
+			$breadcrumb[] = $copy;
+			
+			$i++;
+			return $this->getBreadcrumb($navigation, $searchArray, $i++, $breadcrumb);
+		}
+
+		return $breadcrumb;
+	}	
+
+	public function getPagingForItem($navigation, $item)
+	{
+		# if page is home
+		if(trim($item->pathWithoutType, DIRECTORY_SEPARATOR) == 'index')
+		{
+			return $item;
+		}
+
+		$keyPos 			= count($item->keyPathArray)-1;
+		$thisChapArray		= $item->keyPathArray;
+		$nextItemArray 		= $item->keyPathArray;
+		$prevItemArray 		= $item->keyPathArray;
+		
+		$item->thisChapter 	= false;
+		$item->prevItem 	= false;
+		$item->nextItem 	= false;
+		
+		
+		/************************
+		* 	ADD THIS CHAPTER 	*
+		************************/
+
+		if($keyPos > 0)
+		{
+			array_pop($thisChapArray);
+			$item->thisChapter = $this->getItemWithKeyPath($navigation, $thisChapArray);
+		}
+		
+		/************************
+		* 	ADD NEXT ITEM	 	*
+		************************/
+				
+		if($item->elementType == 'folder')
+		{
+			# get the first element in the folder
+			$item->nextItem = isset($item->folderContent[0]) ? clone($item->folderContent[0]) : false;
+		}
+		
+		# the item is a file or an empty folder
+		if(!$item->nextItem)
+		{
+			# walk to the next file in the same hierarchy
+			$nextItemArray[$keyPos]++;
+
+			# get the key of the last element in this hierarchy level
+			# if there is no chapter, then it is probably an empty first-level-folder. Count content to get the number of first level items
+			$lastKey = $item->thisChapter ? array_key_last($item->thisChapter->folderContent) : count($navigation);
+
+			# as long as the nextItemArray is smaller than the last key in this hierarchy level, search for the next item
+			# this ensures that it does not stop if key is missing (e.g. if the next page is hidden)
+			while( ($nextItemArray[$keyPos] <= $lastKey) && !$item->nextItem = $this->getItemWithKeyPath($navigation, $nextItemArray) )
+			{
+				$nextItemArray[$keyPos]++;
+			}
+		}
+		
+		# there is no next file or folder in this level, so walk up the hierarchy to the next folder or file
+		while(!$item->nextItem)
+		{
+			# delete the array level with the current item, so you are in the parent folder
+			array_pop($nextItemArray);
+
+			# if the array is empty now, then you where in the base level already, so break
+			if(empty($nextItemArray)) break; 
+
+			# define the key position where you are right now
+			$newKeyPos = count($nextItemArray)-1;
+
+			# go to the next position
+			$nextItemArray[$newKeyPos]++;
+
+			# search for 5 items in case there are some hidden elements
+			$maxlength = $nextItemArray[$newKeyPos]+5;
+			while( ($nextItemArray[$newKeyPos] <= $maxlength) && !$item->nextItem = $this->getItemWithKeyPath($navigation, $nextItemArray) )
+			{
+				$nextItemArray[$newKeyPos]++;
+			}
+		}
+
+		/************************
+		* 	ADD PREVIOUS ITEM	*
+		************************/
+		
+		# check if element is the first in the array
+		$first = ($prevItemArray[$keyPos] == 0) ? true : false;
+
+		if(!$first)
+		{
+			$prevItemArray[$keyPos]--;
+			
+			while($prevItemArray[$keyPos] >= 0 && !$item->prevItem = $this->getItemWithKeyPath($navigation, $prevItemArray))
+			{
+				$prevItemArray[$keyPos]--;
+			}
+			
+			# if no item is found, then all previous items are hidden, so set first item to true and it will walk up the array later
+			if(!$item->prevItem)
+			{
+				$first = true;
+			}
+			elseif($item->prevItem && $item->prevItem->elementType == 'folder' && !empty($item->prevItem->folderContent))
+			{
+				# if the previous item is a folder, the get the last item of that folder
+				$item->prevItem = $this->getLastItemOfFolder($item->prevItem);
+			}
+		}
+
+		# if it is the first item in the folder (or all other files are hidden)
+		if($first)
+		{
+			# then the previous item is the containing chapter
+			$item->prevItem = $item->thisChapter;
+		}
+		
+		if($item->prevItem && $item->prevItem->elementType == 'folder'){ unset($item->prevItem->folderContent); }
+		if($item->nextItem && $item->nextItem->elementType == 'folder'){ unset($item->nextItem->folderContent); }
+		if($item->thisChapter){unset($item->thisChapter->folderContent); }
+		
+		return $item;
+	}
+
+	public function getLastItemOfFolder($folder)
+	{
+		$lastItem = end($folder->folderContent);
+		if(is_object($lastItem) && $lastItem->elementType == 'folder' && !empty($lastItem->folderContent))
+		{
+			return $this->getLastItemOfFolder($lastItem);
+		}
+		return $lastItem;
+	}
+
 }
\ No newline at end of file
diff --git a/system/typemill/Models/ProcessFile.php b/system/typemill/Models/ProcessFile.php
index f1b44f1..a8d18e1 100644
--- a/system/typemill/Models/ProcessFile.php
+++ b/system/typemill/Models/ProcessFile.php
@@ -4,9 +4,9 @@ namespace Typemill\Models;
 
 class ProcessFile extends ProcessAssets
 {
+
 	public function storeFile($file, $name)
 	{
-
 		$this->clearTempFolder();
 
 		$this->setPathInfo($name);
@@ -39,6 +39,9 @@ class ProcessFile extends ProcessAssets
 
 
 
+
+
+
 	/**
 	 * Moves the uploaded file to the upload directory. Only used for settings / NON VUE.JS uploads
 	 *
@@ -60,22 +63,6 @@ class ProcessFile extends ProcessAssets
 	    return $this->getFullName();
 	}
 
-	public function publishFile()
-	{
-		$files 			= scandir($this->tmpFolder);
-		$success		= true;
-		
-		foreach($files as $file)
-		{
-			if (!in_array($file, array(".","..")))
-			{
-				$success = rename($this->tmpFolder . $file, $this->fileFolder . $file);
-			}
-		}
-		
-		return $success;
-	}
-
 
 
 
diff --git a/system/typemill/Models/Storage.php b/system/typemill/Models/Storage.php
index 9ede8cf..526076d 100644
--- a/system/typemill/Models/Storage.php
+++ b/system/typemill/Models/Storage.php
@@ -162,8 +162,27 @@ class Storage
 		return false;
 	}
 
+	public function getFileTime($location, $folder, $filename)
+	{
+		$filepath = $this->getFolderPath($location, $folder) . $filename;
+
+		if(!file_exists($filepath))
+		{
+			$this->error = "The file $filepath does not exist.";
+
+			return false;
+		}
+
+		return date("Y-m-d",filemtime($filepath));
+	}
+
 	public function writeFile($location, $folder, $filename, $data, $method = NULL)
 	{
+		# CLEAN EVERYTHING UP FUNCTION
+		$folder 	= trim($folder, DIRECTORY_SEPARATOR);
+		$folder 	= ($folder == '') ? '' : $folder . DIRECTORY_SEPARATOR;
+		$filename 	= trim($filename, DIRECTORY_SEPARATOR);
+
 		if(!$this->checkFolder($location, $folder))
 		{
 			if(!$this->createFolder($location, $folder))
@@ -172,7 +191,7 @@ class Storage
 			}
 		}
 
-		$filepath = $this->getFolderPath($location) . $folder . DIRECTORY_SEPARATOR . $filename;
+		$filepath = $this->getFolderPath($location) . $folder . $filename;
 			
 		$openfile = @fopen($filepath, "w");
 		if(!$openfile)
@@ -203,17 +222,22 @@ class Storage
 
 	public function renameFile($location, $folder, $oldname, $newname)
 	{
+		$folder = trim($folder, DIRECTORY_SEPARATOR);
+
 		$oldFilePath = $this->getFolderPath($location) . $folder . DIRECTORY_SEPARATOR . $oldname;
 		$newFilePath = $this->getFolderPath($location) . $folder . DIRECTORY_SEPARATOR . $newname;
 
-		if(!file_exists($oldFilePath))
+		if($oldFilePath != $newFilePath)
 		{
-			return false;
-		}
+			if(!file_exists($oldFilePath))
+			{
+				return false;
+			}
 
-		if(!rename($oldFilePath, $newFilePath))
-		{
-			return false;
+			if(!rename($oldFilePath, $newFilePath))
+			{
+				return false;
+			}
 		}
 		
 		return true;
@@ -323,7 +347,37 @@ class Storage
 		return $filename;
 	}
 
-	public function publishImage($name)
+	public function publishFile($name)
+	{
+		$pathinfo = pathinfo($name);
+		if(!$pathinfo)
+		{
+			$this->error = 'Could not read pathinfo.';
+
+			return false;
+		}
+
+		$filename = $pathinfo['filename'] . '.' . $pathinfo['extension'];
+		$filepath = $this->tmpFolder . $filename;
+
+		if(!file_exists($this->tmpFolder . $filename))
+		{
+			$this->error = "We did not find the file in the tmp-folder or could not read it.";
+			return false;
+		}
+
+		$success = rename($this->tmpFolder . $filename, $this->fileFolder . $filename);
+		
+		if($success === true)
+		{
+			# return true;
+			return 'media/files/' . $filename;
+		}
+
+		return false;
+	}
+
+	public function publishImage($name, $noresize)
 	{
 		$pathinfo = pathinfo($name);
 		if(!$pathinfo)
@@ -353,12 +407,26 @@ class Storage
 			switch($destinationfolder)
 			{
 				case 'original':
-					if(!rename($imagepath, $this->originalFolder . $filename))
+
+					$result = rename($imagepath, $this->originalFolder . $filename);
+				
+					if($noresize)
 					{
-						$this->error = "We could not store the original image to the original folder";
+						$result = copy($this->originalFolder . $filename, $this->liveFolder . $filename);
+						$extension = pathinfo($this->originalFolder . $filename, PATHINFO_EXTENSION);
 					}
+				
+					if(!$result)
+					{
+						$this->error = "We could not store the original image";
+					}
+				
 					break;
 				case 'live':
+					if($noresize)
+					{
+						break;
+					}
 					if(!rename($imagepath, $this->liveFolder . $filename))
 					{
 						$this->error = "We could not store the live image to the live folder";
@@ -424,6 +492,14 @@ class Storage
 
 
 
+
+
+
+
+
+
+
+
 /*
 
 
@@ -752,36 +828,5 @@ class Storage
 		return false;
 	}
 	
-	public function renamePost($oldPathWithoutType, $newPathWithoutType)
-	{
-		$filetypes			= array('md', 'txt', 'yaml');
-				
-		$oldPath 			= $this->basePath . 'content' . $oldPathWithoutType;
-		$newPath 			= $this->basePath . 'content' . $newPathWithoutType;
-						
-		$result 			= true;
-		
-		foreach($filetypes as $filetype)
-		{
-			$oldFilePath = $oldPath . '.' . $filetype;
-			$newFilePath = $newPath . '.' . $filetype;
-			
-			#check if file with filetype exists and rename
-			if($oldFilePath != $newFilePath && file_exists($oldFilePath))
-			{
-				if(@rename($oldFilePath, $newFilePath))
-				{
-					$result = $result;
-				}
-				else
-				{
-					$result = false;
-				}
-			}
-		}
-
-		return $result;
-	}	
-
 	*/
 }
\ No newline at end of file
diff --git a/system/typemill/Models/Validation.php b/system/typemill/Models/Validation.php
index 07fb188..67835f8 100644
--- a/system/typemill/Models/Validation.php
+++ b/system/typemill/Models/Validation.php
@@ -434,6 +434,70 @@ class Validation
 		}
 	}
 
+	public function articleUpdate(array $params)
+	{
+		$v = new Validator($params);
+
+		# special conditions for startpage
+		if(isset($params['item_id']) && $params['item_id'] == '')
+		{
+			$v->rule('required', ['url', 'title', 'body']);
+			$v->rule('markdownSecure', 'title');
+			$v->rule('markdownSecure', 'body');
+		}
+		else
+		{
+			$v->rule('required', ['item_id', 'url', 'title', 'body']);
+			$v->rule('regex', 'item_id', '/^[0-9.]+$/i');
+			$v->rule('markdownSecure', 'title');
+			$v->rule('markdownSecure', 'body');
+		}
+		
+		if($v->validate())
+		{
+			return true;
+		} 
+		else
+		{
+			return $v->errors();
+		}
+	}
+
+	public function articleRename(array $params)
+	{
+		$v = new Validator($params);
+		
+		$v->rule('required', ['url', 'slug', 'oldslug']);
+		$v->rule('regex', 'slug', '/^[a-z0-9\-]*$/');
+		$v->rule('lengthBetween', 'slug', 1, 50)->message("Length between 1 - 50"); 
+		$v->rule('different', 'slug', 'oldslug');
+
+		if($v->validate())
+		{
+			return true;
+		} 
+		else
+		{
+			return $v->errors();
+		}
+	}
+
+	public function metaInput(array $params)
+	{
+		$v = new Validator($params);
+		
+		$v->rule('required', ['url', 'tab', 'data']);
+
+		if($v->validate())
+		{
+			return true;
+		} 
+		else
+		{
+			return $v->errors();
+		}
+	}
+
 
 
 
diff --git a/system/typemill/Plugin.php b/system/typemill/Plugin.php
index 213fc08..e61a220 100644
--- a/system/typemill/Plugin.php
+++ b/system/typemill/Plugin.php
@@ -3,8 +3,8 @@
 namespace Typemill;
 
 use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Typemill\Models\Fields;
-use Typemill\Models\WriteYaml;
+# use Typemill\Models\Fields;
+# use Typemill\Models\WriteYaml;
 use Typemill\Models\Validation;
 use Typemill\Extensions\ParsedownExtension;
 
@@ -16,195 +16,122 @@ abstract class Plugin implements EventSubscriberInterface
 
 	protected $adminpath = false;
 
-    /**
-     * Constructor
-     *
-     */
+	/**
+	 * Constructor
+	 *
+	 */
 	
-    public function __construct($container)
-    {
+	public function __construct($container)
+	{
 		$this->container 	= $container;
-/*
-		$this->path 		= trim($this->container['request']->getUri()->getPath(),"/");
-
-		if(substr($this->path, 0, 3) === "tm/")
-		{
-			$this->adminpath = true;
-		}
-*/
-    }
-
-    protected function isXhr()
-    {
-    	return true;
-    	if($this->container['request']->isXhr())
-    	{
-			return true;
-		}
-		return false;
-    }
-
-    protected function getParams()
-    {
-    	return true;
-    	return $this->container['request']->getParams();
-    }
-
-    protected function returnJson($data)
-    {
-    	return true;
-        return $this->container['response']
-            ->withHeader("Content-Type", "application/json")
-            ->withStatus(200)
-            ->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
-    }
-
-    protected function returnJsonError($data)
-    {
-    	return true;
-        return $this->container['response']
-            ->withHeader("Content-Type", "application/json")
-            ->withStatus(400)
-            ->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
-    }
+		$this->urlinfo 		= $this->container->get('urlinfo');
+		$this->path  		= $this->urlinfo['currentpath'];
+	}
 	
 	protected function getSettings()
 	{
-		return true;
 		return $this->container->get('settings');
 	}
 	
 	protected function getPluginSettings($plugin)
 	{
-		return true;
 		return $this->container->get('settings')['plugins'][$plugin];
 	}
 
-	protected function getRoute()
+	protected function urlinfo()
 	{
-		return true;
-		return $this->container['request']->getUri()->withUserInfo('');
-	}
-	
-	protected function getPath()
-	{
-		return true;
-		return $this->container['request']->getUri()->getPath();
+		return $this->container->get('urlinfo');
 	}
 	
 	protected function getDispatcher()
 	{
-		return true;
-		return $this->container['dispatcher'];
+		return $this->container->get('dispatcher');
 	}
 	
 	protected function getTwig()
 	{
-		return true;
-		return $this->container['view'];
+		return $this->container->get('view');
 	}
 	
 	protected function addTwigGlobal($name, $class)
 	{
-		return true;
 		$this->container->view->getEnvironment()->addGlobal($name, $class);
 	}
 	
 	protected function addTwigFilter($name, $filter)
 	{
-		return true;
 		$filter = new \Twig_SimpleFilter($name, $filter);
 		$this->container->view->getEnvironment()->addFilter($filter);
 	}
 	
 	protected function addTwigFunction($name, $function)
 	{
-		return true;
 		$function = new \Twig_SimpleFunction($name, $function);
 		$this->container->view->getEnvironment()->addFunction($function);
 	}
 
 	protected function addJS($JS)
 	{
-		return true;
-		$this->container->assets->addJS($JS);
+		$this->container->get('assets')->addJS($JS);
 	}
 
 	protected function addEditorJS($JS)
 	{
-		return true;
-		$this->container->assets->addEditorJS($JS);
+		$this->container->get('assets')->addEditorJS($JS);
 	}
 
 	protected function addInlineJS($JS)
 	{
-		return true;
-		$this->container->assets->addInlineJS($JS);
+		$this->container->get('assets')->addInlineJS($JS);
 	}
 
 	protected function addSvgSymbol($symbol)
 	{
-		return true;
-		$this->container->assets->addSvgSymbol($symbol);
+		$this->container->get('assets')->addSvgSymbol($symbol);
 	}
 
 	protected function addEditorInlineJS($JS)
 	{
-		return true;
-		$this->container->assets->addEditorInlineJS($JS);
+		$this->container->get('assets')->addEditorInlineJS($JS);
 	}
 	
 	protected function addCSS($CSS)
 	{
-		return true;
-		$this->container->assets->addCSS($CSS);		
+		$this->container->get('assets')->addCSS($CSS);
 	}
 	
 	protected function addInlineCSS($CSS)
 	{
-		return true;
-		$this->container->assets->addInlineCSS($CSS);		
+		$this->container->get('assets')->addInlineCSS($CSS);		
 	}
 
 	protected function addEditorCSS($CSS)
 	{
-		return true;
-		$this->container->assets->addEditorCSS($CSS);
+		$this->container->get('assets')->addEditorCSS($CSS);
 	}
 
 	protected function getMeta()
 	{
-		return true;
-		return $this->container->assets->meta;
+		return $this->container->get('assets')->meta;
 	}
 
 	public function addMeta($key,$meta)
 	{
-		return true;
-		$this->container->assets->addMeta($key, $meta);
+		$this->container->get('assets')->addMeta($key, $meta);
 	}
 
 	protected function activateAxios()
 	{
-		return true;
-		$this->container->assets->activateAxios();		
+		$this->container->get('assets')->activateAxios();
 	}
 	
 	protected function activateVue()
 	{
-		return true;
-		$this->container->assets->activateVue();		
+		$this->container->get('assets')->activateVue();
 	}
 
-	protected function activateTachyons()
-	{
-		return true;
-		$this->container->assets->activateTachyons();		
-	}	
-
 	protected function markdownToHtml($markdown)
 	{
-		return true;
 		$parsedown 		= new ParsedownExtension();
 		
 		$contentArray 	= $parsedown->text($markdown);
@@ -238,7 +165,7 @@ abstract class Plugin implements EventSubscriberInterface
 	
 	protected function generateForm($pluginName, $routename)
 	{
-		$fieldsModel = new Fields();
+		$fieldsModel 		= new Fields();
 		
 		$settings 			= $this->getSettings();
 		$form 				= false;
diff --git a/system/typemill/Static/License.php b/system/typemill/Static/License.php
index 2a33492..cf0d2bf 100644
--- a/system/typemill/Static/License.php
+++ b/system/typemill/Static/License.php
@@ -104,126 +104,4 @@ class License
 
 		return false;
 	}
-}
-
-
-
-/* KIRBY -> source -> cms -> system.php
-
-	/**
-	 * Loads the license file and returns
-	 * the license information if available
-	 *
-	 * @return string|bool License key or `false` if the current user has
-	 *                     permissions for access.settings, otherwise just a
-	 *                     boolean that tells whether a valid license is active
-	 
-	public function license()
-	{
-		try {
-			$license = Json::read($this->app->root('license'));
-		} catch (Throwable) {
-			return false;
-		}
-
-		// check for all required fields for the validation
-		if (isset(
-			$license['license'],
-			$license['order'],
-			$license['date'],
-			$license['email'],
-			$license['domain'],
-			$license['signature']
-		) !== true) {
-			return false;
-		}
-
-		// build the license verification data
-		$data = [
-			'license' => $license['license'],
-			'order'   => $license['order'],
-			'email'   => hash('sha256', $license['email'] . 'kwAHMLyLPBnHEskzH9pPbJsBxQhKXZnX'),
-			'domain'  => $license['domain'],
-			'date'    => $license['date']
-		];
-
-
-		// get the public key
-		$pubKey = F::read($this->app->root('kirby') . '/kirby.pub');
-
-		// verify the license signature
-		$data      = json_encode($data);
-		$signature = hex2bin($license['signature']);
-		if (openssl_verify($data, $signature, $pubKey, 'RSA-SHA256') !== 1) {
-			return false;
-		}
-
-		// verify the URL
-		if ($this->licenseUrl() !== $this->licenseUrl($license['domain'])) {
-			return false;
-		}
-
-		// only return the actual license key if the
-		// current user has appropriate permissions
-		if ($this->app->user()?->isAdmin() === true) {
-			return $license['license'];
-		}
-
-		return true;
-	}
-
-
-	/**
-	 * Validates the license key
-	 * and adds it to the .license file in the config
-	 * folder if possible.
-	 *
-	 * @throws \Kirby\Exception\Exception
-	 * @throws \Kirby\Exception\InvalidArgumentException
-	 *
-	public function register(string $license = null, string $email = null): bool
-	{
-		if (Str::startsWith($license, 'K3-PRO-') === false) {
-			throw new InvalidArgumentException(['key' => 'license.format']);
-		}
-
-		if (V::email($email) === false) {
-			throw new InvalidArgumentException(['key' => 'license.email']);
-		}
-
-		// @codeCoverageIgnoreStart
-		$response = Remote::get('https://hub.getkirby.com/register', [
-			'data' => [
-				'license' => $license,
-				'email'   => Str::lower(trim($email)),
-				'domain'  => $this->indexUrl()
-			]
-		]);
-
-		if ($response->code() !== 200) {
-			throw new Exception($response->content());
-		}
-
-		// decode the response
-		$json = Json::decode($response->content());
-
-		// replace the email with the plaintext version
-		$json['email'] = $email;
-
-		// where to store the license file
-		$file = $this->app->root('license');
-
-		// save the license information
-		Json::write($file, $json);
-
-		if ($this->license() === false) {
-			throw new InvalidArgumentException([
-				'key' => 'license.verification'
-			]);
-		}
-		// @codeCoverageIgnoreEnd
-
-		return true;
-	}
-
-*/
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/system/typemill/Static/Plugins.php b/system/typemill/Static/Plugins.php
index f50ddaa..9c631bb 100644
--- a/system/typemill/Static/Plugins.php
+++ b/system/typemill/Static/Plugins.php
@@ -90,7 +90,7 @@ class Plugins
 	public static function getPremiumLicence($className)
 	{
 		$premiumlist = [
-			'\Plugins\demo\demo' => 'BUSINESS'
+			# '\Plugins\demo\demo' => 'BUSINESS'
 		];
 
 		if(isset($premiumList['className']))
diff --git a/system/typemill/Static/Session.php b/system/typemill/Static/Session.php
index ed307b0..e0d3011 100644
--- a/system/typemill/Static/Session.php
+++ b/system/typemill/Static/Session.php
@@ -36,8 +36,36 @@ class Session
 				# start session
 				session_start();
 
-				break;
+#				break;
 			}
 		}
 	}
+
+	public static function stopSession()
+	{
+		if(isset($_SESSION))
+		{
+			# Unset all of the session variables.
+			$_SESSION = array();
+
+			# If it's desired to kill the session, also delete the session cookie. This will destroy the session, and not just the session data
+			if (ini_get("session.use_cookies"))
+			{
+				$params = session_get_cookie_params();
+			
+				setcookie(
+					session_name(), 
+					'', 
+					time() - 42000,
+					$params["path"],
+					$params["domain"],
+					$params["secure"], 
+					$params["httponly"]
+				);
+			}
+
+			# Finally, destroy the session.
+			session_destroy();
+		}
+	}
 }
\ No newline at end of file
diff --git a/system/typemill/Static/Settings.php b/system/typemill/Static/Settings.php
index aeecd84..09eaf3e 100644
--- a/system/typemill/Static/Settings.php
+++ b/system/typemill/Static/Settings.php
@@ -145,6 +145,12 @@ class Settings
 	}
 
 
+
+
+
+
+
+
 ### refactor
   
 	public static function createSettings()
diff --git a/system/typemill/author/auth/login.twig b/system/typemill/author/auth/login.twig
index 01ae972..0a7de05 100644
--- a/system/typemill/author/auth/login.twig
+++ b/system/typemill/author/auth/login.twig
@@ -54,7 +54,7 @@
 
 					
 
-					
+					
 
 					{% if settings.recoverpw %}
 						
diff --git a/system/typemill/author/content/blox-editor.twig b/system/typemill/author/content/blox-editor.twig
index b11c524..0e173c9 100644
--- a/system/typemill/author/content/blox-editor.twig
+++ b/system/typemill/author/content/blox-editor.twig
@@ -3,15 +3,30 @@
 
 {% block content %}
 
-	
- - {% for block in content %} -
{{ block.html|raw }}
- {% endfor %} +
+ +
+ content + meta +
+ +
+ {% for block in content %} +
{{ block.html|raw }}
+ {% endfor %} +
- -
+ + {% if (acl.isAllowed(get_role(), 'content', 'update')) or ( (mycontent) and (acl.isAllowed(get_role(), 'mycontent', 'update')) ) %} + +
you have the right!
+ + {% endif %} + +
+ +
@@ -22,13 +37,19 @@ - + + diff --git a/system/typemill/author/content/raw-editor.twig b/system/typemill/author/content/raw-editor.twig new file mode 100644 index 0000000..aeacc38 --- /dev/null +++ b/system/typemill/author/content/raw-editor.twig @@ -0,0 +1,35 @@ +{% extends 'layouts/layoutContent.twig' %} +{% block title %}{{ translate('Raw Editor') }}{% endblock %} + +{% block content %} + +
+ +
+ +
+ +{% endblock %} + + +{% block javascript %} + + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/system/typemill/author/css/custom.css b/system/typemill/author/css/custom.css index a65840d..1921edd 100644 --- a/system/typemill/author/css/custom.css +++ b/system/typemill/author/css/custom.css @@ -55,7 +55,7 @@ [data-el="editor"] { border-width: 1px; background:rgb(68 64 60); - color: white; + color: transparent; caret-color: white; white-space: break-spaces; word-break: break-word; diff --git a/system/typemill/author/css/output.css b/system/typemill/author/css/output.css index 620ae0a..0a14f23 100644 --- a/system/typemill/author/css/output.css +++ b/system/typemill/author/css/output.css @@ -794,6 +794,10 @@ video { margin-bottom: 0.25rem; } +.mb-16 { + margin-bottom: 4rem; +} + .mb-2 { margin-bottom: 0.5rem; } @@ -866,12 +870,8 @@ video { margin-right: 1rem; } -.mb-12 { - margin-bottom: 3rem; -} - -.mb-16 { - margin-bottom: 4rem; +.mb-8 { + margin-bottom: 2rem; } .block { @@ -1026,10 +1026,6 @@ video { width: 2.5rem; } -.w-11\/12 { - width: 91.666667%; -} - .w-3\/5 { width: 60%; } @@ -1038,14 +1034,18 @@ video { width: 0px; } -.w-3\/4 { - width: 75%; -} - .w-24 { width: 6rem; } +.w-11\/12 { + width: 91.666667%; +} + +.w-3\/4 { + width: 75%; +} + .max-w-md { max-width: 28rem; } @@ -1202,6 +1202,18 @@ video { border-right-width: 8px; } +.border-l { + border-left-width: 1px; +} + +.border-t { + border-top-width: 1px; +} + +.border-r { + border-right-width: 1px; +} + .border-r-2 { border-right-width: 2px; } @@ -1214,18 +1226,14 @@ video { border-bottom-width: 1px; } -.border-r { - border-right-width: 1px; -} - -.border-l { - border-left-width: 1px; -} - .border-l-4 { border-left-width: 4px; } +.border-l-2 { + border-left-width: 2px; +} + .border-b-8 { border-bottom-width: 8px; } @@ -1238,14 +1246,6 @@ video { border-bottom-width: 4px; } -.border-l-2 { - border-left-width: 2px; -} - -.border-t { - border-top-width: 1px; -} - .border-solid { border-style: solid; } @@ -1259,6 +1259,16 @@ video { border-color: rgb(209 213 219 / var(--tw-border-opacity)); } +.border-stone-100 { + --tw-border-opacity: 1; + border-color: rgb(245 245 244 / var(--tw-border-opacity)); +} + +.border-stone-200 { + --tw-border-opacity: 1; + border-color: rgb(231 229 228 / var(--tw-border-opacity)); +} + .border-stone-700 { --tw-border-opacity: 1; border-color: rgb(68 64 60 / var(--tw-border-opacity)); @@ -1269,9 +1279,9 @@ video { border-color: rgb(214 211 209 / var(--tw-border-opacity)); } -.border-stone-200 { +.border-stone-50 { --tw-border-opacity: 1; - border-color: rgb(231 229 228 / var(--tw-border-opacity)); + border-color: rgb(250 250 249 / var(--tw-border-opacity)); } .border-teal-500 { @@ -1279,14 +1289,14 @@ video { border-color: rgb(20 184 166 / var(--tw-border-opacity)); } -.border-stone-100 { +.border-yellow-400 { --tw-border-opacity: 1; - border-color: rgb(245 245 244 / var(--tw-border-opacity)); + border-color: rgb(250 204 21 / var(--tw-border-opacity)); } -.border-stone-50 { +.border-rose-500 { --tw-border-opacity: 1; - border-color: rgb(250 250 249 / var(--tw-border-opacity)); + border-color: rgb(244 63 94 / var(--tw-border-opacity)); } .border-red-500 { @@ -1299,6 +1309,11 @@ video { border-color: rgb(255 255 255 / var(--tw-border-opacity)); } +.border-yellow-500 { + --tw-border-opacity: 1; + border-color: rgb(234 179 8 / var(--tw-border-opacity)); +} + .border-rose-100 { --tw-border-opacity: 1; border-color: rgb(255 228 230 / var(--tw-border-opacity)); @@ -1314,26 +1329,6 @@ video { border-color: rgb(226 232 240 / var(--tw-border-opacity)); } -.border-rose-500 { - --tw-border-opacity: 1; - border-color: rgb(244 63 94 / var(--tw-border-opacity)); -} - -.border-yellow-500 { - --tw-border-opacity: 1; - border-color: rgb(234 179 8 / var(--tw-border-opacity)); -} - -.border-yellow-300 { - --tw-border-opacity: 1; - border-color: rgb(253 224 71 / var(--tw-border-opacity)); -} - -.border-yellow-400 { - --tw-border-opacity: 1; - border-color: rgb(250 204 21 / var(--tw-border-opacity)); -} - .border-x-transparent { border-left-color: transparent; border-right-color: transparent; @@ -1359,9 +1354,14 @@ video { background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } -.bg-stone-300 { +.bg-stone-50 { --tw-bg-opacity: 1; - background-color: rgb(214 211 209 / var(--tw-bg-opacity)); + background-color: rgb(250 250 249 / var(--tw-bg-opacity)); +} + +.bg-stone-100 { + --tw-bg-opacity: 1; + background-color: rgb(245 245 244 / var(--tw-bg-opacity)); } .bg-transparent { @@ -1378,11 +1378,6 @@ video { background-color: rgb(68 64 60 / var(--tw-bg-opacity)); } -.bg-stone-100 { - --tw-bg-opacity: 1; - background-color: rgb(245 245 244 / var(--tw-bg-opacity)); -} - .bg-teal-500 { --tw-bg-opacity: 1; background-color: rgb(20 184 166 / var(--tw-bg-opacity)); @@ -1408,26 +1403,11 @@ video { background-color: rgb(87 83 78 / var(--tw-bg-opacity)); } -.bg-stone-50 { - --tw-bg-opacity: 1; - background-color: rgb(250 250 249 / var(--tw-bg-opacity)); -} - .bg-yellow-500 { --tw-bg-opacity: 1; background-color: rgb(234 179 8 / var(--tw-bg-opacity)); } -.bg-yellow-400 { - --tw-bg-opacity: 1; - background-color: rgb(250 204 21 / var(--tw-bg-opacity)); -} - -.bg-yellow-300 { - --tw-bg-opacity: 1; - background-color: rgb(253 224 71 / var(--tw-bg-opacity)); -} - .bg-opacity-90 { --tw-bg-opacity: 0.9; } @@ -1489,11 +1469,6 @@ video { padding-bottom: 0.75rem; } -.px-6 { - padding-left: 1.5rem; - padding-right: 1.5rem; -} - .px-12 { padding-left: 3rem; padding-right: 3rem; @@ -1504,6 +1479,11 @@ video { padding-bottom: 2rem; } +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + .px-2 { padding-left: 0.5rem; padding-right: 0.5rem; @@ -1519,11 +1499,6 @@ video { padding-right: 0.25rem; } -.px-4 { - padding-left: 1rem; - padding-right: 1rem; -} - .py-10 { padding-top: 2.5rem; padding-bottom: 2.5rem; @@ -1534,11 +1509,36 @@ video { padding-right: 2rem; } +.px-16 { + padding-left: 4rem; + padding-right: 4rem; +} + +.py-12 { + padding-top: 3rem; + padding-bottom: 3rem; +} + .py-4 { padding-top: 1rem; padding-bottom: 1rem; } +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.px-20 { + padding-left: 5rem; + padding-right: 5rem; +} + +.py-16 { + padding-top: 4rem; + padding-bottom: 4rem; +} + .pr-6 { padding-right: 1.5rem; } @@ -1595,14 +1595,14 @@ video { padding-right: 0.75rem; } -.pt-2 { - padding-top: 0.5rem; -} - .pb-4 { padding-bottom: 1rem; } +.pt-2 { + padding-top: 0.5rem; +} + .pt-4 { padding-top: 1rem; } @@ -1660,11 +1660,6 @@ video { line-height: 1.25rem; } -.text-2xl { - font-size: 1.5rem; - line-height: 2rem; -} - .text-lg { font-size: 1.125rem; line-height: 1.75rem; @@ -1680,6 +1675,11 @@ video { line-height: 2.25rem; } +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + .font-normal { font-weight: 400; } @@ -1768,11 +1768,6 @@ video { color: rgb(13 148 136 / var(--tw-text-opacity)); } -.text-black { - --tw-text-opacity: 1; - color: rgb(0 0 0 / var(--tw-text-opacity)); -} - .text-teal-300 { --tw-text-opacity: 1; color: rgb(94 234 212 / var(--tw-text-opacity)); @@ -1788,21 +1783,16 @@ video { color: rgb(231 229 228 / var(--tw-text-opacity)); } +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} + .text-rose-500 { --tw-text-opacity: 1; color: rgb(244 63 94 / var(--tw-text-opacity)); } -.text-cyan-500 { - --tw-text-opacity: 1; - color: rgb(6 182 212 / var(--tw-text-opacity)); -} - -.text-teal-700 { - --tw-text-opacity: 1; - color: rgb(15 118 110 / var(--tw-text-opacity)); -} - .underline { -webkit-text-decoration-line: underline; text-decoration-line: underline; @@ -1828,6 +1818,12 @@ video { opacity: 0.25; } +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + .shadow-lg { --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); @@ -1840,12 +1836,6 @@ video { box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } -.shadow-md { - --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - .outline-none { outline: 2px solid transparent; outline-offset: 2px; @@ -1960,6 +1950,11 @@ video { background-color: rgb(214 211 209 / 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)); @@ -1970,26 +1965,16 @@ video { background-color: rgb(15 118 110 / var(--tw-bg-opacity)); } -.hover\:bg-rose-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(190 18 60 / var(--tw-bg-opacity)); -} - -.hover\:bg-cyan-500:hover { - --tw-bg-opacity: 1; - background-color: rgb(6 182 212 / 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-yellow-600:hover { --tw-bg-opacity: 1; background-color: rgb(202 138 4 / var(--tw-bg-opacity)); } +.hover\:bg-rose-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(190 18 60 / var(--tw-bg-opacity)); +} + .hover\:text-stone-50:hover { --tw-text-opacity: 1; color: rgb(250 250 249 / var(--tw-text-opacity)); @@ -2061,10 +2046,6 @@ video { background-color: rgb(250 250 249 / var(--tw-bg-opacity)); } -.disabled\:cursor-default:disabled { - cursor: default; -} - .disabled\:cursor-not-allowed:disabled { cursor: not-allowed; } @@ -2074,11 +2055,21 @@ video { background-color: rgb(250 250 249 / var(--tw-bg-opacity)); } +.disabled\:bg-stone-200:disabled { + --tw-bg-opacity: 1; + background-color: rgb(231 229 228 / var(--tw-bg-opacity)); +} + .disabled\:text-stone-900:disabled { --tw-text-opacity: 1; color: rgb(28 25 23 / var(--tw-text-opacity)); } +.disabled\:text-stone-800:disabled { + --tw-text-opacity: 1; + color: rgb(41 37 36 / var(--tw-text-opacity)); +} + .group:hover .group-hover\:visible { visibility: visible; } diff --git a/system/typemill/author/js/vue-blox-components.js b/system/typemill/author/js/vue-blox-components.js index 3200086..2100d0e 100644 --- a/system/typemill/author/js/vue-blox-components.js +++ b/system/typemill/author/js/vue-blox-components.js @@ -5,9 +5,16 @@ bloxeditor.component('title-component', {
`, mounted: function(){ this.$refs.markdown.focus(); + autosize(document.querySelectorAll('textarea')); + + eventBus.$on('beforeSave', this.beforeSave ); }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, updatemarkdown(content) { this.$emit('updateMarkdownEvent', content); @@ -29,9 +36,16 @@ bloxeditor.component('markdown-component', { `, mounted: function(){ this.$refs.markdown.focus(); + autosize(document.querySelectorAll('textarea')); + + eventBus.$on('beforeSave', this.beforeSave ); }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, updatemarkdown(content) { var emptyline = /^\s*$(?:\r\n?|\n)/gm; @@ -71,6 +85,8 @@ bloxeditor.component('headline-component', { }, mounted: function(){ + eventBus.$on('beforeSave', this.beforeSave ); + this.$refs.markdown.focus(); this.compmarkdown = this.markdown; @@ -88,6 +104,10 @@ bloxeditor.component('headline-component', { } }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, updatemarkdown: function(event) { this.level = this.getHeadlineLevel(this.compmarkdown); @@ -154,6 +174,8 @@ bloxeditor.component('ulist-component', { }, mounted: function(){ + eventBus.$on('beforeSave', this.beforeSave ); + this.compmarkdown = this.markdown; if(this.compmarkdown == '') @@ -190,6 +212,10 @@ bloxeditor.component('ulist-component', { this.$refs.markdown.focus(); }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, updatemarkdown: function(event) { this.$emit('updateMarkdownEvent', event.target.value); @@ -245,6 +271,8 @@ bloxeditor.component('olist-component', { }, mounted: function(){ + eventBus.$on('beforeSave', this.beforeSave ); + this.compmarkdown = this.markdown; if(this.compmarkdown == '') @@ -259,6 +287,10 @@ bloxeditor.component('olist-component', { this.$refs.markdown.focus(); }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, updatemarkdown: function(event) { this.$emit('updateMarkdownEvent', event.target.value); @@ -303,7 +335,7 @@ bloxeditor.component('code-component', {
- +
@@ -316,7 +348,11 @@ bloxeditor.component('code-component', { } }, mounted: function(){ + + eventBus.$on('beforeSave', this.beforeSave ); + this.$refs.markdown.focus(); + if(this.markdown) { var codelines = this.markdown.split(/\r\n|\n\r|\n|\r/); @@ -347,6 +383,10 @@ bloxeditor.component('code-component', { }); }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, createlanguage: function() { var codeblock = this.prefix + this.language + '\n' + this.codeblock + '\n' + this.prefix; @@ -377,6 +417,8 @@ bloxeditor.component('hr-component', { `, mounted: function(){ + eventBus.$on('beforeSave', this.beforeSave ); + this.$refs.markdown.focus(); autosize(document.querySelectorAll('textarea')); @@ -384,6 +426,10 @@ bloxeditor.component('hr-component', { this.$emit('updateMarkdownEvent', '---'); }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, updatemarkdown: function(event) { var emptyline = /^\s*$(?:\r\n?|\n)/gm; @@ -411,6 +457,8 @@ bloxeditor.component('toc-component', { `, mounted: function(){ + eventBus.$on('beforeSave', this.beforeSave ); + this.$refs.markdown.focus(); autosize(document.querySelectorAll('textarea')); @@ -418,6 +466,10 @@ bloxeditor.component('toc-component', { this.$emit('updateMarkdownEvent', '[TOC]'); }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, updatemarkdown: function(event) { var emptyline = /^\s*$(?:\r\n?|\n)/gm; @@ -453,6 +505,8 @@ bloxeditor.component('quote-component', { }, mounted: function(){ + eventBus.$on('beforeSave', this.beforeSave ); + this.$refs.markdown.focus(); if(this.markdown) @@ -469,6 +523,10 @@ bloxeditor.component('quote-component', { }); }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, updatemarkdown: function(value) { this.quote = value; @@ -518,6 +576,8 @@ bloxeditor.component('notice-component', { }, mounted: function(){ + eventBus.$on('beforeSave', this.beforeSave ); + this.$refs.markdown.focus(); if(this.markdown) @@ -538,6 +598,10 @@ bloxeditor.component('notice-component', { }); }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, noticedown: function() { this.prefix = this.getNoticePrefix(this.markdown); @@ -621,9 +685,9 @@ bloxeditor.component('table-component', { class="border border-stone-300 text-center text-stone-500" >{{value}}
-
add left column
-
add right column
-
delete column
+
{{ $filters.translate('add left column') }}
+
{{ $filters.translate('add right column') }}
+
{{ $filters.translate('delete column') }}
-
add row above
-
add row below
-
delete row
+
{{ $filters.translate('add row above') }}
+
{{ $filters.translate('add row below') }}
+
{{ $filters.translate('delete row') }}
{{ value }} @@ -656,6 +720,8 @@ bloxeditor.component('table-component', { `, mounted: function(){ + eventBus.$on('beforeSave', this.beforeSave ); + this.$refs.markdown.focus(); if(this.markdown) @@ -664,6 +730,10 @@ bloxeditor.component('table-component', { } }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, generateTable(markdown) { var table = []; @@ -834,10 +904,16 @@ bloxeditor.component('definition-component', {
- +
Add description @@ -849,11 +925,14 @@ bloxeditor.component('definition-component', { - Add definition + {{ $filters.translate('Add definition') }}
`, mounted: function(){ + + eventBus.$on('beforeSave', this.beforeSave ); + if(this.markdown) { var definitionList = this.markdown.replace("\r\n", "\n"); @@ -909,6 +988,10 @@ bloxeditor.component('definition-component', { } }, methods: { + beforeSave() + { + this.$emit('saveBlockEvent'); + }, enter() { return false; @@ -1056,7 +1139,6 @@ bloxeditor.component('inline-formats', { }, mounted: function() { this.formatBar = document.getElementById('formatBar'); - console.info(this.formatBar); window.addEventListener('mouseup', this.onMouseup), window.addEventListener('mousedown', this.onMousedown) }, @@ -1239,37 +1321,37 @@ bloxeditor.component('image-component', {
- +
- +
- +
- +
- +
- +
@@ -1280,7 +1362,7 @@ bloxeditor.component('image-component', { compmarkdown: '', saveimage: false, maxsize: 5, // megabyte - imgpreview: false, + imgpreview: '', showmedialib: false, load: false, imgmeta: false, @@ -1299,14 +1381,19 @@ bloxeditor.component('image-component', { imgfile: '', showresize: true, noresize: false, + newblock: true, } }, mounted: function(){ + eventBus.$on('beforeSave', this.beforeSave ); + this.$refs.markdown.focus(); if(this.markdown) { + this.newblock = false; + this.showresize = false; this.imgmeta = true; @@ -1319,8 +1406,7 @@ bloxeditor.component('image-component', { this.imgcaption = imgcaption[0].slice(1,-1); imgmarkdown = imgmarkdown.replace(this.imgcaption,''); - imgmarkdown = imgmarkdown.replace(/\r?\n|\r/g,''); - + imgmarkdown = imgmarkdown.replace(/\r?\n|\r/g,''); } if(this.markdown[0] == '[') @@ -1332,7 +1418,6 @@ bloxeditor.component('image-component', { imgmarkdown = imgmarkdown.replace(imglink[1],''); imgmarkdown = imgmarkdown.slice(1, -1); - } } @@ -1446,7 +1531,7 @@ bloxeditor.component('image-component', { } else { - errors = 'Maximum size of image alt-text is 100 characters'; + errors = this.$filters.translate('Maximum size of image alt-text is 100 characters'); imgmarkdown = '![]'; } @@ -1458,7 +1543,7 @@ bloxeditor.component('image-component', { } else { - errors = 'Maximum size of image title is 100 characters'; + errors = this.$filters.translate('Maximum size of image title is 100 characters'); } } else @@ -1476,7 +1561,7 @@ bloxeditor.component('image-component', { } else { - errors = 'Maximum size of image id is 100 characters'; + errors = this.$filters.translate('Maximum size of image id is 100 characters'); } } if(this.imgclass != '') @@ -1487,7 +1572,7 @@ bloxeditor.component('image-component', { } else { - errors = 'Maximum size of image class is 100 characters'; + errors = this.$filters.translate('Maximum size of image class is 100 characters'); } } if(this.imgloading != '') @@ -1519,7 +1604,7 @@ bloxeditor.component('image-component', { } else { - errors = 'Maximum size of image link is 100 characters'; + errors = this.$filters.translate('Maximum size of image link is 100 characters'); } } @@ -1531,14 +1616,16 @@ bloxeditor.component('image-component', { } else { - errors = 'Maximum size of image caption is 140 characters'; + errors = this.$filters.translate('Maximum size of image caption is 140 characters'); } } + /* if(this.noresize === true) { imgmarkdown = imgmarkdown + '|noresize'; } + */ if(errors) { @@ -1551,6 +1638,7 @@ bloxeditor.component('image-component', { this.compmarkdown = imgmarkdown; // publishController.errors.message = false; // this.$parent.activatePage(); + this.$emit('updateMarkdownEvent', imgmarkdown); } }, @@ -1652,33 +1740,20 @@ bloxeditor.component('image-component', { 'url': data.urlinfo.route, 'image': e.target.result, 'name': imageFile.name, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, }) .then(function (response) { self.load = false; self.saveimage = true; - // self.$parent.activatePage(); self.imgmeta = true; self.imgfile = response.data.name; - // self.$emit('updateMarkdownEvent', '![]('+ response.data.name +')'); }) .catch(function (error) { - /* - if(httpStatus == 400) - { - self.activatePage(); - publishController.errors.message = "Looks like you are logged out. Please login and try again."; - } - */ - if(error.response) { alert("errror in response"); - // publishController.errors.message = error.response.data.errors.message; } }); @@ -1686,76 +1761,46 @@ bloxeditor.component('image-component', { } } }, - saveBlock() + beforeSave() { - /* saves image, after that saves markdown */ + /* publish the image before you save the block */ + if(!this.imgfile) { alert("no file"); return; } - if(!this.saveimage) { - this.saveMarkdown(); + this.$emit('saveBlockEvent'); } else { var self = this; tmaxios.put('/api/v1/image',{ - 'url': data.urlinfo.route, - 'imgfile': this.imgfile, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, + 'url': data.urlinfo.route, + 'imgfile': this.imgfile, + 'noresize': this.noresize }) .then(function (response) { - self.imgfile = response.data.path; - // self.createmarkdownimageloaded(); - self.saveMarkdown(); + self.saveimage = false; + self.imgfile = response.data.path; - // console.info(response.data.path); - return; - // self.$root.$data.content = response.data.content; - // self.closeEditor(); + self.createmarkdownimageloaded(); + + self.$emit('saveBlockEvent'); }) .catch(function (error) { if(error.response) { console.info(error.response); - // publishController.errors.message = error.response.data.errors.message; } }); } }, - saveMarkdown() - { - this.createmarkdownimageloaded(); - - var self = this; - - tmaxios.put('/api/v1/block',{ - 'url': data.urlinfo.route, - 'block_id': this.index, - 'markdown': this.compmarkdown, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, - }) - .then(function (response) - { - self.$root.$data.content = response.data.content; - eventBus.$emit('closeComponents'); - }) - .catch(function (error) - { - if(error.response) - { - publishController.errors.message = error.response.data.errors.message; - } - }); - }, } }) @@ -1770,14 +1815,14 @@ bloxeditor.component('file-component', { - upload file + {{ $filters.translate('upload file') }}

@@ -64,10 +63,13 @@ - + {% block javascript %}{% endblock %} - {{ assets.renderJS() }} - + {{ assets.renderJS()|raw }} + \ No newline at end of file diff --git a/system/typemill/author/layouts/layoutSystem.twig b/system/typemill/author/layouts/layoutSystem.twig index e5d1be5..1bacbb9 100644 --- a/system/typemill/author/layouts/layoutSystem.twig +++ b/system/typemill/author/layouts/layoutSystem.twig @@ -47,13 +47,12 @@ Typemill version xyz - {{ csrf() | raw }} + {% block javascript %}{% endblock %} diff --git a/system/typemill/routes/api.php b/system/typemill/routes/api.php index 2826b34..36bf866 100644 --- a/system/typemill/routes/api.php +++ b/system/typemill/routes/api.php @@ -15,6 +15,7 @@ use Typemill\Controllers\ControllerApiImage; use Typemill\Controllers\ControllerApiFile; use Typemill\Controllers\ControllerApiAuthorArticle; use Typemill\Controllers\ControllerApiAuthorBlock; +use Typemill\Controllers\ControllerApiAuthorMeta; use Typemill\Controllers\ControllerApiAuthorShortcode; $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) { @@ -48,17 +49,20 @@ $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) { $group->get('/filerestrictions', ControllerApiFile::class . ':getFileRestrictions')->setName('api.file.getrestrictions')->add(new ApiAuthorization($acl, 'mycontent', 'create')); $group->post('/filerestrictions', ControllerApiFile::class . ':updateFileRestrictions')->setName('api.file.updaterestrictions')->add(new ApiAuthorization($acl, 'mycontent', 'create')); $group->post('/file', ControllerApiFile::class . ':uploadFile')->setName('api.file.upload')->add(new ApiAuthorization($acl, 'mycontent', 'create')); + $group->put('/file', ControllerApiFile::class . ':publishFile')->setName('api.file.publish')->add(new ApiAuthorization($acl, 'mycontent', 'update')); # $group->get('/api/v1/file', ControllerAuthorMediaApi::class . ':getFile')->setName('api.file.get')->add(new RestrictApiAccess($container['router'])); -# $app->put('/api/v1/file', ControllerAuthorMediaApi::class . ':publishFile')->setName('api.file.publish')->add(new RestrictApiAccess($container['router'])); # $app->delete('/api/v1/file', ControllerAuthorMediaApi::class . ':deleteFile')->setName('api.file.delete')->add(new RestrictApiAccess($container['router'])); # ARTICLE $group->post('/article/sort', ControllerApiAuthorArticle::class . ':sortArticle')->setName('api.article.sort')->add(new ApiAuthorization($acl, 'content', 'create')); # author - $group->post('/article', ControllerApiAuthorArticle::class . ':createArticle')->setName('api.article.create')->add(new ApiAuthorization($acl, 'content', 'create')); # author + $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', 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 + $group->post('/draft/publish', ControllerApiAuthorArticle::class . ':publishDraft')->setName('api.draft.publish')->add(new ApiAuthorization($acl, 'content', 'create')); # author # BLOCKS $group->post('/block', ControllerApiAuthorBlock::class . ':addBlock')->setName('api.block.add')->add(new ApiAuthorization($acl, 'mycontent', 'create')); @@ -70,74 +74,10 @@ $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) { # SHORTCODE $group->get('/shortcodedata', ControllerApiAuthorShortcode::class . ':getShortcodeData')->setName('api.shortcodedata.get')->add(new ApiAuthorization($acl, 'mycontent', 'view')); -})->add(new ApiAuthentication()); + # META + $group->get('/metadata', ControllerApiAuthorMeta::class . ':getMetaData')->setName('api.metadata.get')->add(new ApiAuthorization($acl, 'mycontent', 'view')); + $group->get('/metadefinitions', ControllerApiAuthorMeta::class . ':getMetaDefinitions')->setName('api.definitions.get')->add(new ApiAuthorization($acl, 'mycontent', 'view')); + $group->post('/metadata', ControllerApiAuthorMeta::class . ':updateMetaData')->setName('api.metadata.update')->add(new ApiAuthorization($acl, 'mycontent', 'update')); + $group->get('/meta', ControllerApiAuthorMeta::class . ':getMeta')->setName('api.meta.get')->add(new ApiAuthorization($acl, 'mycontent', 'view')); - -# https://stackoverflow.blog/2021/10/06/best-practices-for-authentication-and-authorization-for-rest-apis/ - -# INTERNAL API -# on login generate token -# tmpApiKey: store token in userfile -# tmpApiDate: store date in userfile -# send username and token to frontend -# AUTHORIZATION: apikey username.tmpapikey -# validy equals session length from settings - -# PUBLIC API -# ApiKey: -# AUTHORIZATION: apikey username.apikey - - -/* -use Typemill\Controllers\ControllerAuthorArticleApi; -use Typemill\Controllers\ControllerAuthorBlockApi; -use Typemill\Controllers\ControllerAuthorMetaApi; -use Typemill\Controllers\ControllerAuthorMediaApi; -use Typemill\Controllers\ControllerSettings; -use Typemill\Middleware\RestrictApiAccess; - -$app->get('/api/v1/themes', ControllerSettings::class . ':getThemeSettings')->setName('api.themes')->add(new RestrictApiAccess($container['router'])); -$app->delete('/api/v1/clearcache', ControllerSettings::class . ':clearCache')->setName('api.clearcache')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/users/getbynames', ControllerSettings::class . ':getUsersByNames')->setName('api.usersbynames')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/users/getbyemail', ControllerSettings::class . ':getUsersByEmail')->setName('api.usersbyemail')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/users/getbyrole', ControllerSettings::class . ':getUsersByRole')->setName('api.usersbyrole')->add(new RestrictApiAccess($container['router'])); - -$app->post('/api/v1/article/markdown', ControllerAuthorArticleApi::class . ':getArticleMarkdown')->setName('api.article.markdown')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/article/html', ControllerAuthorArticleApi::class . ':getArticleHtml')->setName('api.article.html')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/article/publish', ControllerAuthorArticleApi::class . ':publishArticle')->setName('api.article.publish')->add(new RestrictApiAccess($container['router'])); -$app->delete('/api/v1/article/unpublish', ControllerAuthorArticleApi::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new RestrictApiAccess($container['router'])); -$app->delete('/api/v1/article/discard', ControllerAuthorArticleApi::class . ':discardArticleChanges')->setName('api.article.discard')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/article/rename', ControllerAuthorArticleApi::class . ':renameArticle')->setName('api.article.rename')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/article/sort', ControllerAuthorArticleApi::class . ':sortArticle')->setName('api.article.sort')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/article', ControllerAuthorArticleApi::class . ':createArticle')->setName('api.article.create')->add(new RestrictApiAccess($container['router'])); -$app->put('/api/v1/article', ControllerAuthorArticleApi::class . ':updateArticle')->setName('api.article.update')->add(new RestrictApiAccess($container['router'])); -$app->delete('/api/v1/article', ControllerAuthorArticleApi::class . ':deleteArticle')->setName('api.article.delete')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/baseitem', ControllerAuthorArticleApi::class . ':createBaseItem')->setName('api.baseitem.create')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/navigation', ControllerAuthorArticleApi::class . ':getNavigation')->setName('api.navigation.get')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/post', ControllerAuthorArticleApi::class . ':createPost')->setName('api.post.create')->add(new RestrictApiAccess($container['router'])); - -$app->get('/api/v1/metadefinitions', ControllerAuthorMetaApi::class . ':getMetaDefinitions')->setName('api.metadefinitions.get')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/article/metaobject', ControllerAuthorMetaApi::class . ':getArticleMetaobject')->setName('api.articlemetaobject.get')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/article/metadata', ControllerAuthorMetaApi::class . ':getArticleMeta')->setName('api.articlemeta.get')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/article/metadata', ControllerAuthorMetaApi::class . ':updateArticleMeta')->setName('api.articlemeta.update')->add(new RestrictApiAccess($container['router'])); - -$app->post('/api/v1/block', ControllerAuthorBlockApi::class . ':addBlock')->setName('api.block.add')->add(new RestrictApiAccess($container['router'])); -$app->put('/api/v1/block', ControllerAuthorBlockApi::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router'])); -$app->delete('/api/v1/block', ControllerAuthorBlockApi::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router'])); -$app->put('/api/v1/moveblock', ControllerAuthorBlockApi::class . ':moveBlock')->setName('api.block.move')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/shortcodedata', ControllerAuthorBlockApi::class . ':getShortcodeData')->setName('api.shortcodedata.get')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/video', ControllerAuthorMediaApi::class . ':saveVideoImage')->setName('api.video.save')->add(new RestrictApiAccess($container['router'])); - -$app->get('/api/v1/medialib/images', ControllerAuthorMediaApi::class . ':getMediaLibImages')->setName('api.medialibimg.get')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/medialib/files', ControllerAuthorMediaApi::class . ':getMediaLibFiles')->setName('api.medialibfiles.get')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/image', ControllerAuthorMediaApi::class . ':getImage')->setName('api.image.get')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/image', ControllerAuthorMediaApi::class . ':createImage')->setName('api.image.create')->add(new RestrictApiAccess($container['router'])); -$app->put('/api/v1/image', ControllerAuthorMediaApi::class . ':publishImage')->setName('api.image.publish')->add(new RestrictApiAccess($container['router'])); -$app->delete('/api/v1/image', ControllerAuthorMediaApi::class . ':deleteImage')->setName('api.image.delete')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/filerestrictions', ControllerAuthorMediaApi::class . ':getFileRestrictions')->setName('api.file.getrestrictions')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/filerestrictions', ControllerAuthorMediaApi::class . ':updateFileRestrictions')->setName('api.file.updaterestrictions')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/file', ControllerAuthorMediaApi::class . ':getFile')->setName('api.file.get')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/file', ControllerAuthorMediaApi::class . ':uploadFile')->setName('api.file.upload')->add(new RestrictApiAccess($container['router'])); -$app->put('/api/v1/file', ControllerAuthorMediaApi::class . ':publishFile')->setName('api.file.publish')->add(new RestrictApiAccess($container['router'])); -$app->delete('/api/v1/file', ControllerAuthorMediaApi::class . ':deleteFile')->setName('api.file.delete')->add(new RestrictApiAccess($container['router'])); -*/ \ No newline at end of file +})->add(new ApiAuthentication()); \ No newline at end of file diff --git a/system/typemill/routes/web.php b/system/typemill/routes/web.php index b11f816..34228f2 100644 --- a/system/typemill/routes/web.php +++ b/system/typemill/routes/web.php @@ -8,7 +8,7 @@ use Typemill\Controllers\ControllerWebAuth; use Typemill\Controllers\ControllerWebSystem; use Typemill\Controllers\ControllerWebAuthor; use Typemill\Controllers\ControllerWebFrontend; -#use Slim\Views\TwigMiddleware; +use Typemill\Controllers\ControllerWebDownload; # login/register $app->group('/tm', function (RouteCollectorProxy $group) { @@ -30,10 +30,11 @@ $app->group('/tm', function (RouteCollectorProxy $group) use ($routeParser,$acl) $group->get('/account', ControllerWebSystem::class . ':showAccount')->setName('user.account')->add(new WebAuthorization($routeParser, $acl, 'account', 'view')); # member; $group->get('/users', ControllerWebSystem::class . ':showUsers')->setName('users.show')->add(new WebAuthorization($routeParser, $acl, 'user', 'show')); # admin; $group->get('/user/new', ControllerWebSystem::class . ':newUser')->setName('user.new')->add(new WebAuthorization($routeParser, $acl, 'user', 'create')); # admin; - $group->get('/user/{username}', ControllerWebSystem::class . ':showUser')->setName('user.show')->add(new WebAuthorization($routeParser, $acl, 'user', 'show')); # admin;; + $group->get('/user/{username}', ControllerWebSystem::class . ':showUser')->setName('user.show')->add(new WebAuthorization($routeParser, $acl, 'user', 'show')); # admin; # Author Area $group->get('/content/visual[/{route:.*}]', ControllerWebAuthor::class . ':showBlox')->setName('content.visual')->add(new WebAuthorization($routeParser, $acl, 'mycontent', 'view')); + $group->get('/content/raw[/{route:.*}]', ControllerWebAuthor::class . ':showRaw')->setName('content.raw')->add(new WebAuthorization($routeParser, $acl, 'mycontent', 'view')); })->add(new WebRedirectIfUnauthenticated($routeParser)); @@ -42,103 +43,9 @@ $app->redirect('/tm/', $routeParser->urlFor('auth.show'), 302); # same with setup redirect + +# downloads +$app->get('/media/files[/{params:.*}]', ControllerWebDownload::class . ':download')->setName('download.file'); + # website -$app->get('/[{params:.*}]', ControllerWebFrontend::class . ':index')->setName('home'); - - - -/* -use Typemill\Controllers\ControllerAuthorEditor; -use Typemill\Controllers\ControllerSettings; -use Typemill\Controllers\ControllerDownload; -use Typemill\Controllers\ControllerFrontendForms; -use Typemill\Controllers\ControllerFrontendAuth; -use Typemill\Controllers\ControllerFrontendSetup; -use Typemill\Middleware\RedirectIfNoAdmin; -use Typemill\Middleware\accessMiddleware; - -if($settings['settings']['setup']) -{ - $app->get('/setup', ControllerFrontendSetup::class . ':show')->setName('setup.show'); - $app->post('/setup', ControllerFrontendSetup::class . ':create')->setName('setup.create'); -} -else -{ - $app->get('/setup', ControllerFrontendAuth::class . ':redirect'); -} -if($settings['settings']['welcome']) -{ - $app->get('/setup/welcome', ControllerFrontendSetup::class . ':welcome')->setName('setup.welcome')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); -} -else -{ - $app->get('/setup/welcome', ControllerFrontendAuth::class . ':redirect')->setName('setup.welcome'); -} - -$app->post('/tm/formpost', ControllerFrontendForms::class . ':savePublicForm')->setName('form.save'); - -$app->get('/tm', ControllerFrontendAuth::class . ':redirect'); -$app->get('/tm/logout', ControllerFrontendAuth::class . ':logout')->setName('auth.logout')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); - -if(isset($settings['settings']['recoverpw']) && $settings['settings']['recoverpw']) -{ - $app->get('/tm/recoverpw', ControllerFrontendAuth::class . ':showrecoverpassword')->setName('auth.recoverpwshow')->add(new RedirectIfAuthenticated($container['router'], $container['settings'])); - $app->post('/tm/recoverpw', ControllerFrontendAuth::class . ':recoverpassword')->setName('auth.recoverpw')->add(new RedirectIfAuthenticated($container['router'], $container['settings'])); - $app->get('/tm/recoverpwnew', ControllerFrontendAuth::class . ':showrecoverpasswordnew')->setName('auth.recoverpwshownew')->add(new RedirectIfAuthenticated($container['router'], $container['settings'])); - $app->post('/tm/recoverpwnew', ControllerFrontendAuth::class . ':createrecoverpasswordnew')->setName('auth.recoverpwnew')->add(new RedirectIfAuthenticated($container['router'], $container['settings'])); -} - - -/* - -MIGRATED - -$app->get('/tm/settings', ControllerSettings::class . ':showSettings')->setName('settings.show')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'view')); -$app->post('/tm/settings', ControllerSettings::class . ':saveSettings')->setName('settings.save')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'update')); -$app->get('/tm/themes', ControllerSettings::class . ':showThemes')->setName('themes.show')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'view')); -$app->post('/tm/themes', ControllerSettings::class . ':saveThemes')->setName('themes.save')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'update')); -$app->get('/tm/plugins', ControllerSettings::class . ':showPlugins')->setName('plugins.show')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'view')); -$app->post('/tm/plugins', ControllerSettings::class . ':savePlugins')->setName('plugins.save')->add(new accessMiddleware($container['router'], $container['acl'], 'system', 'update')); -$app->get('/tm/account', ControllerSettings::class . ':showAccount')->setName('user.account')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'view')); -$app->get('/tm/login', ControllerFrontendAuth::class . ':show')->setName('auth.show')->add(new RedirectIfAuthenticated($container['router'], $container['settings'])); -$app->post('/tm/login', ControllerFrontendAuth::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($container['router'], $container['settings'])); - - - -$app->get('/tm/content/raw[/{params:.*}]', ControllerAuthorEditor::class . ':showContent')->setName('content.raw')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view')); -$app->get('/tm/content/visual[/{params:.*}]', ControllerAuthorEditor::class . ':showBlox')->setName('content.visual')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view')); -$app->get('/tm/content[/{params:.*}]', ControllerAuthorEditor::class . ':showEmpty')->setName('content.empty')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view')); - -$app->get('/media/files[/{params:.*}]', ControllerDownload::class . ':download')->setName('download.file'); - -foreach($routes as $pluginRoute) -{ - $method = $pluginRoute['httpMethod']; - $route = $pluginRoute['route']; - $class = $pluginRoute['class']; - $resource = isset($pluginRoute['resource']) ? $pluginRoute['resource'] : NULL; - $privilege = isset($pluginRoute['privilege']) ? $pluginRoute['privilege'] : NULL; - - if(isset($pluginRoute['name'])) - { - $app->{$method}($route, $class)->setName($pluginRoute['name'])->add(new accessMiddleware($container['router'], $container['acl'], $resource, $privilege)); - } - else - { - $app->{$method}($route, $class)->add(new accessMiddleware($container['router'], $container['acl'], $resource, $privilege)); - } -} - -if($settings['settings']['setup']) -{ - $app->get('/[{params:.*}]', ControllerFrontendSetup::class . ':redirect'); -} -elseif(isset($settings['settings']['access']) && $settings['settings']['access'] != '') -{ - $app->get('/[{params:.*}]', ControllerFrontendWebsite::class . ':index')->setName('home')->add(new accessMiddleware($container['router'], $container['acl'], 'user', 'view')); -} -else -{ - $app->get('/[{params:.*}]', ControllerFrontendWebsite::class . ':index')->setName('home'); -} -*/ \ No newline at end of file +$app->get('/[{route:.*}]', ControllerWebFrontend::class . ':index')->setName('home'); \ No newline at end of file diff --git a/system/typemill/settings/metatabs.yaml b/system/typemill/settings/metatabs.yaml index 60cfcbf..a0633f2 100644 --- a/system/typemill/settings/metatabs.yaml +++ b/system/typemill/settings/metatabs.yaml @@ -3,7 +3,6 @@ meta: navtitle: type: text label: Navigation Title - class: large maxlength: 60 fieldsetcontent: type: fieldset @@ -13,12 +12,10 @@ meta: type: text label: Meta title maxlength: 100 - class: large description: type: textarea label: Meta description size: 160 - class: large description: If not filled, the description is extracted from content. heroimage: type: image @@ -27,20 +24,6 @@ meta: heroimagealt: type: text label: Alternative Text for the hero image - fieldsetvisibility: - type: fieldset - legend: Visibility - fields: - hide: - type: checkbox - label: Hide - checkboxlabel: Hide page from navigation - class: medium - noindex: - type: checkbox - label: Noindex - checkboxlabel: Add noindex tag and exclude from sitemap - class: medium fieldsetauthor: type: fieldset legend: Author @@ -48,12 +31,12 @@ meta: owner: type: text label: owner (username) - class: medium + css: w-half description: Has edit rights for this article. author: type: text label: author - class: medium + css: w-half description: Can be used for author line in frontend. fieldsetrights: type: fieldset @@ -61,15 +44,15 @@ meta: fields: allowedrole: type: select - label: For access the user must have this minimum role - class: medium + label: Minimum user-role to access this page + css: w-half dataset: userroles options: description: Select the lowest userrole. Higher roles will have access too. alloweduser: type: text label: Only the following users have access - class: medium + css: w-half description: Add one or more usernames separated with comma. fieldsetpubdate: type: fieldset @@ -82,23 +65,55 @@ meta: type: date label: Last modified live (readonly) readonly: readonly - class: medium + css: w-half description: Used as fallback when no manual date is set. created: type: date label: Created at (read only) readonly: readonly - class: medium + css: w-half time: type: text readonly: readonly hidden: true - class: hidden + css: hidden pattern: '[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' - contains: - type: radio - label: This folder contains - class: medium - options: - pages: PAGES (sort in navigation with drag & drop) - posts: POSTS (sorted by publish date, for news or blogs) \ No newline at end of file + 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 + fields: + hide: + type: checkbox + label: Hide + checkboxlabel: Hide page from navigation + css: w-half + noindex: + type: checkbox + label: Noindex + checkboxlabel: Add noindex tag and exclude from sitemap + css: w-half + fieldsetfolder: + type: fieldset + legend: Folder + fields: + contains: + type: radio + label: This folder contains + css: medium + options: + pages: PAGES (sort in navigation with drag & drop) + posts: POSTS (sorted by publish date, for news or blogs) \ No newline at end of file diff --git a/system/typemill/settings/system.yaml b/system/typemill/settings/system.yaml index 913a676..c1d3645 100644 --- a/system/typemill/settings/system.yaml +++ b/system/typemill/settings/system.yaml @@ -225,11 +225,11 @@ fieldsetdeveloper: type: checkbox label: Proxy checkboxlabel: Use x-forwarded-header. - css: w-half + css: w-full trustedproxies: type: text label: Trusted IPs for proxies (comma separated) - css: w-half + css: w-full headersoff: type: checkbox label: Disable headers diff --git a/system/typemill/system.php b/system/typemill/system.php index 81c3030..51ae6bf 100644 --- a/system/typemill/system.php +++ b/system/typemill/system.php @@ -5,30 +5,34 @@ use DI\Container; use Slim\Middleware\ErrorMiddleware; use Slim\Factory\AppFactory; -use Slim\Csrf\Guard; +# use Slim\Csrf\Guard; use Slim\Views\Twig; use Slim\Views\TwigMiddleware; use Slim\Psr7\Factory\UriFactory; -use Slim\Flash\Messages; use Twig\Extension\DebugExtension; use Symfony\Component\EventDispatcher\EventDispatcher; +use Typemill\Assets; use Typemill\Static\Settings; use Typemill\Static\Plugins; use Typemill\Static\Translations; use Typemill\Static\Permissions; -use Typemill\Static\Session; +# use Typemill\Static\Session; use Typemill\Static\Helpers; use Typemill\Events\OnSettingsLoaded; use Typemill\Events\OnPluginsLoaded; use Typemill\Events\OnSessionSegmentsLoaded; use Typemill\Events\OnRolesPermissionsLoaded; use Typemill\Events\OnResourcesLoaded; +use Typemill\Middleware\SessionMiddleware; use Typemill\Middleware\JsonBodyParser; use Typemill\Middleware\FlashMessages; +use Typemill\Middleware\AssetMiddleware; use Typemill\Extensions\TwigCsrfExtension; use Typemill\Extensions\TwigUrlExtension; use Typemill\Extensions\TwigUserExtension; -use Typemill\Models\StorageWrapper; +use Typemill\Extensions\TwigLanguageExtension; +use Typemill\Extensions\TwigMarkdownExtension; +# use Typemill\Models\StorageWrapper; use Typemill\Models\License; $timer = []; @@ -64,8 +68,8 @@ if(isset($settings['displayErrorDetails']) && $settings['displayErrorDetails']) # ADD THEM TO THE SETTINGS AND YOU HAVE THEM EVERYWHERE?? $uriFactory = new UriFactory(); -$uri = $uriFactory->createFromGlobals($_SERVER); -$urlinfo = Helpers::urlInfo($uri); +$uri = $uriFactory->createFromGlobals($_SERVER); +$urlinfo = Helpers::urlInfo($uri); $timer['settings'] = microtime(true); @@ -111,8 +115,8 @@ $settings['license'] = $license->getLicenseScope($urlinfo); * LOAD & UPDATE PLUGINS * ****************************/ -$plugins = Plugins::loadPlugins(); -$routes = []; +$plugins = Plugins::loadPlugins(); +$routes = []; $middleware = []; # if there are less plugins in the scan than in the settings, then a plugin has been removed @@ -149,17 +153,13 @@ foreach($plugins as $plugin) # if the plugin is activated, add routes/middleware and add plugin as event subscriber if(isset($settings['plugins'][$pluginName]['active']) && $settings['plugins'][$pluginName]['active']) { - $routes = Plugins::getNewRoutes($className, $routes); + $routes = Plugins::getNewRoutes($className, $routes); $middleware = Plugins::getNewMiddleware($className, $middleware); $dispatcher->addSubscriber(new $className($container)); } } -# echo '
';
-# print_r($settings);
-# die();
-
 # if plugins have been added or removed
 if(isset($updateSettings))
 {
@@ -173,8 +173,8 @@ $container->set('settings', function() use ($settings){ return $settings; });
 # dispatch the event onPluginsLoaded
 $dispatcher->dispatch(new OnPluginsLoaded($plugins), 'onPluginsLoaded');
 
-# dispatch settings event and get all setting-updates from plugins
-$dispatcher->dispatch(new OnSettingsLoaded($settings), 'onSettingsLoaded')->getData();
+# dispatch settings event
+$dispatcher->dispatch(new OnSettingsLoaded($settings), 'onSettingsLoaded');
 
 $timer['plugins'] = microtime(true);
 
@@ -220,7 +220,7 @@ else
 }
 
 # start session
-Session::startSessionForSegments($session_segments, $urlinfo['route']);
+# Session::startSessionForSegments($session_segments, $urlinfo['route']);
 
 $timer['session segments'] = microtime(true);
 
@@ -236,10 +236,13 @@ $container->set('translations', $translations);
 $container->set('dispatcher', function() use ($dispatcher){ return $dispatcher; });
 
 # asset function for plugins
-$container->set('assets', function() use ($urlinfo){ return new \Typemill\Assets($urlinfo['basepath']); });
+$assets = new \Typemill\Assets($urlinfo['basepath']);
+$container->set('assets', function() use ($assets){ return $assets; });
 
 # Register Middleware On Container
 $csrf = false;
+
+/*
 if(isset($_SESSION))
 {
 	# add flash messsages
@@ -247,12 +250,23 @@ if(isset($_SESSION))
 
 	# Register Middleware On Container
 	$csrf = new Guard($responseFactory);
+	$csrf->setPersistentTokenMode(true);
+	$request = $responseFactory->;
+	$csrf->setFailureHandler(
+		function (ServerRequestInterface $request, RequestHandlerInterface $handler)
+		{
+			$request = $request->withAttribute("csrf_status", false);
+			return $handler->handle($request);
+		}
+	);
 	$container->set('csrf', function () use ($csrf){ return $csrf; });
 
 	# Add Validation Errors Middleware
 	# $app->add(new ValidationErrors($container->get('view')));
 }
 
+*/
+
 /****************************
 * TWIG TO CONTAINER					*
 ****************************/
@@ -261,34 +275,37 @@ $container->set('view', function() use ($settings, $csrf, $urlinfo, $translation
 
 	$twig = Twig::create(
 		[
-			# path to templates
-			$settings['rootPath'] . $settings['authorFolder'],
+			# path to templates with namespaces
 			$settings['rootPath'] . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $settings['theme'],
+			$settings['rootPath'] . $settings['authorFolder'],
 		],
 		[
 			# settings
 			'cache' => ( isset($settings['twigcache']) && $settings['twigcache'] ) ? $settings['rootPath'] . '/cache/twig' : false,
-			'debug' => isset($settings['displayErrorDetails'])
+			'debug' => isset($settings['displayErrorDetails']),
+			'autoescape' => false
 		]
 	);
 	
 	$twig->getEnvironment()->addGlobal('errors', NULL);
 	$twig->getEnvironment()->addGlobal('flash', NULL);
+	$twig->getEnvironment()->addGlobal('assets', NULL);
 
 	# add extensions
 	$twig->addExtension(new DebugExtension());
 	$twig->addExtension(new TwigUserExtension());
 	$twig->addExtension(new TwigUrlExtension($urlinfo));
-
-	# $twig->addExtension(new \Nquire\Extensions\TwigUserExtension());
+	$twig->addExtension(new TwigLanguageExtension( $translations ));
+	$twig->addExtension(new TwigMarkdownExtension());
 
 	# start csrf only if session is active
+	/*
 	if($csrf)
 	{
 		$twig->addExtension(new TwigCsrfExtension($csrf));
 	}
+	*/
 
-	$twig->addExtension(new Typemill\Extensions\TwigLanguageExtension( $translations ));
 
 	return $twig;
 
@@ -296,20 +313,12 @@ $container->set('view', function() use ($settings, $csrf, $urlinfo, $translation
 
 
 /****************************
-* MIDDLEWARE								*
+* MIDDLEWARE				*
 ****************************/
 
-if(isset($_SESSION))
-{
-	# Add Validation Errors Middleware
-	#$app->add(new ValidationErrors($container->get('view')));
+$app->add(new AssetMiddleware($assets, $container->get('view')));
 
-	# Add Flash Messages Middleware
-	$app->add(new FlashMessages($container->get('view')));
-
-	# Add csrf middleware globally
-	$app->add('csrf');
-}
+$app->add(new FlashMessages($container));
 
 # Add Twig-View Middleware
 $app->add(TwigMiddleware::createFromContainer($app));
@@ -340,6 +349,8 @@ $errorMiddleware->setErrorHandler(HttpNotFoundException::class, function ($reque
 
 $app->add($errorMiddleware);
 
+$app->add(new SessionMiddleware($session_segments, $urlinfo['route']));
+
 $timer['middleware'] = microtime(true);
 
 /************************
@@ -357,239 +368,6 @@ $timer['routes'] = microtime(true);
 
 $app->run();
 
-$timer['run'] = microtime(true);
+# $timer['run'] = microtime(true);
 
-# Typemill\Static\Helpers::printTimer($timer);
-
-die();
-die('After app run');
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-/********************************
-*  MOVE TO MIDDLEWARE NEXT TIME *
-********************************/
-
-print_r($session_segments);
-
-$trimmedRoute = ltrim($routepath,'/');
-
-foreach($session_segments as $segment)
-{
-
-  $test = substr( $trimmedRoute, 0, strlen($segment) );
-
-  # echo '
' . $test . ' = ' . $segment; -# continue; - - if(substr( $uri->getPath(), 0, strlen($segment) ) === ltrim($segment, '/')) - { - // configure session - ini_set('session.cookie_httponly', 1 ); - ini_set('session.use_strict_mode', 1); - ini_set('session.cookie_samesite', 'lax'); - if($uri->getScheme() == 'https') - { - ini_set('session.cookie_secure', 1); - session_name('__Secure-typemill-session'); - } - else - { - session_name('typemill-session'); - } - - // add csrf-protection - $container['csrf'] = function ($c) - { - $guard = new \Slim\Csrf\Guard(); - $guard->setPersistentTokenMode(true); - $guard->setfailurecallable(function ($request, $response, $next) - { - $request = $request->withattribute("csrf_result", false); - return $next($request, $response); - }); - - return $guard; - }; - - // add flash to container - $container['flash'] = function () - { - return new \Slim\Flash\Messages(); - }; - - // start session - session_start(); - } -} - - - -Typemill\Static\Helpers::printTimer($timer); - -die('Typemill 2 is comming'); - - - - - - - - -# add flash messsages -$container->set('flash', function(){ - return new Messages(); -}); - -# Register Middleware On Container -$container->set('csrf', function () use ($responseFactory) { - return new Guard($responseFactory); -}); - -# Set view in Container -$container->set('view', function() use ($container) { - - $twig = Twig::create(__DIR__ . DIRECTORY_SEPARATOR . 'views',['cache' => false, 'debug' => true]); - - $twig->getEnvironment()->addGlobal('errors', NULL); - $twig->getEnvironment()->addGlobal('flash', NULL); - - $twig->addExtension(new \Twig\Extension\DebugExtension()); - $twig->addExtension(new \Nquire\Extensions\TwigUserExtension()); - $twig->addExtension(new \Nquire\Extensions\TwigCsrfExtension($container->get('csrf'))); - - return $twig; -}); - -/**************************** -* SET ROUTE PARSER TO USE NAMED ROUTES IN CONTROLLER * -****************************/ - -$container->set('routeParser', $routeParser); - -/**************************** -* MIDDLEWARE * -****************************/ - -# Add Validation Errors Middleware -$app->add(new ValidationErrors($container->get('view'))); - -# Add Flash Messages Middleware -$app->add(new FlashMessages($container->get('view'))); - -# Add csrf middleware globally -$app->add('csrf'); - -# Add Twig-View Middleware -$app->add(TwigMiddleware::createFromContainer($app)); - -# add JsonBodyParser Middleware -$app->add(new JsonBodyParser()); - -/** - * The routing middleware should be added earlier than the ErrorMiddleware - * Otherwise exceptions thrown from it will not be handled by the middleware - */ -$app->addRoutingMiddleware(); - -/** - * Add Error Middleware - * - * @param bool $displayErrorDetails -> Should be set to false in production - * @param bool $logErrors -> Parameter is passed to the default ErrorHandler - * @param bool $logErrorDetails -> Display error details in error log - * @param LoggerInterface|null $logger -> Optional PSR-3 Logger - * - * Note: This middleware should be added last. It will not handle any exceptions/errors - * for middleware added after it. - */ - -# $errorMiddleware = $app->addErrorMiddleware(true, true, true); - -$errorMiddleware = new ErrorMiddleware( - $app->getCallableResolver(), - $app->getResponseFactory(), - true, - false, - false -); - -# Set the Not Found Handler -$errorMiddleware->setErrorHandler(HttpNotFoundException::class, function ($request, $exception) use ($container) { - - $response = new NewResponse(); - - return $container->get('view')->render($response->withStatus(404), 'errors/404.twig'); - -}); - -$app->add($errorMiddleware); - -/* - -# Set the Not Found Handler -$errorMiddleware->setErrorHandler( - HttpNotFoundException::class, - function (ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails) { - $response = new Response(); - $response->getBody()->write('404 NOT FOUND'); - - return $response->withStatus(404); - } -); - -# Set the Not Allowed Handler -$errorMiddleware->setErrorHandler( - HttpMethodNotAllowedException::class, - function (ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails) { - $response = new Response(); - $response->getBody()->write('405 NOT ALLOWED'); - - return $response->withStatus(405); - } -); - -# Set the Not Found Handler -$errorMiddleware->setErrorHandler( - HttpNotFoundException::class, - function () { - die('not found'); - } -); - -$app->add($ErrorMiddleware); - -*/ - -/************************ -* ADD ROUTES * -************************/ - -require __DIR__ . '/routes/api.php'; -require __DIR__ . '/routes/web.php'; - -$app->run(); \ No newline at end of file +# Typemill\Static\Helpers::printTimer($timer); \ No newline at end of file diff --git a/themes/cyanine/cyanine.yaml b/themes/cyanine/cyanine.yaml index 0dc7d8b..0f9a11a 100644 --- a/themes/cyanine/cyanine.yaml +++ b/themes/cyanine/cyanine.yaml @@ -534,11 +534,9 @@ forms: metatabs: meta: fields: - fieldsetGlossary: - type: fieldset - legend: Glossary Style + fieldsetfolder: fields: glossary: type: checkbox - label: Glossary List (only for folders) + label: Glossary List (cyanine theme) checkboxlabel: List pages or posts of this folder as glossary (only for folders) \ No newline at end of file diff --git a/themes/cyanine/home.twig b/themes/cyanine/home.twig index b2ffbe6..f151b05 100644 --- a/themes/cyanine/home.twig +++ b/themes/cyanine/home.twig @@ -1,5 +1,3 @@ -

HOME from theme

- {% set home = { "landingpageIntro" : settings.themes.cyanine.landingpageIntro, "landingpageInfo" : settings.themes.cyanine.landingpageInfo, diff --git a/themes/cyanine/partials/footer.twig b/themes/cyanine/partials/footer.twig index 90e4a3e..300cde7 100644 --- a/themes/cyanine/partials/footer.twig +++ b/themes/cyanine/partials/footer.twig @@ -24,6 +24,6 @@ {% else %} {% set copyrightYears = settings.year ~ ' - ' ~ nowYear %} {% endif %} -

{{ settings.copyright }} {{ __('by') }} {{ settings.author }}, {{ copyrightYears }}. {{ __('All Rights Reserved') }}. {{ __('Built with') }} Typemill.

+

{{ settings.copyright }} {{ translate('by') }} {{ settings.author }}, {{ copyrightYears }}. {{ translate('All Rights Reserved') }}. {{ translate('Built with') }} Typemill.

{% endif %} \ No newline at end of file