diff --git a/.gitignore b/.gitignore
index 3e7280a..2c4d526 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,4 @@
-cache/lastCache.txt
-cache/lastSitemap.txt
-cache/metatabs.yaml
-cache/navigation.txt
cache/sitemap.xml
-cache/structure-draft.txt
-cache/structure-extended.yaml
-cache/structure.txt
content/index.yaml
content/00-welcome/index.yaml
content/00-welcome/00-setup.yaml
@@ -17,12 +10,15 @@ content/01-cyanine-theme/00-landingpage.yaml
content/01-cyanine-theme/01-colors-and-fonts.yaml
content/01-cyanine-theme/02-footer.yaml
content/01-cyanine-theme/03-content-elements.yaml
+cypress
+data/navigation
+data/css
+node_modules
+plugins/demo
+plugins/search
settings/settings.yaml
settings/license.yaml
settings/users
system/vendor
-plugins/demo
-plugins/search
zips
-build.php
-node_modules
\ No newline at end of file
+cypress.json
\ No newline at end of file
diff --git a/.htaccesspublic b/.htaccesspublic
deleted file mode 100644
index 659993e..0000000
--- a/.htaccesspublic
+++ /dev/null
@@ -1,3 +0,0 @@
-RewriteEngine on
-RewriteRule ^$ public/ [L]
-RewriteRule (.*) public/$1 [L]
diff --git a/cache/cyanine-custom.css b/cache/cyanine-custom.css
deleted file mode 100644
index e370893..0000000
--- a/cache/cyanine-custom.css
+++ /dev/null
@@ -1,30 +0,0 @@
-.landingpageintro h1{
-display: inline-block;
-background: white;
-padding: 4px 10px;
-margin: 5px;
-}
-
-.landingpageintro p{
-display: inline;
-background: white;
-line-height: 2.1rem;
-padding: 6px 4px 4px;
-}
-article a[href^="http"]::after,
-article a[href^="https://"]::after
-{
- content: "";
- width: 11px;
- height: 11px;
- margin-left: 4px;
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z'/%3E%3Cpath fill-rule='evenodd' d='M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z'/%3E%3C/svg%3E");
- background-position: center;
- background-repeat: no-repeat;
- background-size: contain;
- display: inline-block;
-}
-a[href^="https://www.electrictoolbox.com"] {
- background: none;
- padding-right: 0;
-}
\ No newline at end of file
diff --git a/content/00-welcome/00-setup-your-website.md b/content/00-welcome/00-setup-your-website.md
index 7f2d72c..13a3396 100644
--- a/content/00-welcome/00-setup-your-website.md
+++ b/content/00-welcome/00-setup-your-website.md
@@ -4,26 +4,13 @@ Typemill provides detailed settings, and you have access to nearly all settings
{#7yvlwXJL9dc .youtube}
-You will find all configurations and settings under the main navigation point `settings` with the following sub-navigation:
+You will find all configurations and settings under the main navigation point `system` with the following sub-navigation:
-* System settings
-* Theme settings
-* Plugin settings
-* User settings
+* System
+* Themes
+* Plugins
+* Account
+* Users
All settings are stored in the `\settings` folder of Typemill. It is not recommended to edit the settings manually, because it might crash the system if done wrong.
-## Developer Settings
-
-As of version 1.4.0 you will find some advanced developer settings in the author panel under `settings`. See the details below.
-
-! **Only for devs**
-!
-! These options are for developers only. Make sure that you fully understand what happens. For example, you should never activate the error reporting on live systems because this is a security risk.
-
-* **Error Reporting**: You can switch the error reporting of the slim-framework on and off here. This can be helpful for bug-analysis, but you should NEVER switch it on (or keep it active) on a productive system.
-* **Twig cache**: You can activate the cache for the twig templates. This will speed up the page rendering a bit, but it can also produce a headace if you changed something in your theme. The best option is to clear the cache if something does not work.
-* **Clear cache**: This will clear the cache for Twig templates and delete all cache files of Typemill. If you clear the cache, then some details might not work or look strange, for example the navigation is set back to the original state. Everything will work again when the cache has been rebuild. This happens every 10 minutes. If you want to spead up the process, then refresh your browser cache with F12 on windows machines, because it will also trigger the recreation of the Typemill cache.
-* **Image sizes**: All images in the content area will be resized to 820px width. If you want to change it, then add another value in the width-field. If you additionally add a height for your images, then the images will be resized first and then cropped to the correct aspect ratio.
-* **Proxy**: If you run Typemill behind a proxy (which is a common usecase in companies), then you can activate the proxy detection. This will read the `X-Forwarded-Proto`, `X-Forwarded-Host` and `X-Forwarded-Port` Headers and return the html with the correct urls. Optionally you can also add a comma separated list of trusted IP-addresses.
-
diff --git a/content/00-welcome/01-manage-access.md b/content/00-welcome/01-manage-access.md
index 64c58e1..a7a8219 100644
--- a/content/00-welcome/01-manage-access.md
+++ b/content/00-welcome/01-manage-access.md
@@ -1,17 +1,17 @@
# Manage Access
-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:
+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 tab "restrictions". 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:
{#UW_m-4g1kAA .youtube}
## Restrict Access for the Website
-This feature is perfect, if you want to lock down the whole website and only grant access for authenticated users. All non-authenticated users will be redirected to the login-page. There are two main use cases for this feature:
+This feature is handy, if you want to lock down the whole website and only grant access for authenticated users. All non-authenticated users will be redirected to the login-page. There are two main use cases for this feature:
* **Launch the website later**: You want to create your website first and launch it to the public later, for example if you have finished the website design or if you have polished your content.
-* **Share website internally**: You want to share your typemill website only with certain users, for example with the company stuff or only with the members of your it-unit.
+* **Share website internally**: You want to share your typemill website only with certain users, for example with the company stuff, or only with the members of your business-unit.
-You can activate the feature with a simple checkbox under "Website Restrictions".
+You can activate the feature with a simple checkbox under "website restrictions".
## Restrict Access for Pages
@@ -20,7 +20,7 @@ If you need a more fine-tuned access and if you want to restrict access only for
* **Minimum role for access**: Here you can select a miminum role that the user needs to view the page content. Be aware that the roles have a hierarchy, so if you choose the role "author", then the "editor" will also have access.
* **Usernames**: Here you can add one or more usernames (separated with comma) that have access to this page.
-If you don't choose anything of it, then the page has no restrictions and everybody can see the content.
+If you don't choose anything, then the page has no restrictions and everybody can see the content.
You have some more features in the settings area:
diff --git a/content/00-welcome/01-manage-access.yaml b/content/00-welcome/01-manage-access.yaml
index d11743e..c232a7b 100644
--- a/content/00-welcome/01-manage-access.yaml
+++ b/content/00-welcome/01-manage-access.yaml
@@ -4,7 +4,7 @@ meta:
description: ' Restrict Access for the Website'
heroimage: null
heroimagealt: null
- owner: Sebastian
+ owner: trendschau
author: null
allowedrole: null
alloweduser: null
diff --git a/content/00-welcome/02-write-content.md b/content/00-welcome/02-write-content.md
index e72f7d0..778a947 100644
--- a/content/00-welcome/02-write-content.md
+++ b/content/00-welcome/02-write-content.md
@@ -1,6 +1,13 @@
# 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.
+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.
+* With the **raw markdown editor** you can write markdown syntax in textarea.
+* The **publish bar** gives you full control over the status of each page.
+
+Watch the following video tutorial to learn all the details.
{#6I2-uV88GkE .youtube}
diff --git a/content/00-welcome/02-write-content.yaml b/content/00-welcome/02-write-content.yaml
index 6db7724..bf48673 100644
--- a/content/00-welcome/02-write-content.yaml
+++ b/content/00-welcome/02-write-content.yaml
@@ -2,17 +2,9 @@ meta:
navtitle: 'write content'
title: 'Write Content'
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'
- heroimage: null
- heroimagealt: null
- owner: Sebastian
- author: null
- allowedrole: null
- alloweduser: null
- manualdate: null
+ owner: trendschau
modified: '2023-05-11'
created: '2023-06-12'
time: 22-09-48
- reference: null
- referencetype: null
- hide: true
+ hide: false
noindex: false
diff --git a/content/00-welcome/05-todos.md b/content/00-welcome/05-todos.md
deleted file mode 100644
index 9baecea..0000000
--- a/content/00-welcome/05-todos.md
+++ /dev/null
@@ -1,200 +0,0 @@
-# ToDos Version 2
-
-[TOC]
-
-## System settings
-
-* DONE: Migrate from backend to frontend with vue and api
-* DONE: Redesign
-* DONE: License feature
-* DONE: Enhance with plugins
-
-## Visual Editor
-
-* DONE: Refactor and redesign
-* DONE: Fix toc component in new block
-* DONE: Fix hr component in new block
-* DONE: finish shortcode component
-* DONE: Fix inline formats
-* DONE: fix lenght of page
-* DONE: Fix design of new block at the end (background color)
-* DONE: Move Block
-* DONE: Fix headline design
-* DONE: Fix save on two enter
-* DONE: fix quote design
-* DONE: Fix toc preview
-* DONE: disable enable
-* DONE: Add load sign (from navigation)
-* DONE: File is not published from tmp to media/files if you save the block.
-
-## Raw Editor
-
-* DONE: Refactor and redesign
-* DONE: Integrate highlighting
-
-## Navigation
-
-* DONE: Refactor and redesign
-* DONE: fix status in navigation
-* DONE: refresh navigation after changes
-
-## Publish Controller
-
-* DONE: Refactor and redesign
-* DONE: Create
-* DONE: publish
-* DONE: unpublish
-* DONE: discard
-* DONE: delete
-* DONE: save draft
-* DONE: switch to raw
-
-## Meta Tabs
-
-* DONE: Refactor and redesign
-* DONE: Enhance with plugins
-
-## Medialib
-
-* DONE: Refactor and redesign
-
-## Posts
-
-* DONE: Refactor and redesign
-
-## Plugins
-
-* Asset Class in progress
-
-## Frontend
-
-* DONE: Refactor
-* DONE: Test restrictions
-
-## Other big tasks
-
-* DONE: System setup
-* DONE: Recover Password
-
-## Medium tasks
-
-* DONE: Merge processAssets modell
-* DONE: Table of content duplicated for published pages
-* DONE: Session handling: csrf fail and session start error if restrictions are active
-* DONE: Image and files for meta
-
-## Open tasks
-
-* DONE: Sitemap and ping
-* DONE: Version check
-* DONE: Proxy support
-* DONE: SVG checker: https://github.com/TribalSystems/SVG-Sanitizer
-* DONE: Backend form builder
-* DONE: Image generation on the fly
-* DONE: Delete folder in base level
-* DONE: Make folder delete easier with glob or scandir
-* DONE: fix error messages (check models)
-* DONE: error status codes (check middleware)
-* DONE: Warn if open another block
-* DONE: Customfields not styled yet
-* DOING: Fix error api systemnavi + validate
-* FIXED: System stores html or sends wrong error messsages
-* FIXED: Wrong frontend navigation if unpublished pages
-* DONE: Icon for hidden pages
-* DOING: Responsive design
-* DONE: Captcha integration
-* DONE: Solution for logo and favicon
-* FIXED: Raw editor jumps if you edit long text at the end
-* DONE: Typemill Utilities
-* DONE: Update CSS for themes
-* DONE: test with different user rights
-* Markdown secure rendering
-* finish youtube component
-* BUG: Error fields in account form not styled correctly
-* BUG: Codefield jumps on editing
-* False for owner on live?
-
-## Dark Mode
-
-* DONE: system
-* DONE: content-navigation
-* DONE: visual editor preview
-* DONE: visual editor edit modes
-* DONE: raw editor
-* DONE: meta
-* DONE: other tabs
-* DONE: modals
-* DONE: medialib
-* DONE: publish-bar.
-
-## Feedback GitHub
-
-* FIXED: Website restriction
-* NO ERROR: Change slug of blog
-* FIXED: undefined array key "title" in TwigMetaExtension on line 25
-* FIXED: CSS for navigation
-* DONE: Test with 8.2.7 (deprecation reports)
-* NOT REPRODUCED: Meta from home folder?
-* automatic generated password in firefox
-* FIXED: upload hero image in landinpage
-* FIXED: Restriction for custom css to 10000 characters
-* NOT REPRODUCED: Custom css löschen => false
-
-## later
-
-* Handle formdata centrally ???
-* Reference Feature
-* Clear cache
-* Show security Log
-* User search only for +10 users
-* For api translations should be done completely in backoffice
-* Change translation files so they are loaded in settings instead of adding them manually to settings-defaults.yaml
-
-## Cleanups:
-
-* DONE: Events
-* DONE: Error messages
-* DONE: Translations
-
-## Info: Select userroles
-
-* Userroles for file restriction: in vue-blox-components loaded via api
-* Userroles for userfields: in php model user getUserFields()
-* Userroles for meta: in php controller apiAuthorMeta getMeta()
-* Plugins and themes: in php model extension getThemeDefinitions()
-
-## Info: License Check
-
-* On activation in apiControllerExtension. It checks the license in yaml.
-* In plugin php code with setPremiumLicense
-* In static plugins, it checks manual premium list and method setPremiumLicense and more
-
-## Plugins
-
-* MAKER: Rebuild search
-* MAKER: Rebuild contactform with shortcode
-
-## Status codes
-
-| Status code | Description |
-|---|---|
-| 200 ok | cell |
-| 400 bad request | The request was unacceptable due to missing or invalid parameter. |
-| 401 unauthorized | The request requires an authorization. |
-| (402 request failed) | The parameters where there but the request failed for other reasons. |
-| 403 forbidden | The user is authenticated but he has not enough rights. |
-| 404 not found | new |
-| 500 internal server error | new |
-
-## Upgrade
-
-* Switch server to php 8.0 at least
-* Delete content of system folders
-* Upload new content of system folder with folders typemill and vendor
-* Backup and delete settings file
-* upload new index.php file
-* Upload new htaccess file
-* Delete theme folder
-* Uplload new cyanine-theme
-* Deactivate and delete all plugins.
-
diff --git a/content/00-welcome/05-todos.txt b/content/00-welcome/05-todos.txt
deleted file mode 100644
index 263d190..0000000
--- a/content/00-welcome/05-todos.txt
+++ /dev/null
@@ -1 +0,0 @@
-["# ToDos Version 2","[TOC]","## System settings","* DONE: Migrate from backend to frontend with vue and api\n* DONE: Redesign\n* DONE: License feature\n* DONE: Enhance with plugins","## Visual Editor ","* DONE: Refactor and redesign\n* DONE: Fix toc component in new block\n* DONE: Fix hr component in new block\n* DONE: finish shortcode component\n* DONE: Fix inline formats\n* DONE: fix lenght of page\n* DONE: Fix design of new block at the end (background color)\n* DONE: Move Block\n* DONE: Fix headline design\n* DONE: Fix save on two enter\n* DONE: fix quote design\n* DONE: Fix toc preview\n* DONE: disable enable \n* DONE: Add load sign (from navigation)\n* DONE: File is not published from tmp to media\/files if you save the block.","## Raw Editor","* DONE: Refactor and redesign\n* DONE: Integrate highlighting","## Navigation","* DONE: Refactor and redesign\n* DONE: fix status in navigation\n* DONE: refresh navigation after changes","## Publish Controller","* DONE: Refactor and redesign\n* DONE: Create \n* DONE: publish\n* DONE: unpublish\n* DONE: discard\n* DONE: delete\n* DONE: save draft\n* DONE: switch to raw","## Meta Tabs","* DONE: Refactor and redesign\n* DONE: Enhance with plugins","## Medialib","* DONE: Refactor and redesign","## Posts","* DONE: Refactor and redesign","## Plugins","* Asset Class in progress","## Frontend","* DONE: Refactor\n* DONE: Test restrictions","## Other big tasks","* DONE: System setup\n* DONE: Recover Password","## Medium tasks","* DONE: Merge processAssets modell\n* DONE: Table of content duplicated for published pages\n* DONE: Session handling: csrf fail and session start error if restrictions are active\n* DONE: Image and files for meta","## Open tasks","* DONE: Sitemap and ping\n* DONE: Version check\n* DONE: Proxy support\n* DONE: SVG checker: https:\/\/github.com\/TribalSystems\/SVG-Sanitizer\n* DONE: Backend form builder\n* DONE: Image generation on the fly\n* DONE: Delete folder in base level\n* DONE: Make folder delete easier with glob or scandir\n* DONE: fix error messages (check models)\n* DONE: error status codes (check middleware)\n* DONE: Warn if open another block\n* DONE: Customfields not styled yet\n* DOING: Fix error api systemnavi + validate\n* FIXED: System stores html or sends wrong error messsages\n* FIXED: Wrong frontend navigation if unpublished pages\n* DONE: Icon for hidden pages\n* DOING: Responsive design\n* DONE: Captcha integration\n* DONE: Solution for logo and favicon\n* FIXED: Raw editor jumps if you edit long text at the end\n* DONE: Typemill Utilities\n* DONE: Update CSS for themes\n* DONE: test with different user rights\n* Markdown secure rendering\n* finish youtube component\n* BUG: Error fields in account form not styled correctly\n* BUG: Codefield jumps on editing\n* False for owner on live?","## Dark Mode","* DONE: system \n* DONE: content-navigation\n* DONE: visual editor preview\n* DONE: visual editor edit modes\n* DONE: raw editor\n* DONE: meta\n* DONE: other tabs\n* DONE: modals\n* DONE: medialib\n* DONE: publish-bar.","## Feedback GitHub","* FIXED: Website restriction\n* NO ERROR: Change slug of blog\n* FIXED: undefined array key \"title\" in TwigMetaExtension on line 25\n* FIXED: CSS for navigation\n* DONE: Test with 8.2.7 (deprecation reports)\n* NOT REPRODUCED: Meta from home folder?\n* automatic generated password in firefox\n* FIXED: upload hero image in landinpage\n* FIXED: Restriction for custom css to 10000 characters\n* NOT REPRODUCED: Custom css l\u00f6schen => false","## later","* Handle formdata centrally ???\n* Reference Feature\n* Clear cache\n* Show security Log\n* User search only for +10 users\n* For api translations should be done completely in backoffice\n* Change translation files so they are loaded in settings instead of adding them manually to settings-defaults.yaml","## Cleanups:","* DONE: Events\n* DONE: Error messages\n* DONE: Translations","## Info: Select userroles","* Userroles for file restriction: in vue-blox-components loaded via api\n* Userroles for userfields: in php model user getUserFields()\n* Userroles for meta: in php controller apiAuthorMeta getMeta()\n* Plugins and themes: in php model extension getThemeDefinitions()","## Info: License Check","* On activation in apiControllerExtension. It checks the license in yaml.\n* In plugin php code with setPremiumLicense\n* In static plugins, it checks manual premium list and method setPremiumLicense and more ","## Plugins","* MAKER: Rebuild search\n* MAKER: Rebuild contactform with shortcode\n* MAKER: SEO\n* REQUIRED DOING: Directory Plugin\n* REQUIRED: Directory Theme\n* REQUIRED: Download\n* REQUIRED DONE: Version\n* REQUIRED DONE: Mailerlite \n* DONE: Search\n* DO: Analytics\n* DO: Contactform\n* DO: ebookproducts\n* DO: ebooks\n* DO: mail\n* DO: math\n* DO: register\n* DO: rss","## Status codes","| Status code | Description | \n|---|---|\n| 200 ok | cell | \n| 400 bad request | The request was unacceptable due to missing or invalid parameter. | \n| 401 unauthorized | The request requires an authorization. | \n| (402 request failed) | The parameters where there but the request failed for other reasons. | \n| 403 forbidden | The user is authenticated but he has not enough rights. | \n| 404 not found | new | \n| 500 internal server error | new |","## Upgrade","* Switch server to php 8.0 at least\n* Delete content of system folders\n* Upload new content of system folder with folders typemill and vendor\n* Backup and delete settings file \n* upload new index.php file\n* Upload new htaccess file\n* Delete theme folder\n* Uplload new cyanine-theme\n* Deactivate and delete all plugins."]
\ No newline at end of file
diff --git a/content/00-welcome/05-todos.yaml b/content/00-welcome/05-todos.yaml
deleted file mode 100644
index 226f61f..0000000
--- a/content/00-welcome/05-todos.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-meta:
- navtitle: 'To Dos'
- title: 'A list of open tasks'
- description: ' Visual Editor with more stuff'
- heroimage: ''
- heroimagealt: ''
- owner: 'Sebastian, testauthor'
- author: ''
- allowedrole: ''
- modified: '2023-06-19'
- created: '2023-06-19'
- time: 15-15-01
- hide: false
- noindex: false
diff --git a/data/css/cyanine-custom.css b/data/css/cyanine-custom.css
deleted file mode 100644
index c4ab8ec..0000000
--- a/data/css/cyanine-custom.css
+++ /dev/null
@@ -1,30 +0,0 @@
-.landingpageintro h1{
-display: inline-block;
-background: white;
-padding: 4px 10px;
-margin: 5px;
-}
-
-.landingpageintro p{
-display: inline;
-background: white;
-line-height: 2.1rem;
-padding: 6px 4px 4px;
-}
-article a[href^="http"]::after,
-article a[href^="https://"]::after
-{
- content: "";
- width: 11px;
- height: 11px;
- margin-left: 4px;
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z'/%3E%3Cpath fill-rule='evenodd' d='M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z'/%3E%3C/svg%3E");
- background-position: center;
- background-repeat: no-repeat;
- background-size: contain;
- display: inline-block;
-}
-a[href^="https://www.electrictoolbox.com"] {
- background: none;
- padding-right: 0;
-}
\ No newline at end of file
diff --git a/data/demo/demotest.yaml b/data/demo/demotest.yaml
deleted file mode 100644
index 47935e0..0000000
--- a/data/demo/demotest.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-title: thank
-subtitle: 'another php book'
-author: chiko
-edition: new
diff --git a/data/ebookproducts/ebookproducts.yaml b/data/ebookproducts/ebookproducts.yaml
deleted file mode 100644
index 9e11745..0000000
--- a/data/ebookproducts/ebookproducts.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-flatfilecms:
- title: 'Flat File CMS for simple projects'
- cover: media/live/cover-report.png
- description: 'Another publication from cmsstash that will uncover the secrets of database-less web publishing. Read about all flat file cms and make an informed choice.'
- downloadlabel: ''
- downloadurl: ''
- firstbuttonlabel: ''
- firstbuttonurl: ''
- secondbuttonlabel: ''
- secondbuttonurl: ''
- downloadlabel1: 'Download now'
- downloadlabel2: 'Buy on amazon'
-enterprisecms:
- title: 'Das CMS Drupal: Open Source für Enterprise'
- cover: ''
- description: ''
- downloadlabel: ''
- downloadurl: ''
- firstbuttonlabel: ''
- firstbuttonurl: ''
- secondbuttonlabel: ''
- secondbuttonurl: ''
diff --git a/data/navigation/navi-draft.txt b/data/navigation/navi-draft.txt
index 1a9c613..82a3b37 100644
--- a/data/navigation/navi-draft.txt
+++ b/data/navigation/navi-draft.txt
@@ -1 +1 @@
-a:2:{i:0;O:8:"stdClass":22:{s:12:"originalName";s:10:"00-welcome";s:11:"elementType";s:6:"folder";s:8:"contains";s:5:"pages";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"00";s:4:"name";s:7:"welcome";s:4:"slug";s:7:"welcome";s:4:"path";s:11:"/00-welcome";s:15:"pathWithoutType";s:17:"/00-welcome/index";s:9:"urlRelWoF";s:8:"/welcome";s:6:"urlRel";s:17:"/typemill/welcome";s:6:"urlAbs";s:33:"http://localhost/typemill/welcome";s:3:"key";i:0;s:7:"keyPath";i:0;s:12:"keyPathArray";a:1:{i:0;s:1:"0";}s:7:"chapter";i:1;s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:13:"folderContent";a:6:{i:0;O:8:"stdClass":20:{s:12:"originalName";s:24:"00-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:"00";s:4:"name";s:18:"setup your website";s:4:"slug";s:18:"setup-your-website";s:4:"path";s:36:"/00-welcome/00-setup-your-website.md";s:15:"pathWithoutType";s:33:"/00-welcome/00-setup-your-website";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: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: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:19:"02-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:"02";s:4:"name";s:13:"write content";s:4:"slug";s:13:"write-content";s:4:"path";s:31:"/00-welcome/02-write-content.md";s:15:"pathWithoutType";s:28:"/00-welcome/02-write-content";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: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:1;s:7:"noindex";b:0;}i:3;O:8:"stdClass":20:{s:12:"originalName";s:14:"03-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:"03";s:4:"name";s:8:"get help";s:4:"slug";s:8:"get-help";s:4:"path";s:26:"/00-welcome/03-get-help.md";s:15:"pathWithoutType";s:23:"/00-welcome/03-get-help";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: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:4;O:8:"stdClass":20:{s:12:"originalName";s:19:"04-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:"04";s:4:"name";s:13:"markdown test";s:4:"slug";s:13:"markdown-test";s:4:"path";s:31:"/00-welcome/04-markdown-test.md";s:15:"pathWithoutType";s:28:"/00-welcome/04-markdown-test";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/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: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:22:"01-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:"01";s:4:"name";s:16:"colors and fonts";s:4:"slug";s:16:"colors-and-fonts";s:4:"path";s:40:"/01-cyanine-theme/01-colors-and-fonts.md";s:15:"pathWithoutType";s:37:"/01-cyanine-theme/01-colors-and-fonts";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:31:"/cyanine-theme/colors-and-fonts";s:6:"urlRel";s:40:"/typemill/cyanine-theme/colors-and-fonts";s:6:"urlAbs";s:56:"http://localhost/typemill/cyanine-theme/colors-and-fonts";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:2;O:8:"stdClass":20:{s:12:"originalName";s:12:"02-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:"02";s:4:"name";s:6:"footer";s:4:"slug";s:6:"footer";s:4:"path";s:30:"/01-cyanine-theme/02-footer.md";s:15:"pathWithoutType";s:27:"/01-cyanine-theme/02-footer";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: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:3;O:8:"stdClass":20:{s:12:"originalName";s:22:"03-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:"03";s:4:"name";s:16:"content elements";s:4:"slug";s:16:"content-elements";s:4:"path";s:40:"/01-cyanine-theme/03-content-elements.md";s:15:"pathWithoutType";s:37:"/01-cyanine-theme/03-content-elements";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/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;}}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:5:{i:0;O:8:"stdClass":20:{s:12:"originalName";s:24:"00-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:"00";s:4:"name";s:18:"setup your website";s:4:"slug";s:18:"setup-your-website";s:4:"path";s:36:"/00-welcome/00-setup-your-website.md";s:15:"pathWithoutType";s:33:"/00-welcome/00-setup-your-website";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: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: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:19:"02-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:"02";s:4:"name";s:13:"write content";s:4:"slug";s:13:"write-content";s:4:"path";s:31:"/00-welcome/02-write-content.md";s:15:"pathWithoutType";s:28:"/00-welcome/02-write-content";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: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:3;O:8:"stdClass":20:{s:12:"originalName";s:14:"03-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:"03";s:4:"name";s:8:"get help";s:4:"slug";s:8:"get-help";s:4:"path";s:26:"/00-welcome/03-get-help.md";s:15:"pathWithoutType";s:23:"/00-welcome/03-get-help";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: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:4;O:8:"stdClass":20:{s:12:"originalName";s:19:"04-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:"04";s:4:"name";s:13:"markdown test";s:4:"slug";s:13:"markdown-test";s:4:"path";s:31:"/00-welcome/04-markdown-test.md";s:15:"pathWithoutType";s:28:"/00-welcome/04-markdown-test";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/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;}}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:22:"01-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:"01";s:4:"name";s:16:"colors and fonts";s:4:"slug";s:16:"colors-and-fonts";s:4:"path";s:40:"/01-cyanine-theme/01-colors-and-fonts.md";s:15:"pathWithoutType";s:37:"/01-cyanine-theme/01-colors-and-fonts";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:31:"/cyanine-theme/colors-and-fonts";s:6:"urlRel";s:40:"/typemill/cyanine-theme/colors-and-fonts";s:6:"urlAbs";s:56:"http://localhost/typemill/cyanine-theme/colors-and-fonts";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:2;O:8:"stdClass":20:{s:12:"originalName";s:12:"02-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:"02";s:4:"name";s:6:"footer";s:4:"slug";s:6:"footer";s:4:"path";s:30:"/01-cyanine-theme/02-footer.md";s:15:"pathWithoutType";s:27:"/01-cyanine-theme/02-footer";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: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:3;O:8:"stdClass":20:{s:12:"originalName";s:22:"03-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:"03";s:4:"name";s:16:"content elements";s:4:"slug";s:16:"content-elements";s:4:"path";s:40:"/01-cyanine-theme/03-content-elements.md";s:15:"pathWithoutType";s:37:"/01-cyanine-theme/03-content-elements";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/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;}}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 9096508..6914183 100644
--- a/data/navigation/navi-extended.txt
+++ b/data/navigation/navi-extended.txt
@@ -18,7 +18,7 @@
keyPath: '0.1'
/welcome/write-content:
navtitle: 'write content'
- hide: true
+ hide: false
noindex: false
path: /00-welcome/02-write-content.md
keyPath: '0.2'
@@ -34,12 +34,6 @@
noindex: false
path: /00-welcome/04-markdown-test.md
keyPath: '0.4'
-/welcome/todos:
- navtitle: 'To Dos'
- hide: false
- noindex: false
- path: /00-welcome/05-todos.txtmd
- keyPath: '0.5'
/cyanine-theme:
navtitle: 'cyanine theme'
hide: false
diff --git a/data/navigation/navi-live.txt b/data/navigation/navi-live.txt
deleted file mode 100644
index b7f3496..0000000
--- a/data/navigation/navi-live.txt
+++ /dev/null
@@ -1 +0,0 @@
-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:24:"00-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:"00";s:4:"name";s:18:"setup your website";s:4:"slug";s:18:"setup-your-website";s:4:"path";s:36:"/00-welcome/00-setup-your-website.md";s:15:"pathWithoutType";s:33:"/00-welcome/00-setup-your-website";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: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: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:19:"02-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:"02";s:4:"name";s:13:"write content";s:4:"slug";s:13:"write-content";s:4:"path";s:31:"/00-welcome/02-write-content.md";s:15:"pathWithoutType";s:28:"/00-welcome/02-write-content";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: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:1;s:7:"noindex";b:0;}i:3;O:8:"stdClass":20:{s:12:"originalName";s:14:"03-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:"03";s:4:"name";s:8:"get help";s:4:"slug";s:8:"get-help";s:4:"path";s:26:"/00-welcome/03-get-help.md";s:15:"pathWithoutType";s:23:"/00-welcome/03-get-help";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: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:4;O:8:"stdClass":20:{s:12:"originalName";s:19:"04-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:"04";s:4:"name";s:13:"markdown test";s:4:"slug";s:13:"markdown-test";s:4:"path";s:31:"/00-welcome/04-markdown-test.md";s:15:"pathWithoutType";s:28:"/00-welcome/04-markdown-test";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/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:5;O:8:"stdClass":20:{s:12:"originalName";s:11:"05-todos.md";s:11:"elementType";s:4:"file";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"05";s:4:"name";s:6:"To Dos";s:4:"slug";s:5:"todos";s:4:"path";s:23:"/00-welcome/05-todos.md";s:15:"pathWithoutType";s:20:"/00-welcome/05-todos";s:3:"key";i:5;s:7:"keyPath";s:3:"0.5";s:12:"keyPathArray";a:2:{i:0;s:1:"0";i:1;s:1:"5";}s:7:"chapter";s:3:"1.6";s:9:"urlRelWoF";s:14:"/welcome/todos";s:6:"urlRel";s:23:"/typemill/welcome/todos";s:6:"urlAbs";s:39:"http://localhost/typemill/welcome/todos";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}}s:7:"noindex";b:0;}i:1;O:8:"stdClass":22:{s:12:"originalName";s:16:"01-cyanine-theme";s:11:"elementType";s:6:"folder";s:8:"contains";s:5:"pages";s:6:"status";s:9:"published";s:8:"fileType";s:2:"md";s:5:"order";s:2:"01";s:4:"name";s:13:"cyanine theme";s:4:"slug";s:13:"cyanine-theme";s:4:"path";s:17:"/01-cyanine-theme";s:15:"pathWithoutType";s:23:"/01-cyanine-theme/index";s:9:"urlRelWoF";s:14:"/cyanine-theme";s:6:"urlRel";s:23:"/typemill/cyanine-theme";s:6:"urlAbs";s:39:"http://localhost/typemill/cyanine-theme";s:3:"key";i:1;s:7:"keyPath";i:1;s:12:"keyPathArray";a:1:{i:0;s:1:"1";}s:7:"chapter";i:2;s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:13:"folderContent";a: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:22:"01-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:"01";s:4:"name";s:16:"colors and fonts";s:4:"slug";s:16:"colors-and-fonts";s:4:"path";s:40:"/01-cyanine-theme/01-colors-and-fonts.md";s:15:"pathWithoutType";s:37:"/01-cyanine-theme/01-colors-and-fonts";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:31:"/cyanine-theme/colors-and-fonts";s:6:"urlRel";s:40:"/typemill/cyanine-theme/colors-and-fonts";s:6:"urlAbs";s:56:"http://localhost/typemill/cyanine-theme/colors-and-fonts";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:7:"noindex";b:0;}i:2;O:8:"stdClass":20:{s:12:"originalName";s:12:"02-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:"02";s:4:"name";s:6:"footer";s:4:"slug";s:6:"footer";s:4:"path";s:30:"/01-cyanine-theme/02-footer.md";s:15:"pathWithoutType";s:27:"/01-cyanine-theme/02-footer";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: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:3;O:8:"stdClass":20:{s:12:"originalName";s:22:"03-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:"03";s:4:"name";s:16:"content elements";s:4:"slug";s:16:"content-elements";s:4:"path";s:40:"/01-cyanine-theme/03-content-elements.md";s:15:"pathWithoutType";s:37:"/01-cyanine-theme/03-content-elements";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/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;}}s:7:"noindex";b:0;}}
\ No newline at end of file
diff --git a/data/security/securitylog.txt b/data/security/securitylog.txt
deleted file mode 100644
index 5ce3ebc..0000000
--- a/data/security/securitylog.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-127.0.0.1;2023-07-26 15:15:18;wrong input for password recovery
-127.0.0.1;2023-07-26 15:24:27;wrong input for password recovery
-127.0.0.1;2023-07-26 15:25:30;wrong input for password recovery
-127.0.0.1;2023-07-29 22:34:30;wrong login
-127.0.0.1;2023-09-13 21:18:22;honeypot http://localhost/typemill/tm/login
-127.0.0.1;2023-09-13 21:26:34;honeypot http://localhost/typemill/tm/login
-127.0.0.1;2023-09-13 21:29:10;honeypot http://localhost/typemill/tm/login
-127.0.0.1;2023-09-13 21:42:59;wrong captcha http://localhost/typemill/tm/login
-127.0.0.1;2023-09-13 22:18:42;wrong captcha http://localhost/typemill/tm/login
-127.0.0.1;2023-09-13 22:20:17;wrong captcha http://localhost/typemill/tm/login
-127.0.0.1;2023-09-15 06:06:46;wrong login
-127.0.0.1;2023-09-15 22:03:38;wrong login
-127.0.0.1;2023-09-16 08:49:53;wrong login
-127.0.0.1;2023-10-23 20:42:13;wrong login
-127.0.0.1;2023-10-23 21:26:26;wrong login
-127.0.0.1;2023-10-24 21:52:19;wrong login
-127.0.0.1;2023-10-26 20:57:09;wrong login
-127.0.0.1;2023-10-28 12:18:34;wrong login
diff --git a/public/.htaccess b/public/.htaccess
deleted file mode 100644
index 11b28de..0000000
--- a/public/.htaccess
+++ /dev/null
@@ -1,4 +0,0 @@
-RewriteEngine On
-RewriteCond %{REQUEST_FILENAME} !-f
-RewriteCond %{REQUEST_FILENAME} !-d
-RewriteRule ^ index.php [QSA,L]
\ No newline at end of file
diff --git a/public/index.php b/public/index.php
deleted file mode 100644
index 6b5a90a..0000000
--- a/public/index.php
+++ /dev/null
@@ -1,5 +0,0 @@
- [typemill](https://github.com/typemill/typemill/tree/tm2-dev/system/typemill).
+Typemill is often used for documentations, manuals, and other websites with a focus on content and text.
-## Tech-stack of Typemill 2
+## Resources
-Typemill 2 will use the following tech-stack:
+* Download and documentation: https:typemill.net
+* Plugins: https://plugins.typemill.net
+* Themes: https://themes.typemill.net
+* Book-layouts: https://books.typemill.net
+* Issues and bug-reports: https://github.com/typemill/typemill/issues
+* Newsletter: https://typemill.net/news
+
+## Requirements
+
+* Webserver (apache, not tested on other servers)
+* PHP 8.0 or higher
+* Some standard PHP-libraries like mod_rewrite, gd-image, mbstring, fileinfo, session, iconv, and more.
+
+## Installation
+
+### With zip-file and ftp
+
+* Download and upack the latest version of typemill as zip-file from https://typemill.net.
+* Upload all files to your server.
+* Visit your new website www.your-typemill-website.com/tm/setup and create and admin user.
+* login and start publishing.
+
+### With github and composer
+
+Clone this repository:
+
+```
+git clone "https://github.com/typemill/typemill.git"
+```
+Then update composer to load the libraries:
+
+```
+composer update
+```
+
+### With docker
+
+Will follow soon ...
+
+## Folder permissions
+
+make sure that the following folders are writable:
+
+* /cache
+* /content
+* /data
+* /media
+* /settings
+
+## Tech-stack
* Slim framework version 4
* Vue.js version 3
* Tailwind css
-With slim framework 4 Typemill 2 will run on PHP 8.0 or 8.1 and higher.
+## Security issues
-The code of Typemill 2 will be refactored in most parts.
+If you discover a possible security issue related to Typemill, please send an email to security@typemill.net and we'll address it as soon as possible.
-## New license system
+## License
-Typemill 2 will stay under the MIT-license.
-
-I will try to update all current plugins and themes to version 2, so that the community can proceed with the further development.
-
-I will also proceed with the development of old and new plugins, but I will use a new commercial license with two different subscription plans. This will help to refinance the open source license of the core system.
-
-## Follow
-
-Twitter: https://twitter.com/typemill
\ No newline at end of file
+Typemill is an open source project published under the MIT-license. Plugins, themes, and services are published under MIT and commercial licenses.
\ No newline at end of file
diff --git a/system/Assets.php b/system/Assets.php
deleted file mode 100644
index 7319107..0000000
--- a/system/Assets.php
+++ /dev/null
@@ -1,305 +0,0 @@
-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[] = '';
- }
-
-# print_r($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/Controllers/ControllerAuthor.php b/system/Controllers/ControllerAuthor.php
deleted file mode 100644
index c68360a..0000000
--- a/system/Controllers/ControllerAuthor.php
+++ /dev/null
@@ -1,340 +0,0 @@
-editorInput($this->params);
-
- if(is_array($vResult))
- {
- $message = reset($vResult);
- $this->errors = ['errors' => $vResult];
-
- if(isset($message[0])){
- $this->errors['errors']['message'] = $message[0];
- }
-
- return false;
- }
- return true;
- }
-
- # author
- protected function validateBlockInput()
- {
- $validate = new Validation();
- $vResult = $validate->blockInput($this->params);
-
- if(is_array($vResult))
- {
- $message = reset($vResult);
- $this->errors = ['errors' => $vResult];
-
- if(isset($message[0]))
- {
- $this->errors['errors']['message'] = $message[0];
- }
-
- return false;
- }
- return true;
- }
-
- # author
- protected function validateNavigationSort()
- {
- $validate = new Validation();
- $vResult = $validate->navigationSort($this->params);
-
- if(is_array($vResult))
- {
- $message = reset($vResult);
- $this->errors = ['errors' => $vResult];
-
- if(isset($message[0])){
- $this->errors['errors']['message'] = $message[0];
- }
-
- return false;
- }
- return true;
- }
-
- # author
- protected function validateNaviItem()
- {
- $validate = new Validation();
- $vResult = $validate->navigationItem($this->params);
-
- if(is_array($vResult))
- {
- $message = reset($vResult);
- $this->errors = ['errors' => $vResult];
-
- if(isset($message[0]))
- {
- $this->errors['errors']['message'] = $message[0];
- }
-
- return false;
- }
- return true;
- }
-
- # author
- protected function validateBaseNaviItem()
- {
- $validate = new Validation();
- $vResult = $validate->navigationBaseItem($this->params);
-
- if(is_array($vResult))
- {
- $message = reset($vResult);
- $this->errors = ['errors' => $vResult];
-
- if(isset($message[0]))
- {
- $this->errors['errors']['message'] = $message[0];
- }
-
- return false;
- }
- return true;
- }
-
- # only backoffice
- protected function setHomepage($args)
- {
- $contentFolder = Folder::scanFolderFlat($this->settings['rootPath'] . $this->settings['contentFolder']);
-
- if(in_array('index.md', $contentFolder))
- {
- $md = true;
- $status = 'published';
- }
- if(in_array('index.txt', $contentFolder))
- {
- $txt = true;
- $status = 'unpublished';
- }
- if(isset($txt) && isset($md))
- {
- $status = 'modified';
- }
-
- $active = false;
- if($this->params['url'] == '/' || (is_array($args) && empty($args)))
- {
- $active = 'active';
- }
-
- $this->homepage = ['status' => $status, 'active' => $active];
- }
-
- # only backoffice
- protected function setItem()
- {
- # home is only set by backend controller, not by api calls
- $home = isset($this->homepage['active']) ? $this->homepage['active'] : false;
-
- # search for the url in the structure
- $item = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl(), NULL, $home);
-
- if($item)
- {
- $this->item = $item;
- return true;
- }
-
- $this->errors = ['errors' => ['message' => 'requested page-url not found']];
-
- return false;
- }
-
- # only backoffice
- # determine if you want to write to published file (md) or to draft (txt)
- protected function setItemPath($fileType)
- {
- $this->path = $this->item->pathWithoutType . '.' . $fileType;
- }
-
- # only backoffice
- protected function setPublishStatus()
- {
- $this->item->published = false;
- $this->item->drafted = false;
-
- if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.md'))
- {
- $this->item->published = true;
-
- # add file-type in case it is a folder
- $this->item->fileType = "md";
- }
-
- if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.txt'))
- {
- $this->item->drafted = true;
-
- # add file-type in case it is a folder
- $this->item->fileType = "txt";
- }
-
- if(!$this->item->drafted && !$this->item->published && $this->item->elementType == "folder")
- {
- # set txt as default for a folder, so that we can create an index.txt for a folder.
- $this->item->fileType = "txt";
- }
- }
-
- # only backoffice
- protected function setContent()
- {
- # if the file exists
- if($this->item->published OR $this->item->drafted)
- {
- $content = $this->writeCache->getFile($this->settings['contentFolder'], $this->path);
- if($this->item->fileType == 'txt')
- {
- # decode the json-draft to an array
- $content = json_decode($content);
- }
- }
- elseif($this->item->elementType == "folder")
- {
- $content = '';
- }
- else
- {
- $this->errors = ['errors' => ['message' => 'requested file not found']];
- return false;
- }
-
- $this->content = $content;
- return true;
- }
-
- # only backoffice
- protected function checkContentOwnership()
- {
- # get page meta
- $writeMeta = new writeMeta();
- $pagemeta = $writeMeta->getPageMeta($this->settings, $this->item);
-
- # check ownership
- if(isset($pagemeta['meta']['owner']) && $pagemeta['meta']['owner'] && $pagemeta['meta']['owner'] !== '' )
- {
- $allowedusers = array_map('trim', explode(",", $pagemeta['meta']['owner']));
- if(isset($_SESSION['user']) && in_array($_SESSION['user'], $allowedusers))
- {
- $this->mycontent = true;
- return true;
- }
- }
-
- return false;
- }
-
- # only backoffice
- protected function deleteContentFiles($fileTypes, $folder = false)
- {
- $basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
-
- foreach($fileTypes as $fileType)
- {
- if(file_exists($basePath . $this->item->pathWithoutType . '.' . $fileType) && !unlink($basePath . $this->item->pathWithoutType . '.' . $fileType) )
- {
- $this->errors = ['message' => 'We could not delete the file, please check, if the file is writable.'];
- }
- }
-
- if($this->errors)
- {
- return false;
- }
-
- return true;
- }
-
- # only backoffice
- protected function deleteContentFolder()
- {
- $basePath = $this->settings['rootPath'] . $this->settings['contentFolder'];
- $path = $basePath . $this->item->path;
-
- if(file_exists($path))
- {
- $files = array_diff(scandir($path), array('.', '..'));
-
- # check if there are published pages or folders inside, then stop the operation
- foreach ($files as $file)
- {
- if(is_dir(realpath($path) . DIRECTORY_SEPARATOR . $file))
- {
- $this->errors = ['message' => 'Please delete the sub-folder first.'];
- }
-
- if(substr($file, -3) == '.md' && $file != 'index.md')
- {
- $this->errors = ['message' => 'Please unpublish all pages in the folder first.'];
- }
- }
-
- if(!$this->errors)
- {
- foreach ($files as $file)
- {
- unlink(realpath($path) . DIRECTORY_SEPARATOR . $file);
- }
- return rmdir($path);
- }
-
- # delete all files from the extended file
- $this->deleteFromExtended();
- }
- return false;
- }
-}
\ No newline at end of file
diff --git a/system/Controllers/ControllerAuthorArticleApi.php b/system/Controllers/ControllerAuthorArticleApi.php
deleted file mode 100644
index 95dc88b..0000000
--- a/system/Controllers/ControllerAuthorArticleApi.php
+++ /dev/null
@@ -1,1134 +0,0 @@
-params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user can publish his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'publish'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to publish content.']), 403);
- }
-
- # validate input only if raw mode
- if($this->params['raw'])
- {
- if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); }
- }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- # set item
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to update content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'publish'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to publish content.']), 403);
- }
- }
-
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # set path
- $this->setItemPath($this->item->fileType);
-
- # if raw mode, use the content from request
- if($this->params['raw'])
- {
- $this->content = '# ' . $this->params['title'] . "\r\n\r\n" . $this->params['content'];
- }
- else
- {
- # read content from file
- if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- # If it is a draft, then create clean markdown content
- if(is_array($this->content))
- {
- # initialize parsedown extension
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl());
-
- # turn markdown into an array of markdown-blocks
- $this->content = $parsedown->arrayBlocksToMarkdown($this->content);
- }
- }
-
- # set path for the file (or folder)
- $this->setItemPath('md');
-
- # update the file
- if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $this->content))
- {
- # update the file
- $delete = $this->deleteContentFiles(['txt']);
-
- # update the internal structure
- $this->setFreshStructureDraft();
-
- # update the public structure
- $this->setFreshStructureLive();
-
- # update the navigation
- $this->setFreshNavigation();
-
- # update the sitemap
- $this->updateSitemap($ping = true);
-
- # complete the page meta if title or description not set
- $writeMeta = new WriteMeta();
- $meta = $writeMeta->completePageMeta($this->content, $this->settings, $this->item);
-
- # dispatch event
- $page = ['content' => $this->content, 'meta' => $meta, 'item' => $this->item];
- $page = $this->c->dispatcher->dispatch('onPagePublished', new OnPagePublished($page))->getData();
-
- return $response->withJson(['success' => true, 'meta' => $page['meta']], 200);
- }
- else
- {
- return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
- }
- }
-
- public function unpublishArticle(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user can unpublish his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'unpublish'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to unpublish content.']), 403);
- }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- # set item
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to update content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'unpublish'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to unpublish content.']), 403);
- }
- }
-
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # check if draft exists, if not, create one.
- if(!$this->item->drafted)
- {
- # set path for the live file (or folder)
- $this->setItemPath('md');
-
- # set content of markdown-file
- if(!$this->setContent()){ return $response->withJson($this->errors, 404); }
-
- # initialize parsedown extension
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl());
-
- # turn markdown into an array of markdown-blocks
- $contentArray = $parsedown->markdownToArrayBlocks($this->content);
-
- # encode the content into json
- $contentJson = json_encode($contentArray);
-
- # set path for the draft file (or folder)
- $this->setItemPath('txt');
-
- # update the file
- if(!$this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $contentJson))
- {
- return $response->withJson(['errors' => ['message' => 'Could not create a draft of the page. Please check if the folder is writable']], 404);
- }
- }
-
- # check if it is a folder and if the folder has published pages.
- $message = false;
- if($this->item->elementType == 'folder' && isset($this->item->folderContent))
- {
- foreach($this->item->folderContent as $folderContent)
- {
- if($folderContent->status == 'published')
- {
- $message = 'There are published pages within this folder. The pages are not visible on your website anymore.';
- }
- }
- }
-
- # delete the live file
- $delete = $this->deleteContentFiles(['md']);
-
- if($delete)
- {
- # update the internal structure
- $this->setFreshStructureDraft();
-
- # update the live structure
- $this->setFreshStructureLive();
-
- # update the navigation
- $this->setFreshNavigation();
-
- # update the sitemap
- $this->updateSitemap();
-
- # dispatch event
- $this->c->dispatcher->dispatch('onPageUnpublished', new OnPageUnpublished($this->item));
-
- return $response->withJson(['success' => ['message' => $message]], 200);
- }
- else
- {
- return $response->withJson(['errors' => ['message' => "Could not delete some files. Please check if the files exists and are writable"]], 404);
- }
- }
-
- public function discardArticleChanges(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to publish content.']), 403);
- }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- # set item
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to update content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to update content.']), 403);
- }
- }
-
- # set redirect url to edit page
- $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'];
- if(isset($this->item->urlRelWoF) && $this->item->urlRelWoF != '/' )
- {
- $url = $url . $this->item->urlRelWoF;
- }
-
- # remove the unpublished changes
- $delete = $this->deleteContentFiles(['txt']);
-
- if($delete)
- {
- # update the backend structure
- $this->setFreshStructureDraft();
-
- return $response->withJson(['data' => $this->structureDraft, 'errors' => false, 'url' => $url], 200);
- }
- else
- {
- return $response->withJson(['data' => $this->structureDraft, 'errors' => $this->errors], 404);
- }
- }
-
- public function deleteArticle(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to delete his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'delete'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to delete content.']), 403);
- }
-
- # set url to base path initially
- $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'];
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- # set item
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to delete content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'delete'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to delete content.']), 403);
- }
- }
-
- if($this->item->elementType == 'file')
- {
- $delete = $this->deleteContentFiles(['md','txt', 'yaml']);
- }
- elseif($this->item->elementType == 'folder')
- {
- $delete = $this->deleteContentFolder();
- }
-
- if($delete)
- {
- # check if it is a subfile or subfolder and set the redirect-url to the parent item
- if(count($this->item->keyPathArray) > 1)
- {
- # get the parent item
- $parentItem = Folder::getParentItem($this->structureDraft, $this->item->keyPathArray);
-
- if($parentItem)
- {
- # an active file has been moved to another folder
- $url .= $parentItem->urlRelWoF;
- }
- }
-
- # update the live structure
- $this->setFreshStructureDraft();
-
- # update the backend structure
- $this->setFreshStructureLive();
-
- # check if page is in extended structure and delete it
- $this->deleteFromExtended();
-
- # update the navigation
- $this->setFreshNavigation();
-
- # update the sitemap
- $this->updateSitemap();
-
- # dispatch event
- $this->c->dispatcher->dispatch('onPageDeleted', new OnPageDeleted($this->item));
-
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => false, 'url' => $url), 200);
- }
- else
- {
- return $response->withJson(array('data' => $this->structure, 'errors' => $this->errors), 422);
- }
- }
-
- public function updateArticle(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to update content.']), 403);
- }
-
- # validate input
- if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- # set item
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to delete content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to update content.']), 403);
- }
- }
-
- # set draft path for the file (or folder)
- $this->setItemPath('txt');
-
- # merge title with content for complete markdown document
- $updatedContent = '# ' . $this->params['title'] . "\r\n\r\n" . $this->params['content'];
-
- # initialize parsedown extension
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl());
-
- # turn markdown into an array of markdown-blocks
- $contentArray = $parsedown->markdownToArrayBlocks($updatedContent);
-
- # encode the content into json
- $contentJson = json_encode($contentArray);
-
- # update the file
- if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $contentJson))
- {
- # update the internal structure to show that this page has drafted changes now
- $this->setFreshStructureDraft();
-
- return $response->withJson(['success'], 200);
- }
- else
- {
- return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
- }
- }
-
- public function renameArticle(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
- $dir = $this->settings['basePath'] . 'cache';
- $pathToContent = $this->settings['rootPath'] . $this->settings['contentFolder'];
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => 'You are not allowed to update content.'), 403);
- }
-
- # validate input 1: check if valid characters
- if(!preg_match("/^[a-z0-9\-]*$/", $this->params['slug']))
- {
- return $response->withJson(['errors' => ['message' => 'the slug contains invalid characters.' ]],422);
- }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- # set item
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # validate input part 2: check if slug has changed or is empty
- if($this->params['slug'] == $this->item->slug OR $this->params['slug'] == '')
- {
- return $response->withJson(['errors' => ['message' => 'the slug is empty or the same as the old one.' ]],422);
- }
-
- # if user has no right to update content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => $this->structure, 'errors' => 'You are not allowed to move that content.'), 403);
- }
- }
-
- # get the folder where file lives in
- $pathWithoutFile = str_replace($this->item->originalName, '', $this->item->path);
-
- # create the new file name with the updated slug
- $newPathWithoutType = $pathWithoutFile . $this->item->order . '-' . $this->params['slug'];
-
- # validate input part 3: check if name is taken already
- $parentKey = $this->item->keyPathArray;
- array_pop($parentKey);
- if(!empty($parentKey))
- {
- $parentFolder = Folder::getItemWithKeyPath($this->structureDraft, $parentKey);
-
- foreach($parentFolder->folderContent as $item)
- {
- if($item->slug == $this->params['slug'])
- {
- return $response->withJson(['errors' => ['message' => 'There is already a page with that slug' ]],422);
- }
- }
- }
- else
- {
- foreach($this->structureDraft as $baseItem)
- {
- if($baseItem->slug == $this->params['slug'])
- {
- return $response->withJson(['errors' => ['message' => 'There is already a page with that slug' ]],422);
- }
- }
- }
-
- # rename the file
- if($this->item->elementType == 'file')
- {
- $this->writeCache->renamePost($this->item->pathWithoutType, $newPathWithoutType);
- }
- elseif($this->item->elementType == 'folder')
- {
- $this->writeCache->renameFile('content', $this->item->path, $newPathWithoutType);
- }
-
- # delete the cache
- $error = $this->writeCache->deleteCacheFiles($dir);
- if($error)
- {
- return $response->withJson(['errors' => $errors], 500);
- }
-
- # recreates the cache for structure, structure-extended, navigation, sitemap
- $this->setFreshStructureDraft();
- $this->setFreshStructureLive();
- $this->setFreshNavigation();
- $this->updateSitemap($ping = true);
-
- $newUrlRel = str_replace($this->item->slug, $this->params['slug'], $this->item->urlRelWoF);
-
- $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $newUrlRel;
-
- return $response->withJson(array('data' => false, 'errors' => false, 'url' => $url));
- }
-
- public function sortArticle(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => 'You are not allowed to update content.'), 403);
- }
-
- # url is only needed, if an active page is moved to another folder, so user has to be redirected to the new url
- $url = false;
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
-
- # validate input
- if(!$this->validateNavigationSort()){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'Data not valid. Please refresh the page and try again.', 'url' => $url), 422); }
-
- # get the ids (key path) for item, old folder and new folder
- $itemKeyPath = explode('.', $this->params['item_id']);
- $parentKeyFrom = explode('.', $this->params['parent_id_from']);
- $parentKeyTo = explode('.', $this->params['parent_id_to']);
-
- # get the item from structure
- $item = Folder::getItemWithKeyPath($this->structureDraft, $itemKeyPath);
-
- if(!$item){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); }
-
- # needed for acl check
- $this->item = $item;
-
- # if user has no right to update content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'You are not allowed to move that content.'), 403);
- }
- }
-
- # if an item is moved to the first level
- if($this->params['parent_id_to'] == 'navi')
- {
- # create empty and default values so that the logic below still works
- $newFolder = new \stdClass();
- $newFolder->path = '';
- $folderContent = $this->structureDraft;
- }
- else
- {
- # get the target folder from structure
- $newFolder = Folder::getItemWithKeyPath($this->structureDraft, $parentKeyTo);
-
- # get the content of the target folder
- $folderContent = $newFolder->folderContent;
- }
-
- # if the item has been moved within the same folder
- if($this->params['parent_id_from'] == $this->params['parent_id_to'])
- {
- # no need to ping search engines
- $ping = false;
-
- # get key of item
- $itemKey = end($itemKeyPath);
- reset($itemKeyPath);
-
- # delete item from folderContent
- unset($folderContent[$itemKey]);
- }
- else
- {
- # let us ping search engines
- $ping = true;
-
- # rename links in extended file
- $this->renameExtended($item, $newFolder);
-
- # an active file has been moved to another folder, so send new url with response
- if($this->params['active'] == 'active')
- {
- $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $newFolder->urlRelWoF . '/' . $item->slug;
- }
- }
-
- # add item to newFolder
- array_splice($folderContent, $this->params['index_new'], 0, array($item));
-
- # initialize index
- $index = 0;
-
- # iterate through the whole content of the new folder to rename the files
- $writeError = false;
- foreach($folderContent as $folderItem)
- {
- if(!$this->writeCache->moveElement($folderItem, $newFolder->path, $index))
- {
- $writeError = true;
- }
- $index++;
- }
- if($writeError){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => ['message' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.'], 'url' => $url), 404); }
-
- # update the structure for editor
- $this->setFreshStructureDraft();
-
- # get item for url and set it active again
- if(isset($this->params['url']))
- {
- $activeItem = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl());
- }
-
- # update the structure for website
- $this->setFreshStructureLive();
-
- # update the navigation
- $this->setFreshNavigation();
-
- # update the sitemap
- $this->updateSitemap($ping);
-
- # dispatch event
- $this->c->dispatcher->dispatch('onPageSorted', new OnPageSorted($this->params));
-
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => false, 'url' => $url));
- }
-
- public function createPost(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'create'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to create content.']), 403);
- }
-
- # url is only needed, if an active page is moved
- $url = false;
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
-
- # validate input
- if(!$this->validateNaviItem()){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => ['message' => 'Special Characters not allowed. Length between 1 and 60 chars.'], 'url' => $url), 422); }
-
- # get the ids (key path) for item, old folder and new folder
- $folderKeyPath = explode('.', $this->params['folder_id']);
-
- # get the item from structure
- $folder = Folder::getItemWithKeyPath($this->structureDraft, $folderKeyPath);
-
- if(!$folder){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => ['message' => 'We could not find this page. Please refresh and try again.'], 'url' => $url), 404); }
-
- $name = $this->params['item_name'];
- $slug = Folder::createSlug($this->params['item_name'], $this->settings);
- $namePath = date("YmdHi") . '-' . $slug;
- $folderPath = 'content' . $folder->path;
- $content = json_encode(['# ' . $name, 'Content']);
-
- # initialise write object
- $writeYaml = new WriteYaml();
-
- # check, if name exists
- if($writeYaml->checkFile($folderPath, $namePath . '.txt') OR $writeYaml->checkFile($folderPath, $namePath . '.md'))
- {
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404);
- }
-
- if(!$writeYaml->writeFile($folderPath, $namePath . '.txt', $content))
- {
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
- }
-
- # get extended structure
- $extended = $writeYaml->getYaml('cache', 'structure-extended.yaml');
-
- # create the url for the item
- $urlWoF = $folder->urlRelWoF . '/' . $slug;
-
- # add the navigation name to the item htmlspecialchars needed for french language
- $extended[$urlWoF] = ['hide' => false, 'navtitle' => $name];
-
- # store the extended structure
- $writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended);
-
- # update the structure for editor
- $this->setFreshStructureDraft();
-
- $folder = Folder::getItemWithKeyPath($this->structureDraft, $folderKeyPath);
-
- # activate this if you want to redirect after creating the page...
- # $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $folder->urlRelWoF . '/' . $slug;
-
- return $response->withJson(array('posts' => $folder, $this->structureDraft, 'errors' => false, 'url' => $url));
- }
-
- public function createArticle(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'create'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to create content.']), 403);
- }
-
- # url is only needed, if an active page is moved
- $url = false;
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
-
- # validate input
- if(!$this->validateNaviItem()){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'Special Characters not allowed. Length between 1 and 60 chars.', 'url' => $url), 422); }
-
- # get the ids (key path) for item, old folder and new folder
- $folderKeyPath = explode('.', $this->params['folder_id']);
-
- # get the item from structure
- $folder = Folder::getItemWithKeyPath($this->structureDraft, $folderKeyPath);
-
- if(!$folder){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); }
-
- # Rename all files within the folder to make sure, that namings and orders are correct
- # get the content of the target folder
- $folderContent = $folder->folderContent;
-
- $name = $this->params['item_name'];
- $slug = Folder::createSlug($this->params['item_name'], $this->settings);
-
- # initialize index
- $index = 0;
-
- # initialise write object
- $writeYaml = new WriteYaml();
-
- # iterate through the whole content of the new folder
- $writeError = false;
-
- foreach($folderContent as $folderItem)
- {
- # check, if the same name as new item, then return an error
- if($folderItem->slug == $slug)
- {
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404);
- }
-
- if(!$writeYaml->moveElement($folderItem, $folder->path, $index))
- {
- $writeError = true;
- }
- $index++;
- }
-
- if($writeError){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); }
-
- # add prefix number to the name
- $namePath = $index > 9 ? $index . '-' . $slug : '0' . $index . '-' . $slug;
- $folderPath = 'content' . $folder->path;
-
- # create default content
- $content = json_encode(['# ' . $name, 'Content']);
-
- if($this->params['type'] == 'file')
- {
- if(!$writeYaml->writeFile($folderPath, $namePath . '.txt', $content))
- {
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
- }
- }
- elseif($this->params['type'] == 'folder')
- {
- if(!$writeYaml->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath))
- {
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
- }
- $this->writeCache->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content);
-
- # always redirect to a folder
- $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $folder->urlRelWoF . '/' . $slug;
-
- }
-
- # get extended structure
- $extended = $writeYaml->getYaml('cache', 'structure-extended.yaml');
-
- # create the url for the item
- $urlWoF = $folder->urlRelWoF . '/' . $slug;
-
- # add the navigation name to the item htmlspecialchars needed for french language
- $extended[$urlWoF] = ['hide' => false, 'navtitle' => $name];
-
- # store the extended structure
- $writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended);
-
- # update the structure for editor
- $this->setFreshStructureDraft();
-
- # get item for url and set it active again
- if(isset($this->params['url']))
- {
- $activeItem = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl());
- }
-
- # activate this if you want to redirect after creating the page...
- # $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $folder->urlRelWoF . '/' . $slug;
-
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => false, 'url' => $url));
- }
-
- public function createBaseItem(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'create'))
- {
- return $response->withJson(array('data' => false, 'errors' => 'You are not allowed to create content.'), 403);
- }
-
- # url is only needed, if an active page is moved
- $url = false;
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
-
- # validate input
- if(!$this->validateBaseNaviItem()){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'Special Characters not allowed. Length between 1 and 20 chars.', 'url' => $url), 422); }
-
- $name = $this->params['item_name'];
- $slug = Folder::createSlug($this->params['item_name'], $this->settings);
-
- # initialize index
- $index = 0;
-
- # initialise write object
- $writeYaml = new WriteYaml();
-
- # iterate through the whole content of the new folder
- $writeError = false;
-
- foreach($this->structureDraft as $item)
- {
- # check, if the same name as new item, then return an error
- if($item->slug == $slug)
- {
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 422);
- }
-
- if(!$writeYaml->moveElement($item, '', $index))
- {
- $writeError = true;
- }
- $index++;
- }
-
- if($writeError){ return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 422); }
-
- # add prefix number to the name
- $namePath = $index > 9 ? $index . '-' . $slug : '0' . $index . '-' . $slug;
- $folderPath = 'content';
-
- # create default content
- $content = json_encode(['# ' . $name, 'Content']);
-
- if($this->params['type'] == 'file')
- {
- if(!$writeYaml->writeFile($folderPath, $namePath . '.txt', $content))
- {
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 422);
- }
- }
- elseif($this->params['type'] == 'folder')
- {
- if(!$this->writeCache->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath))
- {
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 422);
- }
- $writeYaml->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content);
-
- # activate this if you want to redirect after creating the page...
- $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . '/' . $slug;
- }
-
-
- # get extended structure
- $extended = $writeYaml->getYaml('cache', 'structure-extended.yaml');
-
- # create the url for the item
- $urlWoF = '/' . $slug;
-
- # add the navigation name to the item htmlspecialchars needed for frensh language
- $extended[$urlWoF] = ['hide' => false, 'navtitle' => $name];
-
- # store the extended structure
- $writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended);
-
- # update the structure for editor
- $this->setFreshStructureDraft();
-
- # get item for url and set it active again
- if(isset($this->params['url']))
- {
- $activeItem = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl());
- }
-
- return $response->withJson(array('data' => $this->structureDraft, 'errors' => false, 'url' => $url));
- }
-
- # get the backend navigation
- public function getNavigation(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- # get item for url and set it active again
- if(isset($this->params['url']))
- {
- $activeItem = Folder::getItemForUrl($this->structureDraft, $this->params['url'], $this->uri->getBaseUrl());
- }
-
- return $response->withJson(array('data' => $this->structureDraft, 'homepage' => $this->homepage, 'errors' => false));
- }
-
- public function getArticleMarkdown(Request $request, Response $response, $args)
- {
- /* get params from call */
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content. This will completely disable the block-editor
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => 'You are not allowed to edit content.'), 403);
- }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- /* set item */
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to delete content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => 'You are not allowed to delete content.'), 403);
- }
- }
-
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # set path
- $this->setItemPath($this->item->fileType);
-
- # read content from file
- if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- $content = $this->content;
-
- if($content == '')
- {
- $content = [];
- }
-
- # if content is not an array, then transform it
- if(!is_array($content))
- {
- # initialize parsedown extension
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl());
-
- # turn markdown into an array of markdown-blocks
- $content = $parsedown->markdownToArrayBlocks($content);
- }
-
- # delete markdown from title
- if(isset($content[0]))
- {
- $content[0] = trim($content[0], "# ");
- }
- return $response->withJson(array('data' => $content, 'errors' => false));
- }
-
- public function getArticleHtml(Request $request, Response $response, $args)
- {
- /* get params from call */
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => 'You are not allowed to edit content.'), 403);
- }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- /* set item */
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to delete content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => 'You are not allowed to delete content.'), 403);
- }
- }
-
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # set path
- $this->setItemPath($this->item->fileType);
-
- # read content from file
- if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- $content = $this->content;
-
- if($content == '')
- {
- $content = [];
- }
-
- # initialize parsedown extension
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl(), $settings = false, $this->c->dispatcher);
-
- # fix footnotes in parsedown, might break with complicated footnotes
- $parsedown->setVisualMode();
-
- # flag for TOC
- $toc = false;
-
- $tocMarkup = false;
-
- # 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);
-
- # build toc here to avoid duplicated toc for live content
- $tocMarkup = $parsedown->buildTOC($parsedown->headlines);
- }
-
- # needed for ToC links
- $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
-
- # loop through mardkown-array and create html-blocks
- foreach($content as $key => $block)
- {
- # parse markdown-file to content-array
- $contentArray = $parsedown->text($block);
-
- if($block == '[TOC]')
- {
- $toc = $key;
- }
-
- # parse markdown-content-array to content-string
- $content[$key] = ['id' => $key, 'html' => $parsedown->markup($contentArray)];
- }
-
- if($toc)
- {
- if(!$tocMarkup)
- {
- $tocMarkup = $parsedown->buildTOC($parsedown->headlines);
- }
-
- $content[$toc] = ['id' => $toc, 'html' => $tocMarkup];
- }
-
- return $response->withJson(array('data' => $content, 'errors' => false));
- }
-}
\ No newline at end of file
diff --git a/system/Controllers/ControllerAuthorBlockApi.php b/system/Controllers/ControllerAuthorBlockApi.php
deleted file mode 100644
index 35665b4..0000000
--- a/system/Controllers/ControllerAuthorBlockApi.php
+++ /dev/null
@@ -1,597 +0,0 @@
-params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to publish content.']), 403);
- }
-
- /* validate input */
- if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- $this->setHomepage($args = false);
-
- /* set item */
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to delete content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to edit content.']), 403);
- }
- }
-
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # set path
- $this->setItemPath($this->item->fileType);
-
- # read content from file
- if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- # make it more clear which content we have
- $pageMarkdown = $this->content;
-
- $blockMarkdown = $this->params['markdown'];
-
- # standardize line breaks
- $blockMarkdown = str_replace(array("\r\n", "\r"), "\n", $blockMarkdown);
-
- # remove surrounding line breaks
- $blockMarkdown = trim($blockMarkdown, "\n");
-
- if($pageMarkdown == '')
- {
- $pageMarkdown = [];
- }
-
- # initialize parsedown extension
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl(), $settings = false, $this->c->dispatcher);
-
- # if content is not an array, then transform it
- if(!is_array($pageMarkdown))
- {
- # turn markdown into an array of markdown-blocks
- $pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown);
- }
-
- # if it is a new content-block
- if($this->params['block_id'] == 99999)
- {
- # set the id of the markdown-block (it will be one more than the actual array, so count is perfect)
- $id = count($pageMarkdown);
-
- # add the new markdown block to the page content
- $pageMarkdown[] = $blockMarkdown;
- }
- elseif(($this->params['block_id'] == 0) OR !isset($pageMarkdown[$this->params['block_id']]))
- {
- # if the block does not exists, return an error
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'The ID of the content-block is wrong.']), 404);
- }
- else
- {
- # insert new markdown block
- array_splice( $pageMarkdown, $this->params['block_id'], 0, $blockMarkdown );
- $id = $this->params['block_id'];
- }
-
- # encode the content into json
- $pageJson = json_encode($pageMarkdown);
-
- # set path for the file (or folder)
- $this->setItemPath('txt');
-
- /* update the file */
- if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
- {
- # update the internal structure
- $this->setFreshStructureDraft();
- $this->content = $pageMarkdown;
- }
- else
- {
- return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
- }
-
- /* set safe mode to escape javascript and html in markdown */
- $parsedown->setSafeMode(true);
-
- /* parse markdown-file to content-array */
- $blockArray = $parsedown->text($blockMarkdown);
-
- # we assume that toc is not relevant
- $toc = false;
-
- # needed for ToC links
- $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
-
- if($blockMarkdown == '[TOC]')
- {
- # if block is table of content itself, then generate the table of content
- $tableofcontent = $this->generateToc();
-
- # and only use the html-markup
- $blockHTML = $tableofcontent['html'];
- }
- else
- {
- # parse markdown-content-array to content-string
- $blockHTML = $parsedown->markup($blockArray);
-
- # if it is a headline
- if($blockMarkdown[0] == '#')
- {
- # then the TOC holds either false (if no toc used in the page) or it holds an object with the id and toc-markup
- $toc = $this->generateToc();
- }
- }
-
- return $response->withJson(array('content' => [ 'id' => $id, 'html' => $blockHTML ] , 'markdown' => $blockMarkdown, 'id' => $id, 'toc' => $toc, 'errors' => false));
- }
-
- protected function generateToc()
- {
- # we assume that page has no table of content
- $toc = false;
-
- # make sure $this->content is updated
- $content = $this->content;
-
- if($content == '')
- {
- $content = [];
- }
-
- # initialize parsedown extension
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl());
-
- # 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;
-
- # loop through mardkown-array and create html-blocks
- foreach($content as $key => $block)
- {
- # parse markdown-file to content-array
- $contentArray = $parsedown->text($block);
-
- if($block == '[TOC]')
- {
- # toc is true and holds the key of the table of content now
- $toc = $key;
- }
-
- # parse markdown-content-array to content-string
- $content[$key] = ['id' => $key, 'html' => $parsedown->markup($contentArray)];
- }
-
- # if page has a table of content
- if($toc)
- {
- # generate the toc markup
- $tocMarkup = $parsedown->buildTOC($parsedown->headlines);
-
- # toc holds the id of the table of content and the html-markup now
- $toc = ['id' => $toc, 'html' => $tocMarkup];
- }
-
- return $toc;
- }
-
- public function updateBlock(Request $request, Response $response, $args)
- {
- /* get params from call */
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to publish content.']), 403);
- }
-
- /* validate input */
- if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- $this->setHomepage($args = false);
-
- /* set item */
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to delete content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to edit content.']), 403);
- }
- }
-
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # set path
- $this->setItemPath($this->item->fileType);
-
- # read content from file
- if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- # make it more clear which content we have
- $pageMarkdown = $this->content;
-
- $blockMarkdown = $this->params['markdown'];
-
- # standardize line breaks
- $blockMarkdown = str_replace(array("\r\n", "\r"), "\n", $blockMarkdown);
-
- # remove surrounding line breaks
- $blockMarkdown = trim($blockMarkdown, "\n");
-
- if($pageMarkdown == '')
- {
- $pageMarkdown = [];
- }
-
- # initialize parsedown extension
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl(), $settings = false, $this->c->dispatcher);
- $parsedown->setVisualMode();
-
- # if content is not an array, then transform it
- if(!is_array($pageMarkdown))
- {
- # turn markdown into an array of markdown-blocks
- $pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown);
- }
-
- if(!isset($pageMarkdown[$this->params['block_id']]))
- {
- # if the block does not exists, return an error
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'The ID of the content-block is wrong.']), 404);
- }
- elseif($this->params['block_id'] == 0)
- {
- # if it is the title, then delete the "# " if it exists
- $blockMarkdown = trim($blockMarkdown, "# ");
-
- # store the markdown-headline in a separate variable
- $blockMarkdownTitle = '# ' . $blockMarkdown;
-
- # add the markdown-headline to the page-markdown
- $pageMarkdown[0] = $blockMarkdownTitle;
- $id = 0;
- }
- else
- {
- # update the markdown block in the page content
- $pageMarkdown[$this->params['block_id']] = $blockMarkdown;
- $id = $this->params['block_id'];
- }
-
- # encode the content into json
- $pageJson = json_encode($pageMarkdown);
-
- # set path for the file (or folder)
- $this->setItemPath('txt');
-
- # update the file
- if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
- {
- # update the internal structure
- $this->setFreshStructureDraft();
-
- # updated the content variable
- $this->content = $pageMarkdown;
- }
- else
- {
- return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
- }
-
- # parse markdown-file to content-array, if title parse title.
- if($this->params['block_id'] == 0)
- {
- $blockArray = $parsedown->text($blockMarkdownTitle);
- }
- else
- {
- /* set safe mode to escape javascript and html in markdown */
- $parsedown->setSafeMode(true);
-
- $blockArray = $parsedown->text($blockMarkdown);
- }
-
- # we assume that toc is not relevant
- $toc = false;
-
- # needed for ToC links
- $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
-
- if($blockMarkdown == '[TOC]')
- {
- # if block is table of content itself, then generate the table of content
- $tableofcontent = $this->generateToc();
-
- # and only use the html-markup
- $blockHTML = $tableofcontent['html'];
- }
- else
- {
- # parse markdown-content-array to content-string
- $blockHTML = $parsedown->markup($blockArray);
-
- # if it is a headline
- if($blockMarkdown[0] == '#')
- {
- # then the TOC holds either false (if no toc used in the page) or it holds an object with the id and toc-markup
- $toc = $this->generateToc();
- }
- }
-
- return $response->withJson(array('content' => [ 'id' => $id, 'html' => $blockHTML ] , 'markdown' => $blockMarkdown, 'id' => $id, 'toc' => $toc, 'errors' => false));
- }
-
- public function moveBlock(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to publish content.']), 403);
- }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- $this->setHomepage($args = false);
-
- # set item
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to delete content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to delete content.']), 403);
- }
- }
-
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # set path
- $this->setItemPath($this->item->fileType);
-
- # read content from file
- if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- # make it more clear which content we have
- $pageMarkdown = $this->content;
-
- if($pageMarkdown == '')
- {
- $pageMarkdown = [];
- }
-
- # initialize parsedown extension
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl());
-
- # if content is not an array, then transform it
- if(!is_array($pageMarkdown))
- {
- # turn markdown into an array of markdown-blocks
- $pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown);
- }
-
- $oldIndex = ($this->params['old_index'] + 1);
- $newIndex = ($this->params['new_index'] + 1);
-
- if(!isset($pageMarkdown[$oldIndex]))
- {
- # if the block does not exists, return an error
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'The ID of the content-block is wrong.']), 404);
- }
-
- $extract = array_splice($pageMarkdown, $oldIndex, 1);
- array_splice($pageMarkdown, $newIndex, 0, $extract);
-
- # encode the content into json
- $pageJson = json_encode($pageMarkdown);
-
- # set path for the file (or folder)
- $this->setItemPath('txt');
-
- /* update the file */
- if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
- {
- # update the internal structure
- $this->setFreshStructureDraft();
-
- # update this content
- $this->content = $pageMarkdown;
- }
- else
- {
- return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
- }
-
- # we assume that toc is not relevant
- $toc = false;
-
- # needed for ToC links
- $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
-
- # if the moved item is a headline
- if($extract[0][0] == '#')
- {
- $toc = $this->generateToc();
- }
-
- # if it is the title, then delete the "# " if it exists
- $pageMarkdown[0] = trim($pageMarkdown[0], "# ");
-
- return $response->withJson(array('markdown' => $pageMarkdown, 'toc' => $toc, 'errors' => false));
- }
-
- public function deleteBlock(Request $request, Response $response, $args)
- {
- /* get params from call */
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
- $errors = false;
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to delete this content.']), 403);
- }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- $this->setHomepage($args = false);
-
- # set item
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to delete content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to delete this content.']), 403);
- }
- }
-
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # set path
- $this->setItemPath($this->item->fileType);
-
- # read content from file
- if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- # get content
- $this->content;
-
- if($this->content == '')
- {
- $this->content = [];
- }
-
- # initialize parsedown extension
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl());
-
- # if content is not an array, then transform it
- if(!is_array($this->content))
- {
- # turn markdown into an array of markdown-blocks
- $this->content = $parsedown->markdownToArrayBlocks($this->content);
- }
-
- # check if id exists
- if(!isset($this->content[$this->params['block_id']])){ return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404); }
-
- $contentBlock = $this->content[$this->params['block_id']];
-
- # delete the block
- unset($this->content[$this->params['block_id']]);
- $this->content = array_values($this->content);
-
- $pageMarkdown = $this->content;
-
- # delete markdown from title
- if(isset($pageMarkdown[0]))
- {
- $pageMarkdown[0] = trim($pageMarkdown[0], "# ");
- }
-
- # encode the content into json
- $pageJson = json_encode($this->content);
-
- # set path for the file (or folder)
- $this->setItemPath('txt');
-
- /* update the file */
- if($this->writeCache->writeFile($this->settings['contentFolder'], $this->path, $pageJson))
- {
- # update the internal structure
- $this->setFreshStructureDraft();
- }
- else
- {
- return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
- }
-
- $toc = false;
-
- if($contentBlock[0] == '#')
- {
- $toc = $this->generateToc();
- }
-
- return $response->withJson(array('markdown' => $pageMarkdown, 'toc' => $toc, 'errors' => $errors));
- }
-
- public function getShortcodeData(Request $request, Response $response, $args)
- {
- /* get params from call */
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
- $errors = false;
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to delete this content.']), 403);
- }
-
- $shortcodeData = $this->c->dispatcher->dispatch('onShortcodeFound', new OnShortcodeFound(['name' => 'registershortcode', 'data' => []]))->getData();
-
- if(empty($shortcodeData['data']))
- {
- return $response->withJson(array('shortcodedata' => false));
- }
-
- return $response->withJson(array('shortcodedata' => $shortcodeData['data']));
- }
-}
\ No newline at end of file
diff --git a/system/Controllers/ControllerAuthorEditor.php b/system/Controllers/ControllerAuthorEditor.php
deleted file mode 100644
index c410f43..0000000
--- a/system/Controllers/ControllerAuthorEditor.php
+++ /dev/null
@@ -1,181 +0,0 @@
-uri = $request->getUri()->withUserInfo('');
- $this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
-
- # set structure
- if(!$this->setStructureDraft()){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
-
- # set information for homepage
- $this->setHomepage($args);
-
- # set item
- if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
-
- # we have to check ownership here to use it for permission-check in tempates
- $this->checkContentOwnership();
-
- # get the breadcrumb (here we need it only to mark the actual item active in navigation)
- $breadcrumb = isset($this->item->keyPathArray) ? Folder::getBreadcrumb($this->structureDraft, $this->item->keyPathArray) : false;
-
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # set path
- $this->setItemPath($this->item->fileType);
-
- # add the modified date for the file
- $this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
-
- # read content from file
- if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); }
-
- $content = $this->content;
- $title = false;
-
- # if content is an array, then it is a draft
- if(is_array($content))
- {
- # transform array to markdown
- $parsedown = new ParsedownExtension($this->uri->getBaseUrl());
- $content = $parsedown->arrayBlocksToMarkdown($content);
- }
-
- # if there is content
- if($content != '')
- {
- # normalize linebreaks
- $content = str_replace(array("\r\n", "\r"), "\n", $content);
- $content = trim($content, "\n");
-
- # and strip out title
- if($content[0] == '#')
- {
- $contentParts = explode("\n", $content, 2);
- $title = trim($contentParts[0], "# \t\n\r\0\x0B");
- $content = trim($contentParts[1]);
- }
- }
-
- return $this->renderIntern($response, 'editor/editor-raw.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
- ));
- }
-
- /**
- * Show Content for blox editor
- *
- * @param obj $request the slim request object
- * @param obj $response the slim response object
- * @return obje $response with redirect to route
- */
-
- public function showBlox(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()];
-
- # set structure
- if(!$this->setStructureDraft()){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); }
-
- # set information for homepage
- $this->setHomepage($args);
-
- # set item
- if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structureDraft, 'settings' => $this->settings, 'content' => $this->errors )); }
-
- # we have to check ownership here to use it for permission-check in tempates
- $this->checkContentOwnership();
-
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # set path
- $this->setItemPath($this->item->fileType);
-
- # add the modified date for the file
- $this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false;
-
- # 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
- ));
- }
-}
\ No newline at end of file
diff --git a/system/Controllers/ControllerAuthorMediaApi.php b/system/Controllers/ControllerAuthorMediaApi.php
deleted file mode 100644
index c8c26ea..0000000
--- a/system/Controllers/ControllerAuthorMediaApi.php
+++ /dev/null
@@ -1,639 +0,0 @@
-params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- $imageProcessor = new ProcessImage($this->settings['images']);
- if(!$imageProcessor->checkFolders('images'))
- {
- return $response->withJson(['errors' => 'Please check if your media-folder exists and all folders inside are writable.'], 500);
- }
-
- $imagelist = $imageProcessor->scanMediaFlat();
-
- return $response->withJson(['images' => $imagelist]);
- }
-
- public function getMediaLibFiles(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- $fileProcessor = new ProcessFile();
- if(!$fileProcessor->checkFolders())
- {
- return $response->withJson(['errors' => 'Please check if your media-folder exists and all folders inside are writable.'], 500);
- }
-
- $filelist = $fileProcessor->scanFilesFlat();
-
- return $response->withJson(['files' => $filelist]);
- }
-
- public function getImage(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- $this->setStructureDraft();
-
- $imageProcessor = new ProcessImage($this->settings['images']);
- if(!$imageProcessor->checkFolders('images'))
- {
- return $response->withJson(['errors' => 'Please check if your media-folder exists and all folders inside are writable.'], 500);
- }
-
- $imageDetails = $imageProcessor->getImageDetails($this->params['name'], $this->structureDraft);
-
- if($imageDetails)
- {
- return $response->withJson(['image' => $imageDetails]);
- }
-
- return $response->withJson(['errors' => 'Image not found or image name not valid.'], 404);
- }
-
- public function getFile(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- $this->setStructureDraft();
-
- $fileProcessor = new ProcessFile();
- if(!$fileProcessor->checkFolders())
- {
- return $response->withJson(['errors' => 'Please check if your media-folder exists and all folders inside are writable.'], 500);
- }
-
- $fileDetails = $fileProcessor->getFileDetails($this->params['name'], $this->structureDraft);
-
- if($fileDetails)
- {
- return $response->withJson(['file' => $fileDetails]);
- }
-
- return $response->withJson(['errors' => 'file not found or file name invalid'],404);
- }
-
- public function getFileRestrictions(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
- $restriction = 'all';
-
- $userroles = $this->c->acl->getRoles();
-
- if(isset($this->params['filename']) && $this->params['filename'] != '')
- {
- $writeYaml = new WriteYaml();
- $restrictions = $writeYaml->getYaml('media' . DIRECTORY_SEPARATOR . 'files', 'filerestrictions.yaml');
- if(isset($restrictions[$this->params['filename']]))
- {
- $restriction = $restrictions[$this->params['filename']];
- }
- }
-
- return $response->withJson(['userroles' => $userroles, 'restriction' => $restriction]);
- }
-
- public function updateFileRestrictions(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
- $filename = isset($this->params['filename']) ? $this->params['filename'] : false;
- $role = isset($this->params['role']) ? $this->params['role'] : false;
-
- if(!$filename OR !$role)
- {
- return $response->withJson(['errors' => ['message' => 'Filename or userrole is missing.']], 422);
- }
-
- $userroles = $this->c->acl->getRoles();
-
- if($role != 'all' AND !in_array($role, $userroles))
- {
- return $response->withJson(['errors' => ['message' => 'Userrole is unknown.']], 422);
- }
-
- $writeYaml = new WriteYaml();
- $restrictions = $writeYaml->getYaml('media' . DIRECTORY_SEPARATOR . 'files', 'filerestrictions.yaml');
- if(!$restrictions)
- {
- $restrictions = [];
- }
-
- if($role == 'all')
- {
- unset($restrictions[$filename]);
- }
- else
- {
- $restrictions[$filename] = $role;
- }
-
- $writeYaml->updateYaml('media' . DIRECTORY_SEPARATOR . 'files', 'filerestrictions.yaml', $restrictions);
-
- return $response->withJson(['restrictions' => $restrictions]);
- }
-
- public function createImage(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- $imageProcessor = new ProcessImage($this->settings['images']);
-
- if(!$imageProcessor->checkFolders('images'))
- {
- return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
- }
-
- $imageParts = explode(";base64,", $this->params['image']);
- $imageType = explode("/", $imageParts[0]);
-
- if(!isset($imageType[1]))
- {
- return $response->withJson(['errors' => ['message' => 'We did not find an image type, the file might be corrupted.']], 422);
- }
-
- $acceptedTypes = [
- 'png' => true,
- 'jpg' => true,
- 'jpeg' => true,
- 'gif' => true,
- 'webp' => true,
- ];
-
- if(isset($this->settings['svg']) && $this->settings['svg'])
- {
- $acceptedTypes['svg+xml'] = true;
- }
-
- if(!isset($acceptedTypes[$imageType[1]]))
- {
- return $response->withJson(['errors' => ['message' => 'The image type is not supported.']], 422);
- }
-
- $imageResult = $imageProcessor->createImage($this->params['image'], $this->params['name'], $this->settings['images']);
-
- if($imageResult)
- {
- if(is_array($imageResult) && isset($imageResult['errors']))
- {
- return $response->withJson($imageResult,422);
- }
-
- # publish image directly, used for example by image field for meta-tabs
- if($this->params['publish'])
- {
- $imageProcessor->publishImage();
- }
- return $response->withJson(['name' => 'media/live/' . $imageProcessor->getFullName(),'errors' => false]);
- }
-
- return $response->withJson(['errors' => 'could not store image to temporary folder'],422);
- }
-
- public function uploadFile(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- if (!isset($this->params['file']))
- {
- return $response->withJson(['errors' => 'No file found.'],404);
- }
-
- $size = (int) (strlen(rtrim($this->params['file'], '=')) * 3 / 4);
- $extension = pathinfo($this->params['name'], PATHINFO_EXTENSION);
- $finfo = finfo_open( FILEINFO_MIME_TYPE );
- $mtype = @finfo_file( $finfo, $this->params['file'] );
- finfo_close($finfo);
-
- if ($size === 0)
- {
- return $response->withJson(['errors' => 'File is empty.'],422);
- }
-
- # 20 MB (1 byte * 1024 * 1024 * 20 (for 20 MB))
- if ($size > 20971520)
- {
- return $response->withJson(['errors' => 'File is bigger than 20MB.'],422);
- }
-
- # check extension first
- if (!$this->checkAllowedExtensions($extension))
- {
- return $response->withJson(['errors' => 'File is not allowed.'],422);
- }
-
- # check mimetype and extension if there is a mimetype.
- # in some environments the finfo_file does not work with a base64 string.
- if($mtype)
- {
- if(!$this->checkAllowedMimeTypes($mtype, $extension))
- {
- return $response->withJson(['errors' => 'The mime-type or file extension is not allowed.'],422);
- }
- }
-
- $fileProcessor = new ProcessFile();
-
- if(!$fileProcessor->checkFolders())
- {
- return $response->withJson(['errors' => 'Please check if your media-folder exists and all folders inside are writable.'], 500);
- }
-
- $fileinfo = $fileProcessor->storeFile($this->params['file'], $this->params['name']);
-
- 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 );
- finfo_close($finfo);
-
- if(!$mtype OR !$this->checkAllowedMimeTypes($mtype, $extension))
- {
- $fileProcessor->clearTempFolder();
-
- return $response->withJson(['errors' => 'The mime-type is missing, not allowed or does not fit to the file extension.'],422);
- }
- }
-
- # publish file directly, used for example by file field for meta-tabs
- if(isset($this->params['publish']) && $this->params['publish'])
- {
- $fileProcessor->publishFile();
- }
-
- return $response->withJson(['errors' => false, 'info' => $fileinfo]);
- }
-
- return $response->withJson(['errors' => 'could not store file to temporary folder'],500);
- }
-
- public function publishImage(Request $request, Response $response, $args)
- {
- $params = $request->getParsedBody();
-
- $imageProcessor = new ProcessImage($this->settings['images']);
- if(!$imageProcessor->checkFolders())
- {
- return $response->withJson(['errors' => 'Please check if your media-folder exists and all folders inside are writable.'], 500);
- }
-
- # check the resize modifier in the image markdown, set it to true and delete it from markdown
- $noresize = false;
- $markdown = isset($params['markdown']) ? $params['markdown'] : false;
-
- if($markdown && (strlen($markdown) > 9) && (substr($markdown, -9) == '|noresize') )
- {
- $noresize = true;
- $params['markdown'] = substr($markdown,0,-9);
- }
-
- if($imageProcessor->publishImage($noresize))
- {
- $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 image to media folder'],500);
- }
-
- 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 deleteImage(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to delete content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'delete'))
- {
- return $response->withJson(array('data' => false, 'errors' => 'You are not allowed to delete images.'), 403);
- }
-
- if(!isset($this->params['name']))
- {
- return $response->withJson(['errors' => 'image name is missing'],500);
- }
-
- $imageProcessor = new ProcessImage($this->settings['images']);
- if(!$imageProcessor->checkFolders('images'))
- {
- return $response->withJson(['errors' => 'Please check if your media-folder exists and all folders inside are writable.'], 500);
- }
-
- if($imageProcessor->deleteImage($this->params['name']))
- {
- return $response->withJson(['errors' => false]);
- }
-
- return $response->withJson(['errors' => 'Oops, looks like we could not delete all sizes of that image.'], 500);
- }
-
- public function deleteFile(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to delete content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'delete'))
- {
- return $response->withJson(array('data' => false, 'errors' => 'You are not allowed to delete files.'), 403);
- }
-
- if(!isset($this->params['name']))
- {
- return $response->withJson(['errors' => 'file name is missing'],500);
- }
-
- $fileProcessor = new ProcessFile();
-
- if($fileProcessor->deleteFile($this->params['name']))
- {
- return $response->withJson(['errors' => false]);
- }
-
- return $response->withJson(['errors' => 'could not delete the file'],500);
- }
-
- public function saveVideoImage(Request $request, Response $response, $args)
- {
- /* get params from call */
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
- $class = false;
-
- $imageUrl = $this->params['markdown'];
-
- if(strpos($imageUrl, 'https://www.youtube.com/watch?v=') !== false)
- {
- $videoID = str_replace('https://www.youtube.com/watch?v=', '', $imageUrl);
- $videoID = strpos($videoID, '&') ? substr($videoID, 0, strpos($videoID, '&')) : $videoID;
- $class = 'youtube';
- }
- if(strpos($imageUrl, 'https://youtu.be/') !== false)
- {
- $videoID = str_replace('https://youtu.be/', '', $imageUrl);
- $videoID = strpos($videoID, '?') ? substr($videoID, 0, strpos($videoID, '?')) : $videoID;
- $class = 'youtube';
- }
-
- if($class == 'youtube')
- {
- $videoURLmaxres = 'https://i1.ytimg.com/vi/' . $videoID . '/maxresdefault.jpg';
- $videoURL0 = 'https://i1.ytimg.com/vi/' . $videoID . '/0.jpg';
- }
-
- $ctx = stream_context_create(array(
- 'https' => array(
- 'timeout' => 1
- )
- )
- );
-
- $imageData = @file_get_contents($videoURLmaxres, 0, $ctx);
- if($imageData === false)
- {
- $imageData = @file_get_contents($videoURL0, 0, $ctx);
- if($imageData === false)
- {
- return $response->withJson(array('errors' => 'could not get the video image'));
- }
- }
-
- $imageData64 = 'data:image/jpeg;base64,' . base64_encode($imageData);
- $desiredSizes = ['live' => ['width' => 560, 'height' => 315]];
- $imageProcessor = new ProcessImage($this->settings['images']);
- if(!$imageProcessor->checkFolders())
- {
- return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
- }
-
- $tmpImage = $imageProcessor->createImage($imageData64, $videoID, $desiredSizes);
-
- if(!$tmpImage)
- {
- return $response->withJson(array('errors' => 'could not create temporary image'));
- }
-
- $imageUrl = $imageProcessor->publishImage();
- if($imageUrl)
- {
- $this->params['markdown'] = '{#' . $videoID. ' .' . $class . '}';
-
- $request = $request->withParsedBody($this->params);
- $block = new ControllerAuthorBlockApi($this->c);
- if($this->params['new'])
- {
- return $block->addBlock($request, $response, $args);
- }
- return $block->updateBlock($request, $response, $args);
- }
-
- return $response->withJson(array('errors' => 'could not store the preview image'));
- }
-
- # https://www.sitepoint.com/mime-types-complete-list/
- # https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
- # https://wiki.selfhtml.org/wiki/MIME-Type/%C3%9Cbersicht
- # http://www.mime-type.net/application/x-latex/
- private function getAllowedMtypes()
- {
- return array(
- 'application/vnd.oasis.opendocument.chart' => 'odc',
- 'application/vnd.oasis.opendocument.formula' => 'odf',
- 'application/vnd.oasis.opendocument.graphics' => 'odg',
- 'application/vnd.oasis.opendocument.image' => 'odi',
- 'application/vnd.oasis.opendocument.presentation' => 'odp',
- 'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
- 'application/vnd.oasis.opendocument.text' => 'odt',
- 'application/vnd.oasis.opendocument.text-master' => 'odm',
-
- 'application/powerpoint' => 'ppt',
- 'application/mspowerpoint' => ['ppt','ppz','pps','pot'],
- 'application/x-mspowerpoint' => 'ppt',
- 'application/vnd.ms-powerpoint' => 'ppt',
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
-
- 'application/x-visio' => ['vsd','vst','msw'],
- 'application/vnd.visio' => ['vsd','vst','msw'],
- 'application/x-project' => ['mpc','mpt','mpv','mpx'],
- 'application/vnd.ms-project' => 'mpp',
-
- 'application/excel' => ['xla','xlb','xlc','xld','xlk','xll','xlm','xls','xlt','xlv','xlw'],
- 'application/msexcel' => ['xls','xla'],
- 'application/x-excel' => ['xla','xlb','xlc','xld','xlk','xll','xlm','xls','xlt','xlv','xlw'],
- 'application/x-msexcel' => ['xls', 'xla','xlw'],
- 'application/vnd.ms-excel' => ['xlb','xlc','xll','xlm','xls','xlw'],
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
-
- 'application/mshelp' => ['hlp','chm'],
- 'application/msword' => ['doc','dot'],
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
-
- 'application/vnd.apple.keynote' => 'key',
- 'application/vnd.apple.numbers' => 'numbers',
- 'application/vnd.apple.pages' => 'pages',
-
- 'application/x-latex' => ['ltx','latex'],
- 'application/pdf' => 'pdf',
-
- 'application/vnd.amazon.mobi8-ebook' => 'azw3',
- 'application/x-mobipocket-ebook' => 'mobi',
- 'application/epub+zip' => 'epub',
-
- 'application/x-gtar' => 'gtar',
- 'application/x-tar' => 'tar',
- 'application/zip' => 'zip',
- 'application/gzip' => 'gz',
- 'application/x-gzip' => ['gz', 'gzip'],
- 'application/x-compressed' => ['gz','tgz','z','zip'],
- 'application/x-zip-compressed' => 'zip',
- 'application/vnd.rar' => 'rar',
- 'application/x-7z-compressed' => '7z',
-
- 'application/rtf' => 'rtf',
- 'application/x-rtf' => 'rtf',
-
- 'text/calendar' => 'ics',
- 'text/comma-separated-values' => 'csv',
- 'text/css' => 'css',
- 'text/plain' => 'txt',
- 'text/richtext' => 'rtx',
- 'text/rtf' => 'rtf',
-
- 'audio/basic' => ['au','snd'],
- 'audio/mpeg' => 'mp3',
- 'audio/mp4' => 'mp4',
- 'audio/ogg' => 'ogg',
- 'audio/wav' => 'wav',
- 'audio/x-aiff' => ['aif','aiff','aifc'],
- 'audio/x-midi' => ['mid','midi'],
- 'audio/x-mpeg' => 'mp2',
- 'audio/x-pn-realaudio' => ['ram','ra'],
-
- 'image/png' => 'png',
- 'image/jpeg' => ['jpeg','jpe','jpg'],
- 'image/gif' => 'gif',
- 'image/tiff' => ['tiff','tif'],
- 'image/svg+xml' => 'svg',
- 'image/x-icon' => 'ico',
- 'image/webp' => 'webp',
-
- 'video/mpeg' => ['mpeg','mpg','mpe'],
- 'video/mp4' => 'mp4',
- 'video/ogg' => ['ogg','ogv'],
- 'video/quicktime' => ['qt','mov'],
- 'video/vnd.vivo' => ['viv','vivo'],
- 'video/webm' => 'webm',
- 'video/x-msvideo' => 'avi',
- 'video/x-sgi-movie' => 'movie',
- 'video/3gpp' => '3gp',
- );
- }
-
- protected function checkAllowedMimeTypes($mtype, $extension)
- {
- $allowedMimes = $this->getAllowedMtypes();
-
- if(!isset($allowedMimes[$mtype]))
- {
- return false;
- }
-
- if(
- (is_array($allowedMimes[$mtype]) && !in_array($extension, $allowedMimes[$mtype])) OR
- (!is_array($allowedMimes[$mtype]) && $allowedMimes[$mtype] != $extension )
- )
- {
- return false;
- }
-
- return true;
- }
-
- protected function checkAllowedExtensions($extension)
- {
- $mtypes = $this->getAllowedMtypes();
- foreach($mtypes as $mtExtension)
- {
- if(is_array($mtExtension))
- {
- if(in_array($extension, $mtExtension))
- {
- return true;
- }
- }
- else
- {
- if($extension == $mtExtension)
- {
- return true;
- }
- }
- }
-
- return false;
- }
-}
diff --git a/system/Controllers/ControllerAuthorMetaApi.php b/system/Controllers/ControllerAuthorMetaApi.php
deleted file mode 100644
index f96c6a4..0000000
--- a/system/Controllers/ControllerAuthorMetaApi.php
+++ /dev/null
@@ -1,424 +0,0 @@
-aggregateMetaDefinitions();
-
- return $response->withJson(array('definitions' => $metatabs, 'errors' => false));
- }
-
- # get the standard meta-definitions and the meta-definitions from plugins (same for all sites)
- public function aggregateMetaDefinitions($folder = null)
- {
- $writeYaml = new writeYaml();
-
- $metatabs = $writeYaml->getYaml('system' . DIRECTORY_SEPARATOR . 'author', 'metatabs.yaml');
-
- # 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']);
- }
-
- # 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 getArticleMetaObject(Request $request, Response $response, $args)
- {
- /* get params from call */
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- # set item
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- $writeMeta = new writeMeta();
-
- $pagemeta = $writeMeta->getPageMeta($this->settings, $this->item);
-
- if(!$pagemeta)
- {
- # set the status for published and drafted
- $this->setPublishStatus();
-
- # set path
- $this->setItemPath($this->item->fileType);
-
- # read content from file
- if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); }
-
- $pagemeta = $writeMeta->getPageMetaBlank($this->content, $this->settings, $this->item);
- }
-
- # if item is a folder
- if($this->item->elementType == "folder" && isset($this->item->contains))
- {
-
- $pagemeta['meta']['contains'] = isset($pagemeta['meta']['contains']) ? $pagemeta['meta']['contains'] : $this->item->contains;
-
- # get global metadefinitions
- $metadefinitions = $this->aggregateMetaDefinitions($folder = true);
- }
- else
- {
- # get global metadefinitions
- $metadefinitions = $this->aggregateMetaDefinitions();
- }
-
- $metadata = [];
- $metascheme = [];
-
- foreach($metadefinitions as $tabname => $tab )
- {
- $metadata[$tabname] = [];
-
- $tab = $this->flattenTabFields($tab['fields'],[]);
-
- foreach($tab as $fieldname => $fielddefinitions)
- {
- $metascheme[$tabname][$fieldname] = true;
- $metadata[$tabname][$fieldname] = isset($pagemeta[$tabname][$fieldname]) ? $pagemeta[$tabname][$fieldname] : null;
-
- # check if there is a selectfield for userroles
- if(isset($fielddefinitions['type']) && ($fielddefinitions['type'] == 'select' ) && isset($fielddefinitions['dataset']) && ($fielddefinitions['dataset'] == 'userroles' ) )
- {
- $userroles = [null => null];
- foreach($this->c->acl->getRoles() as $userrole)
- {
- $userroles[$userrole] = $userrole;
- }
-
- if(isset($fielddefinitions['fieldset']))
- {
- $metadefinitions[$tabname]['fields'][$fielddefinitions['fieldset']]['fields'][$fieldname]['options'] = $userroles;
- }
- else
- {
- $metadefinitions[$tabname]['fields'][$fieldname]['options'] = $userroles;
- }
- }
- }
- }
-
- # store the metascheme in cache for frontend
- $writeMeta->updateYaml('cache', 'metatabs.yaml', $metascheme);
-
- return $response->withJson(array('metadata' => $metadata, 'metadefinitions' => $metadefinitions, 'item' => $this->item, 'errors' => false));
- }
-
- public function updateArticleMeta(Request $request, Response $response, $args)
- {
- # get params from call
- $this->params = $request->getParams();
- $this->uri = $request->getUri()->withUserInfo('');
-
- # minimum permission is that user is allowed to update his own content
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'mycontent', 'update'))
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to update content.']), 403);
- }
-
- $tab = isset($this->params['tab']) ? $this->params['tab'] : false;
- $metaInput = isset($this->params['data']) ? $this->params['data'] : false ;
- $objectName = 'meta';
- $errors = false;
-
- if(!$tab or !$metaInput)
- {
- return $response->withJson($this->errors, 404);
- }
-
- # set structure
- if(!$this->setStructureDraft()){ return $response->withJson($this->errors, 404); }
-
- # set information for homepage
- $this->setHomepage($args = false);
-
- # set item
- if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
-
- # if user has no right to delete content from others (eg admin or editor)
- if(!$this->c->acl->isAllowed($_SESSION['role'], 'content', 'update'))
- {
- # check ownership. This code should nearly never run, because there is no button/interface to trigger it.
- if(!$this->checkContentOwnership())
- {
- return $response->withJson(array('data' => false, 'errors' => ['message' => 'You are not allowed to edit content.']), 403);
- }
- }
-
- # if item is a folder
- if($this->item->elementType == "folder" && isset($this->item->contains))
- {
- $pagemeta['meta']['contains'] = isset($pagemeta['meta']['contains']) ? $pagemeta['meta']['contains'] : $this->item->contains;
-
- # get global metadefinitions
- $metaDefinitions = $this->aggregateMetaDefinitions($folder = true);
- }
- else
- {
- # get global metadefinitions
- $metaDefinitions = $this->aggregateMetaDefinitions();
- }
-
- $tabFieldDefinitions = $this->flattenTabFields($metaDefinitions[$tab]['fields'], []);
-
- # create validation object
- $validate = $this->getValidator();
-
- # take the user input data and iterate over all fields and values
- foreach($metaInput as $fieldName => $fieldValue)
- {
- # get the corresponding field definition from original plugin settings */
- $fieldDefinition = isset($tabFieldDefinitions[$fieldName]) ? $tabFieldDefinitions[$fieldName] : false;
-
- if(!$fieldDefinition)
- {
- $errors[$tab][$fieldName] = 'This field is not defined';
- }
- else
- {
- if($fieldDefinition && isset($fieldDefinition['type']) && ($fieldDefinition['type'] == 'select' ) && isset($fieldDefinition['dataset']) && ($fieldDefinition['dataset'] == 'userroles' ) )
- {
- $userroles = [null => null];
- foreach($this->c->acl->getRoles() as $userrole)
- {
- $userroles[$userrole] = $userrole;
- }
- $fieldDefinition['options'] = $userroles;
- }
-
- # validate user input for this field
- $result = $validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition);
-
- if($result !== true)
- {
- $errors[$tab][$fieldName] = $result[$fieldName][0];
- }
- }
- }
-
- # return validation errors
- if($errors){ return $response->withJson(array('errors' => $errors),422); }
-
- $writeMeta = new writeMeta();
-
- # get existing metadata for page
- $metaPage = $writeMeta->getYaml($this->settings['contentFolder'], $this->item->pathWithoutType . '.yaml');
-
- # get extended structure
- $extended = $writeMeta->getYaml('cache', 'structure-extended.yaml');
-
- # flag for changed structure
- $structure = false;
-
- if($tab == 'meta')
- {
- # if manual date has been modified
- if( $this->hasChanged($metaInput, $metaPage['meta'], 'manualdate'))
- {
- # update the time
- $metaInput['time'] = date('H-i-s', time());
-
- # if it is a post, then rename the post
- if($this->item->elementType == "file" && strlen($this->item->order) == 12)
- {
- # create file-prefix with date
- $metadate = $metaInput['manualdate'];
- if($metadate == ''){ $metadate = $metaPage['meta']['created']; }
- $datetime = $metadate . '-' . $metaInput['time'];
- $datetime = implode(explode('-', $datetime));
- $datetime = substr($datetime,0,12);
-
- # create the new filename
- $pathWithoutFile = str_replace($this->item->originalName, "", $this->item->path);
- $newPathWithoutType = $pathWithoutFile . $datetime . '-' . $this->item->slug;
-
- $writeMeta->renamePost($this->item->pathWithoutType, $newPathWithoutType);
-
- # recreate the draft structure
- $this->setFreshStructureDraft();
-
- # update item
- $this->setItem();
- }
- }
-
- # if folder has changed and contains pages instead of posts or posts instead of pages
- if($this->item->elementType == "folder" && isset($metaInput['contains']) && isset($metaPage['meta']['contains']) && $this->hasChanged($metaInput, $metaPage['meta'], 'contains'))
- {
- $structure = true;
-
- if($writeMeta->folderContainsFolders($this->item))
- {
- return $response->withJson(array('errors' => ['message' => 'The folder contains another folder so we cannot transform it. Please make sure there are only files in this folder.']),422);
- }
-
- if($metaInput['contains'] == "posts")
- {
- if(!$writeMeta->transformPagesToPosts($this->item))
- {
- return $response->withJson(array('errors' => ['message' => 'One or more files could not be transformed.']),422);
- }
- }
- if($metaInput['contains'] == "pages")
- {
- if(!$writeMeta->transformPostsToPages($this->item))
- {
- return $response->withJson(array('errors' => ['message' => 'One or more files could not be transformed.']),422);
- }
- }
- }
-
- # normalize the meta-input
- $metaInput['navtitle'] = (isset($metaInput['navtitle']) && $metaInput['navtitle'] !== null )? $metaInput['navtitle'] : '';
- $metaInput['hide'] = (isset($metaInput['hide']) && $metaInput['hide'] !== null) ? $metaInput['hide'] : false;
- $metaInput['noindex'] = (isset($metaInput['noindex']) && $metaInput['noindex'] !== null) ? $metaInput['noindex'] : false;
-
- # input values are empty but entry in structure exists
- if(!$metaInput['hide'] && $metaInput['navtitle'] == "" && isset($extended[$this->item->urlRelWoF]))
- {
- # delete the entry in the structure
- unset($extended[$this->item->urlRelWoF]);
-
- $structure = true;
- }
- elseif(
- # check if navtitle or hide-value has been changed
- ($this->hasChanged($metaInput, $metaPage['meta'], 'navtitle'))
- OR
- ($this->hasChanged($metaInput, $metaPage['meta'], 'hide'))
- OR
- ($this->hasChanged($metaInput, $metaPage['meta'], 'noindex'))
- )
- {
- # add new file data. Also makes sure that the value is set.
- $extended[$this->item->urlRelWoF] = ['hide' => $metaInput['hide'], 'navtitle' => $metaInput['navtitle'], 'noindex' => $metaInput['noindex']];
-
- $structure = true;
- }
- }
-
- # add the new/edited metadata
- $metaPage[$tab] = $metaInput;
-
- # store the metadata
- $writeMeta->updateYaml($this->settings['contentFolder'], $this->item->pathWithoutType . '.yaml', $metaPage);
-
- if($structure)
- {
- # store the extended file
- $writeMeta->updateYaml('cache', 'structure-extended.yaml', $extended);
-
- # recreate the draft structure
- $this->setFreshStructureDraft();
-
- # recreate the live structure
- $this->setFreshStructureLive();
-
- # recreate the navigation
- $this->setFreshNavigation();
-
- # update the sitemap
- $this->updateSitemap();
-
- # update item
- $this->setItem();
-
- # set item in navigation active again
- $activeItem = Folder::getItemForUrl($this->structureDraft, $this->item->urlRel, $this->uri->getBaseUrl());
-
- # send new structure to frontend
- $structure = $this->structureDraft;
- }
-
- # return with the new metadata
- return $response->withJson(array('metadata' => $metaInput, 'structure' => $structure, 'item' => $this->item, 'errors' => false));
- }
-
- # 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/Controllers/ControllerDownload.php b/system/Controllers/ControllerDownload.php
deleted file mode 100644
index 7e9ce5e..0000000
--- a/system/Controllers/ControllerDownload.php
+++ /dev/null
@@ -1,147 +0,0 @@
-c->get('settings')['rootPath'];
- $mediapath = 'media/files/';
- $filepath = $root . $mediapath;
-
- if(!$filename)
- {
- die('the requested file does not exist.');
- }
-
- # validate
- $allowedFiletypes = [];
- if(!$this->validate($filepath, $filename, $allowedFiletypes))
- {
- die('the requested file is not allowed.');
- }
-
- $writeYaml = new WriteYaml();
- $restrictions = $writeYaml->getYaml('media' . DIRECTORY_SEPARATOR . 'files', 'filerestrictions.yaml');
-
- if($restrictions && isset($restrictions[$mediapath . $filename]))
- {
- $allowedrole = $restrictions[$mediapath . $filename];
-
- if(!isset($_SESSION['role']))
- {
- $this->c->flash->addMessage('error', "You have to be an authenticated $allowedrole to download this file.");
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
- elseif(
- $_SESSION['role'] != 'administrator'
- AND $_SESSION['role'] != $allowedrole
- AND !$this->c->acl->inheritsRole($_SESSION['role'], $allowedrole)
- )
- {
- $this->c->flash->addMessage('error', "You have to be a $allowedrole to download this file.");
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
- }
-
- $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, $file, $allowedFiletypes)
- {
- $filepath = $path . $file;
-
- # 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($file, $filetype) === (strlen($file) - strlen($filetype)))
- {
- $fileAllowed = true; //ends with $filetype
- }
- }
-
- if (!$fileAllowed) return false;
- }
-
- # check download directory
- if (strpos($file, '..') !== 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/Controllers/ControllerFrontendAuth.php b/system/Controllers/ControllerFrontendAuth.php
deleted file mode 100644
index 00ee77f..0000000
--- a/system/Controllers/ControllerFrontendAuth.php
+++ /dev/null
@@ -1,341 +0,0 @@
-withRedirect($this->c->router->pathFor('content.raw'));
- }
- else
- {
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
- }
-
- /**
- * show login form
- *
- * @param obj $request the slim request object.
- * @param obj $response the slim response object.
- * @param array $args with arguments past to the slim router
- * @return obj $response and string route.
- */
-
- public function show(Request $request, Response $response, $args)
- {
- $settings = $this->c->get('settings');
-
- return $this->render($response, '/auth/login.twig', ['settings' => $settings]);
- }
-
- /**
- * signin an existing user
- *
- * @param obj $request the slim request object with form data in the post params.
- * @param obj $response the slim response object.
- * @return obj $response with redirect to route.
- */
-
- public function login(Request $request, Response $response)
- {
- if( ( null !== $request->getattribute('csrf_result') ) OR ( $request->getattribute('csrf_result') === false ) )
- {
- $this->c->flash->addMessage('error', 'The form has a timeout, please try again.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- /* authentication */
- $params = $request->getParams();
- $validation = new Validation();
- $settings = $this->c->get('settings');
-
- if($validation->signin($params))
- {
- $user = new User();
- $userdata = $user->getUser($params['username']);
-
- if($userdata && password_verify($params['password'], $userdata['password']))
- {
- # check if user has confirmed the account
- if(isset($userdata['optintoken']) && $userdata['optintoken'])
- {
- $this->c->flash->addMessage('error', 'Your registration is not confirmed yet. Please check your e-mails and use the confirmation link.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- $user->login($userdata['username']);
-
- # if user is allowed to view content-area
- if($this->c->acl->hasRole($userdata['userrole']) && $this->c->acl->isAllowed($userdata['userrole'], 'content', 'view'))
- {
- $settings = $this->c->get('settings');
- $editor = (isset($settings['editor']) && $settings['editor'] == 'visual') ? 'visual' : 'raw';
-
- return $response->withRedirect($this->c->router->pathFor('content.' . $editor));
- }
- return $response->withRedirect($this->c->router->pathFor('user.account'));
- }
- }
-
- if(isset($this->settings['securitylog']) && $this->settings['securitylog'])
- {
- \Typemill\Models\Helpers::addLogEntry('wrong login');
- }
-
- $this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- /**
- * log out a user
- *
- * @param obj $request the slim request object
- * @param obj $response the slim response object
- * @return obje $response with redirect to route
- */
-
- public function logout(Request $request, Response $response)
- {
- if(isset($_SESSION))
- {
- session_destroy();
- }
-
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- public function showRecoverPassword(Request $request, Response $response, $args)
- {
- $data = array();
-
- return $this->render($response, '/auth/recoverpw.twig', $data);
- }
-
- public function recoverPassword(Request $request, Response $response, $args)
- {
- $params = $request->getParams();
- $validation = new Validation();
- $settings = $this->c->get('settings');
- $uri = $request->getUri()->withUserInfo('');
- $base_url = $uri->getBaseUrl();
-
- if(!isset($params['email']) OR filter_var($params['email'], \FILTER_VALIDATE_EMAIL) === false )
- {
- $this->c->flash->addMessage('error', 'Please enter a valid email.');
- return $response->withRedirect($this->c->router->pathFor('auth.recoverpwshow'));
- }
-
- $user = new User();
- $requiredUser = $user->findUsersByEmail($params['email']);
-
- if(!$requiredUser)
- {
- $this->c->flash->addMessage('error', 'The email address is unknown.');
- return $response->withRedirect($this->c->router->pathFor('auth.recoverpwshow'));
- }
-
- $requiredUser = $user->getSecureUser($requiredUser[0]);
-
- $requiredUser['recoverdate'] = date("Y-m-d H:i:s");
- $requiredUser['recovertoken'] = bin2hex(random_bytes(32));
-
- $url = $base_url . '/tm/recoverpwnew?username=' . $requiredUser['username'] . '&recovertoken=' . $requiredUser['recovertoken'];
- $link = '' . $url . '';
-
- # define the headers
- $headers = 'Content-Type: text/html; charset=utf-8' . "\r\n";
- $headers .= 'Content-Transfer-Encoding: base64' . "\r\n";
- if(isset($settings['recoverfrom']) && $settings['recoverfrom'] != '')
- {
- $headers .= 'From: ' . $settings['recoverfrom'];
- }
-
- $subjectline = (isset($settings['recoversubject']) && ($settings['recoversubject'] != '') ) ? $settings['recoversubject'] : 'Recover your password';
- $subject = '=?UTF-8?B?' . base64_encode($subjectline) . '?=';
-
- $messagetext = "Dear user,
please use the following link to set a new password:";
- if(isset($settings['recovermessage']) && ($settings['recovermessage'] != ''))
- {
- $parsedown = new ParsedownExtension($base_url);
- $parsedown->setSafeMode(true);
-
- $contentArray = $parsedown->text($settings['recovermessage']);
- $messagetext = $parsedown->markup($contentArray);
- }
-
- $message = base64_encode($messagetext . "
" . $link);
-
- # store user
- $user->updateUser($requiredUser);
-
- $send = mail($requiredUser['email'], $subject, $message, $headers);
-
- if(!$send)
- {
- $data = [
- 'title' => 'We could not send the email',
- 'message' => 'Dear ' . $requiredUser['username'] . ', we could not send the email with the password instructions to your address. You can try it again but chances are low that it will work next time. Please contact the website owner and ask for help.',
- ];
- }
- else
- {
- # store user
- $user->updateUser($requiredUser);
-
- $data = [
- 'title' => 'Please check your inbox',
- 'message' => 'Dear ' . $requiredUser['username'] . ', please check the inbox of your email account. We have sent you some short instructions how to recover your password. Do not forget to check your spam-folder if your inbox is empty.',
- ];
- }
-
- return $this->render($response, '/auth/recoverpwsend.twig', $data);
- }
-
- public function showRecoverPasswordNew(Request $request, Response $response, $args)
- {
- $params = $request->getParams();
-
- if(!isset($params['username']) OR !isset($params['recovertoken']))
- {
- $this->c->flash->addMessage('error', 'Ups, you tried to open the password recovery page but the link was invalid.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- $user = new user();
-
- $requiredUser = $user->getSecureUser($params['username']);
-
- if(!$requiredUser)
- {
- $this->c->flash->addMessage('error', 'Ups, you tried to open the password recovery page but the link was invalid.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- if(!isset($requiredUser['recovertoken']) OR $requiredUser['recovertoken'] != $params['recovertoken'] )
- {
- $this->c->flash->addMessage('error', 'Ups, you tried to open the password recovery page but the link was invalid.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- $recoverdate = isset($requiredUser['recoverdate']) ? $requiredUser['recoverdate'] : false;
-
- if(!$recoverdate )
- {
- $user->unsetFromUser($requiredUser['username'], ['recovertoken']);
-
- $this->c->flash->addMessage('error', 'The link to recover the password was too old. Please create a new one.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- $now = new \DateTime('NOW');
- $recoverdate = new \DateTime($recoverdate);
-
- if(!$recoverdate)
- {
- $user->unsetFromUser($requiredUser['username'], ['recovertoken', 'recoverdate']);
-
- $this->c->flash->addMessage('error', 'The link to recover the password was too old. Please create a new one.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- $validDate = $recoverdate->add(new \DateInterval('P1D'));
-
- if($validDate <= $now)
- {
- $user->unsetFromUser($requiredUser['username'], ['recovertoken', 'recoverdate']);
-
- $this->c->flash->addMessage('error', 'The link to recover the password was too old. Please create a new one.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- return $this->render($response, '/auth/recoverpwnew.twig', ['recovertoken' => $params['recovertoken'],'username' => $requiredUser['username']]);
- }
-
- public function createRecoverPasswordNew(Request $request, Response $response, $args)
- {
- $params = $request->getParams();
-
- if(!isset($params['username']) OR !isset($params['recovertoken']))
- {
- $this->c->flash->addMessage('error', 'Ups, you tried to set a new password but username or token was invalid.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- $validation = new Validation();
-
- if(!$validation->recoverPassword($params))
- {
- $this->c->flash->addMessage('error', 'Please check your input.');
- return $response->withRedirect($this->c->router->pathFor('auth.recoverpwshownew',[], ['username' => $params['username'], 'recovertoken' => $params['recovertoken']]));
- }
-
- $user = new user();
-
- $requiredUser = $user->getSecureUser($params['username']);
-
- if(!$requiredUser)
- {
- $this->c->flash->addMessage('error', 'Ups, you tried to create a new password but the username was invalid.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- if(!isset($requiredUser['recovertoken']) OR $requiredUser['recovertoken'] != $params['recovertoken'] )
- {
- $this->c->flash->addMessage('error', 'Ups, you tried to create a new password but the token was invalid.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- $recoverdate = isset($requiredUser['recoverdate']) ? $requiredUser['recoverdate'] : false;
-
- if(!$recoverdate )
- {
- $user->unsetFromUser($requiredUser['username'], ['recovertoken']);
-
- $this->c->flash->addMessage('error', 'The date for the password reset was invalid. Please create a new one.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- $now = new \DateTime('NOW');
- $recoverdate = new \DateTime($recoverdate);
-
- if(!$recoverdate)
- {
- $user->unsetFromUser($requiredUser['username'], ['recovertoken', 'recoverdate']);
-
- $this->c->flash->addMessage('error', 'The date for the password reset was too old. Please create a new one.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- $validDate = $recoverdate->add(new \DateInterval('P1D'));
-
- if($validDate <= $now)
- {
- $user->unsetFromUser($requiredUser['username'], ['recovertoken', 'recoverdate']);
-
- $this->c->flash->addMessage('error', 'The link to recover the password was too old. Please create a new one.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-
- $requiredUser['password'] = $params['password'];
- $user->updateUser($requiredUser);
- $user->unsetFromUser($requiredUser['username'], ['recovertoken', 'recoverdate']);
-
- unset($_SESSION['old']);
-
- $this->c->flash->addMessage('info', 'A new password has been created. Please login.');
- return $response->withRedirect($this->c->router->pathFor('auth.show'));
- }
-}
\ No newline at end of file
diff --git a/system/Controllers/ControllerFrontendForms.php b/system/Controllers/ControllerFrontendForms.php
deleted file mode 100644
index 8d03d84..0000000
--- a/system/Controllers/ControllerFrontendForms.php
+++ /dev/null
@@ -1,99 +0,0 @@
-isPost())
- {
- $params = $request->getParams();
- reset($params);
- $pluginName = key($params);
- $referer = $request->getHeader('HTTP_REFERER');
-
- # check csrf protection
- if($request->getattribute('csrf_result') === false )
- {
- $this->c->flash->addMessage('error', 'The form has a timeout. Please try again.');
- return $response->withRedirect($referer[0]);
- }
-
- if(isset($params[$pluginName]))
- {
- # validate the user-input
- $this->validateInput('plugins', $pluginName, $params[$pluginName]);
- }
-
- # check for errors and redirect to path, if errors found
- if(isset($_SESSION['errors']))
- {
- $this->c->flash->addMessage('error', 'Please correct the errors');
- return $response->withRedirect($referer[0]);
- }
-
- # clean up and make sure that only validated data are stored
- $data = [ $pluginName => $params[$pluginName]];
-
- # create write object
- $writeYaml = new WriteYaml();
-
- # write the form data into yaml file
- $writeYaml->updateYaml('settings', 'formdata.yaml', $data);
-
- # add message and return to original site
- $this->c->flash->addMessage('formdata', $pluginName);
- return $response->withRedirect($referer[0]);
- }
- }
-
- private function validateInput($objectType, $objectName, $userInput)
- {
- # get settings and start validation
- $originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName);
- $userSettings = \Typemill\Settings::getUserSettings();
- $validate = new Validation();
-
- if(isset($originalSettings['public']['fields']))
- {
- /* flaten the multi-dimensional array with fieldsets to a one-dimensional array */
- $originalFields = array();
- foreach($originalSettings['public']['fields'] as $fieldName => $fieldValue)
- {
- if(isset($fieldValue['fields']))
- {
- foreach($fieldValue['fields'] as $subFieldName => $subFieldValue)
- {
- $originalFields[$subFieldName] = $subFieldValue;
- }
- }
- else
- {
- $originalFields[$fieldName] = $fieldValue;
- }
- }
-
- /* take the user input data and iterate over all fields and values */
- foreach($userInput as $fieldName => $fieldValue)
- {
- /* get the corresponding field definition from original plugin settings */
- $fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false;
-
- if($fieldDefinition)
- {
- /* validate user input for this field */
- $validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition);
- }
- if(!$fieldDefinition && $fieldName != 'active')
- {
- $_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!');
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/system/Controllers/ControllerFrontendSetup.php b/system/Controllers/ControllerFrontendSetup.php
deleted file mode 100644
index 2e13297..0000000
--- a/system/Controllers/ControllerFrontendSetup.php
+++ /dev/null
@@ -1,113 +0,0 @@
-withRedirect($this->c->router->pathFor('setup.show'));
- }
-
- public function show($request, $response, $args)
- {
- /* make some checks befor you install */
- $checkFolder = new Write();
-
- $systemcheck = array();
-
- # check folders and create them if possible
- try{ $checkFolder->checkPath('settings'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
- try{ $checkFolder->checkPath('settings/users'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
- try{ $checkFolder->checkPath('content'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
- try{ $checkFolder->checkPath('cache'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
- try{ $checkFolder->checkPath('media'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); }
-
-
- # check php-version
- if (version_compare(phpversion(), '7.0.0', '<'))
- {
- $systemcheck['error'][] = 'The PHP-version of your server is ' . phpversion() . ' and Typemill needs at least 7.0.0';
- }
-
- /* check if mod rewrite is enabled, does not work with PHP-fpm or NGINX
- $modules = apache_get_modules();
- if(!in_array('mod_rewrite', $modules))
- {
- $systemcheck['error'][] = 'The apache module "mod_rewrite" is not enabled.';
- }
- */
-
- # check if extensions are loaded
- if(!extension_loaded('gd')){ $systemcheck['error'][] = 'The php-extension GD for image manipulation is not enabled.'; }
- if(!extension_loaded('mbstring')){ $systemcheck['error'][] = 'The php-extension mbstring is not enabled.'; }
- if(!extension_loaded('fileinfo')){ $systemcheck['error'][] = 'The php-extension fileinfo is not enabled.'; }
- if(!extension_loaded('session')){ $systemcheck['error'][] = 'The php-extension session is not enabled.'; }
- if(!extension_loaded('iconv')){ $systemcheck['error'][] = 'The php-extension iconv is not enabled.'; }
-
- $setuperrors = empty($systemcheck) ? false : 'Some system requirements for Typemill are missing.';
- $systemcheck = empty($systemcheck) ? false : $systemcheck;
-
- return $this->render($response, 'auth/setup.twig', array( 'messages' => $setuperrors, 'systemcheck' => $systemcheck ));
- }
-
- public function create($request, $response, $args)
- {
- if($request->isPost())
- {
- if( ( null !== $request->getattribute('csrf_result') ) OR ( $request->getattribute('csrf_result') === false ) )
- {
- $this->c->flash->addMessage('error', 'The form has a timeout, please try again.');
- return $response->withRedirect($this->c->router->pathFor('setup.show'));
- }
-
- $params = $request->getParams();
- $validate = new Validation();
- $user = new User();
-
- # set user as admin
- $params['userrole'] = 'administrator';
-
- # get userroles for validation
- $userroles = $this->c->acl->getRoles();
-
- /* validate user */
- if($validate->newUser($params, $userroles))
- {
- $userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']);
-
- /* create initial user */
- $username = $user->createUser($userdata);
-
- if($username)
- {
- /* login user */
- $user->login($username);
-
- # create initial settings file
- \Typemill\Settings::createSettings();
-
- return $response->withRedirect($this->c->router->pathFor('setup.welcome'));
- }
- }
-
- $this->c->flash->addMessage('error', 'Please check your input and try again');
- return $response->withRedirect($this->c->router->pathFor('setup.show'));
- }
- }
-
- public function welcome($request, $response, $args)
- {
- /* store updated settings */
- \Typemill\Settings::updateSettings(array('welcome' => false));
-
- return $this->render($response, 'auth/welcome.twig', array());
- }
-}
\ No newline at end of file
diff --git a/system/Controllers/ControllerFrontendWebsite.php b/system/Controllers/ControllerFrontendWebsite.php
deleted file mode 100644
index 701a890..0000000
--- a/system/Controllers/ControllerFrontendWebsite.php
+++ /dev/null
@@ -1,491 +0,0 @@
-pathToContent = $this->settings['rootPath'] . $this->settings['contentFolder'];
- $this->uri = $request->getUri()->withUserInfo('');
- $this->base_url = $this->uri->getBaseUrl();
-
- # if there is no structure at all, the content folder is probably empty
- if(!$this->setStructureLive())
- {
- return $this->render($response, '/index.twig', array( 'content' => '
No Content
Your content folder is empty.
' ));
- }
-
- # we can create an initial sitemap here, but makes no sense for every pagecall. Sitemap will be created on first author interaction (publish/delete/channge page).
- # $this->checkSitemap();
-
- # if the admin activated to refresh the cache automatically each 10 minutes (e.g. use without admin area)
- if(isset($this->settings['refreshcache']) && $this->settings['refreshcache'] && !$this->writeCache->validate('cache', 'lastCache.txt', 600))
- {
- # delete the cache
- $dir = $this->settings['basePath'] . 'cache';
- $this->writeCache->deleteCacheFiles($dir);
-
- # update the internal structure
- $this->setFreshStructureDraft();
-
- # update the public structure
- $this->setFreshStructureLive();
-
- # update the navigation
- $this->setFreshNavigation();
-
- # update the sitemap
- $this->updateSitemap();
- }
-
- # dispatch event and let others manipulate the structure
- $this->structureLive = $this->c->dispatcher->dispatch('onPagetreeLoaded', new OnPagetreeLoaded($this->structureLive))->getData();
-
- # check if there is a custom theme css
- $theme = $this->settings['theme'];
- $customcss = $this->writeCache->checkFile('cache', $theme . '-custom.css');
- if($customcss)
- {
- $this->c->assets->addCSS($this->base_url . '/cache/' . $theme . '-custom.css');
- }
-
- $logo = false;
- if(isset($this->settings['logo']) && $this->settings['logo'] != '')
- {
- # check if logo exists
- if(file_exists($this->settings['rootPath'] . 'media/live/' . $this->settings['logo']))
- {
- $logo = 'media/live/' . $this->settings['logo'];
- }
- elseif(file_exists($this->settings['rootPath'] . 'media/files/' . $this->settings['logo']))
- {
- $logo = 'media/files/' . $this->settings['logo'];
- }
- }
-
- $favicon = false;
- if(isset($this->settings['favicon']) && $this->settings['favicon'] != '')
- {
- $favicon = true;
- $this->c->assets->addMeta('tilecolor','');
- $this->c->assets->addMeta('tileimage','');
- $this->c->assets->addMeta('icon16','');
- $this->c->assets->addMeta('icon32','');
- $this->c->assets->addMeta('icon72','');
- $this->c->assets->addMeta('icon114','');
- $this->c->assets->addMeta('icon144','');
- $this->c->assets->addMeta('icon180','');
- }
-
- # 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)
- $navigation = $this->writeCache->getCache('cache', 'navigation.txt');
- if(!$navigation)
- {
- # use the structure if there is no cached navigation
- $navigation = $this->structureLive;
- }
-
- # start pagination
- if(isset($args['params']))
- {
- $argSegments = explode("/", $args['params']);
-
- # 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")
- {
- # use page number as current page variable
- $currentpage = $pageNumber;
-
- # set empty args for startpage
- $args = [];
-
- # if there are still params
- if(!empty($argSegments))
- {
- # add them to the args again
- $args['params'] = implode("/", $argSegments);
- }
- }
- }
- }
-
- # if the user is on startpage
- $home = false;
- if(empty($args))
- {
- $home = true;
- $item = Folder::getItemForUrl($navigation, $this->uri->getBasePath(), $this->uri->getBaseUrl(), NULL, $home);
- $urlRel = $this->uri->getBasePath();
- }
- else
- {
- # get the request url, trim args so physical folders have no trailing slash
- $urlRel = $this->uri->getBasePath() . '/' . trim($args['params'], "/");
-
- # find the url in the content-item-tree and return the item-object for the file
- # important to use the structure here so it is found, even if the item is hidden.
- $item = Folder::getItemForUrl($this->structureLive, $urlRel, $this->uri->getBasePath());
-
- # if the item is a folder and if that folder is not hidden
- if($item && $item->elementType == 'folder' && isset($item->hide) && !$item->hide)
- {
- # use the navigation instead of the structure so that hidden elements are erased
- $item = Folder::getItemForUrl($navigation, $urlRel, $this->uri->getBaseUrl(), NULL, $home);
- }
-
- # if there is still no item, return a 404-page
- if(!$item)
- {
- return $this->render404($response, array(
- 'navigation' => $navigation,
- 'settings' => $this->settings,
- 'base_url' => $this->base_url,
- 'title' => false,
- 'content' => false,
- 'item' => false,
- 'breadcrumb' => false,
- 'metatabs' => false,
- 'image' => false,
- 'logo' => $logo,
- 'favicon' => $favicon
- ));
- }
- }
-
- if(isset($item->hide))
- {
- # if it is a hidden page
- if($item->hide)
- {
- # get breadcrumb for page and set pages active
- # use structure here because the hidden item is not part of the navigation
- $breadcrumb = Folder::getBreadcrumb($this->structureLive, $item->keyPathArray);
- $breadcrumb = $this->c->dispatcher->dispatch('onBreadcrumbLoaded', new OnBreadcrumbLoaded($breadcrumb))->getData();
-
- # add the paging to the item
- $item = Folder::getPagingForItem($this->structureLive, $item);
- }
- else
- {
- # get breadcrumb for page and set pages active
- # use navigation, because it is used for frontend
- $breadcrumb = Folder::getBreadcrumb($navigation, $item->keyPathArray);
- $breadcrumb = $this->c->dispatcher->dispatch('onBreadcrumbLoaded', new OnBreadcrumbLoaded($breadcrumb))->getData();
-
- # add the paging to the item
- $item = Folder::getPagingForItem($navigation, $item);
- }
- }
-
- # dispatch the item
- $item = $this->c->dispatcher->dispatch('onItemLoaded', new OnItemLoaded($item))->getData();
-
- # set the filepath
- $filePath = $this->pathToContent . $item->path;
-
- # check if url is a folder and add index.md
- if($item->elementType == 'folder')
- {
- $filePath = $filePath . DIRECTORY_SEPARATOR . 'index.md';
- }
-
- # read the content of the file
- $contentMD = file_exists($filePath) ? file_get_contents($filePath) : false;
-
- # dispatch the original content without plugin-manipulations for case anyone wants to use it
- $this->c->dispatcher->dispatch('onOriginalLoaded', new OnOriginalLoaded($contentMD));
-
- # initiate object for metadata
- $writeMeta = new WriteMeta();
-
- # makes sure that you always have the full meta with title, description and all the rest.
- $metatabs = $writeMeta->completePageMeta($contentMD, $this->settings, $item);
-
- # write meta
- if(isset($metatabs['meta']['noindex']) && $metatabs['meta']['noindex'])
- {
- $this->c->assets->addMeta('noindex','');
- }
-
- $this->c->assets->addMeta('og_site_name','');
- $this->c->assets->addMeta('og_title','');
- $this->c->assets->addMeta('og_description','');
- $this->c->assets->addMeta('og_type','');
- $this->c->assets->addMeta('og_url','');
-
- # dispatch meta
- $metatabs = $this->c->dispatcher->dispatch('onMetaLoaded', new OnMetaLoaded($metatabs))->getData();
-
- # dispatch content
- $contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData();
-
- $itemUrl = isset($item->urlRel) ? $item->urlRel : false;
-
- /* initialize parsedown */
- $parsedown = new ParsedownExtension($this->base_url, $this->settings, $this->c->dispatcher);
-
- /* set safe mode to escape javascript and html in markdown */
- $parsedown->setSafeMode(true);
-
- # check access restriction here
- $restricted = $this->checkRestrictions($metatabs['meta']);
- if($restricted)
- {
- # convert markdown into array of markdown block-elements
- $markdownBlocks = $parsedown->markdownToArrayBlocks($contentMD);
-
- # infos that plugins need to add restriction content
- $restrictions = [
- 'restricted' => $restricted,
- 'defaultContent' => true,
- 'markdownBlocks' => $markdownBlocks,
- ];
-
- # dispatch the data
- $restrictions = $this->c->dispatcher->dispatch('onRestrictionsLoaded', new OnRestrictionsLoaded( $restrictions ))->getData();
-
- # use the returned markdown
- $markdownBlocks = $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
- $markdownBlocks = $shortenedPage;
- }
-
- # finally transform the markdown blocks back to pure markdown text
- $contentMD = $parsedown->arrayBlocksToMarkdown($markdownBlocks);
- }
-
- /* 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 */
- $contentHTML = $parsedown->markup($contentArray);
- $contentHTML = $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData();
-
- /* 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 */
- $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 */
- if(!$img_url OR $img_url == '')
- {
- # extract first image from content
- $firstImageMD = $this->getFirstImage($contentArray);
-
- if($firstImageMD)
- {
- preg_match('#\((.*?)\)#', $firstImageMD, $img_url_result);
- $img_url = isset($img_url_result[1]) ? $img_url_result[1] : false;
-
- if($img_url)
- {
- preg_match('#\[(.*?)\]#', $firstImageMD, $img_alt_result);
- $img_alt = isset($img_alt_result[1]) ? $img_alt_result[1] : false;
- }
- }
- elseif($logo)
- {
- $img_url = $logo;
- $pathinfo = pathinfo($this->settings['logo']);
- $img_alt = $pathinfo['filename'];
- }
- }
-
- $firstImage = false;
- if($img_url)
- {
- $firstImage = array('img_url' => $this->base_url . '/' . $img_url, 'img_alt' => $img_alt);
-
- $this->c->assets->addMeta('og_image','');
- $this->c->assets->addMeta('twitter_image_alt','');
- $this->c->assets->addMeta('twitter_card','');
- }
-
- $route = empty($args) && isset($this->settings['themes'][$theme]['cover']) ? '/cover.twig' : '/index.twig';
-
- return $this->render($response, $route, [
- 'home' => $home,
- 'navigation' => $navigation,
- 'title' => $title,
- 'content' => $contentHTML,
- 'item' => $item,
- 'breadcrumb' => $breadcrumb,
- 'settings' => $this->settings,
- 'base_url' => $this->base_url,
- 'metatabs' => $metatabs,
- 'image' => $firstImage,
- 'logo' => $logo,
- 'favicon' => $favicon,
- 'currentpage' => $currentpage
- ]);
- }
-
-
- 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/Controllers/ControllerSettings.php b/system/Controllers/ControllerSettings.php
deleted file mode 100644
index 9e43199..0000000
--- a/system/Controllers/ControllerSettings.php
+++ /dev/null
@@ -1,1229 +0,0 @@
-c->get('settings');
- $route = $request->getAttribute('route');
- $navigation = $this->getMainNavigation();
-
- $content = '
Hello
I am the showBlank method from the settings controller
In most cases I have been called from a plugin. But if you see this content, then the plugin does not work or has forgotten to inject its own content.