From 0ffb49b411de47919d2ac328ba3c44f1f24a1e2e Mon Sep 17 00:00:00 2001 From: trendschau Date: Sat, 11 Nov 2023 23:19:38 +0100 Subject: [PATCH] finish v2.0 and delete old v1.X files --- .gitignore | 18 +- .htaccesspublic | 3 - cache/cyanine-custom.css | 30 - content/00-welcome/00-setup-your-website.md | 25 +- content/00-welcome/01-manage-access.md | 10 +- content/00-welcome/01-manage-access.yaml | 2 +- content/00-welcome/02-write-content.md | 9 +- content/00-welcome/02-write-content.yaml | 12 +- content/00-welcome/05-todos.md | 200 -- content/00-welcome/05-todos.txt | 1 - content/00-welcome/05-todos.yaml | 14 - data/css/cyanine-custom.css | 30 - data/demo/demotest.yaml | 4 - data/ebookproducts/ebookproducts.yaml | 22 - data/navigation/navi-draft.txt | 2 +- data/navigation/navi-extended.txt | 8 +- data/navigation/navi-live.txt | 1 - data/security/securitylog.txt | 18 - public/.htaccess | 4 - public/index.php | 5 - readme.md | 79 +- system/Assets.php | 305 -- system/Controllers/ControllerAuthor.php | 340 -- .../ControllerAuthorArticleApi.php | 1134 ------ .../Controllers/ControllerAuthorBlockApi.php | 597 ---- system/Controllers/ControllerAuthorEditor.php | 181 - .../Controllers/ControllerAuthorMediaApi.php | 639 ---- .../Controllers/ControllerAuthorMetaApi.php | 424 --- system/Controllers/ControllerDownload.php | 147 - system/Controllers/ControllerFrontendAuth.php | 341 -- .../Controllers/ControllerFrontendForms.php | 99 - .../Controllers/ControllerFrontendSetup.php | 113 - .../Controllers/ControllerFrontendWebsite.php | 491 --- system/Controllers/ControllerSettings.php | 1229 ------- system/Controllers/ControllerShared.php | 477 --- system/Events/BaseEvent.php | 25 - system/Events/OnBreadcrumbLoaded.php | 14 - system/Events/OnCacheUpdated.php | 14 - system/Events/OnContentArrayLoaded.php | 14 - system/Events/OnHtmlLoaded.php | 14 - system/Events/OnItemLoaded.php | 14 - system/Events/OnMarkdownLoaded.php | 14 - system/Events/OnMetaDefinitionsLoaded.php | 14 - system/Events/OnMetaLoaded.php | 14 - system/Events/OnOriginalLoaded.php | 35 - system/Events/OnPageDeleted.php | 14 - system/Events/OnPagePublished.php | 14 - system/Events/OnPageReady.php | 14 - system/Events/OnPageSorted.php | 14 - system/Events/OnPageUnpublished.php | 14 - system/Events/OnPagetreeLoaded.php | 14 - system/Events/OnPluginsLoaded.php | 14 - system/Events/OnResourcesLoaded.php | 14 - system/Events/OnRestrictionsLoaded.php | 14 - system/Events/OnRolesPermissionsLoaded.php | 14 - system/Events/OnSessionSegmentsLoaded.php | 14 - system/Events/OnSettingsLoaded.php | 14 - system/Events/OnShortcodeFound.php | 14 - system/Events/OnSystemnaviLoaded.php | 14 - system/Events/OnUserConfirmed.php | 14 - system/Events/OnUserDeleted.php | 14 - system/Events/OnUserfieldsLoaded.php | 14 - system/Extensions/ParsedownExtension.php | 1268 ------- system/Extensions/TwigCaptchaExtension.php | 44 - system/Extensions/TwigCsrfExtension.php | 31 - system/Extensions/TwigLanguageExtension.php | 59 - system/Extensions/TwigMarkdownExtension.php | 24 - system/Extensions/TwigMetaExtension.php | 45 - system/Extensions/TwigPagelistExtension.php | 22 - system/Extensions/TwigUserExtension.php | 55 - .../Extensions/license-parsedownattributes.md | 20 - system/Extensions/license-parsedownmath.md | 21 - system/Middleware/OldInputMiddleware.php | 32 - system/Middleware/RedirectIfAuthenticated.php | 30 - .../Middleware/RedirectIfUnauthenticated.php | 27 - system/Middleware/RestrictApiAccess.php | 32 - system/Middleware/SecurityMiddleware.php | 117 - .../Middleware/ValidationErrorsMiddleware.php | 36 - system/Middleware/accessMiddleware.php | 54 - system/Middleware/assetMiddleware.php | 40 - system/Models/Field.php | 289 -- system/Models/Fields.php | 138 - system/Models/Folder.php | 669 ---- system/Models/Helpers.php | 108 - system/Models/ProcessAssets.php | 254 -- system/Models/ProcessFile.php | 164 - system/Models/ProcessImage.php | 510 --- system/Models/User.php | 379 -- system/Models/Validation.php | 609 ---- system/Models/Write.php | 212 -- system/Models/WriteCache.php | 113 - system/Models/WriteMeta.php | 379 -- system/Models/WriteYaml.php | 38 - system/Plugin.php | 332 -- system/Plugins.php | 111 - system/Routes/Api.php | 53 - system/Routes/Web.php | 99 - system/Settings.php | 268 -- system/Translations.php | 89 - system/author/auth/login.twig | 59 - system/author/auth/recoverpw.twig | 39 - system/author/auth/recoverpwnew.twig | 41 - system/author/auth/recoverpwsend.twig | 19 - system/author/auth/setup.twig | 53 - system/author/auth/welcome.twig | 49 - system/author/css/a11y-dark.min.css | 7 - system/author/css/style.css | 3066 ----------------- system/author/css/tachyons.min.css | 2 - system/author/css/visiblecontrol.woff | Bin 980 -> 0 bytes system/author/css/visiblecontrol.woff2 | Bin 536 -> 0 bytes system/author/editor/editor-blox.twig | 112 - system/author/editor/editor-raw.twig | 84 - system/author/editor/publish-controller.twig | 56 - system/author/img/favicon-114.png | Bin 7650 -> 0 bytes system/author/img/favicon-144.png | Bin 10336 -> 0 bytes system/author/img/favicon-16.png | Bin 471 -> 0 bytes system/author/img/favicon-180.png | Bin 9537 -> 0 bytes system/author/img/favicon-32.png | Bin 1017 -> 0 bytes system/author/img/favicon-72.png | Bin 3530 -> 0 bytes system/author/intern404.twig | 10 - system/author/js/author.js | 351 -- system/author/js/autosize.min.js | 6 - system/author/js/axios.min.js | 3 - system/author/js/getLevel | 3 - system/author/js/highlight.min.js | 709 ---- system/author/js/sortable.min.js | 2 - system/author/js/typemillutils.js | 116 - system/author/js/vue-blox-config.js | 105 - system/author/js/vue-blox.js | 2444 ------------- system/author/js/vue-editor.js | 28 - system/author/js/vue-meta.js | 266 -- system/author/js/vue-navi.js | 361 -- system/author/js/vue-posts.js | 69 - system/author/js/vue-publishcontroller.js | 213 -- system/author/js/vue-shared.js | 1341 ------- system/author/js/vue-userlist.js | 268 -- system/author/js/vue.min.js | 6 - system/author/languages/de.yaml | 268 -- system/author/languages/en.yaml | 268 -- system/author/languages/fr.yaml | 268 -- system/author/languages/it.yaml | 246 -- system/author/languages/nl.yaml | 268 -- system/author/languages/ru.yaml | 268 -- system/author/languages/updater.py | 42 - system/author/layouts/layout.twig | 64 - system/author/layouts/layoutAuth.twig | 43 - system/author/layouts/layoutBlank.twig | 39 - system/author/layouts/layoutBlox.twig | 82 - system/author/layouts/layoutEditor.twig | 79 - system/author/metatabs.yaml | 104 - system/author/partials/aside.twig | 12 - system/author/partials/editorNavi.twig | 114 - system/author/partials/fields.twig | 112 - system/author/partials/flash.twig | 31 - system/author/partials/form.twig | 58 - system/author/partials/navi.twig | 20 - system/author/partials/symbols.twig | 182 - system/author/settings/blank.twig | 17 - system/author/settings/plugins.twig | 93 - system/author/settings/system.twig | 326 -- system/author/settings/themes.twig | 93 - system/author/settings/user.twig | 71 - system/author/settings/userlist.twig | 44 - system/author/settings/userlistvue.twig | 53 - system/author/settings/usernew.twig | 63 - system/system.php | 329 -- typemill.png | Bin 0 -> 99417 bytes 167 files changed, 91 insertions(+), 27649 deletions(-) delete mode 100644 .htaccesspublic delete mode 100644 cache/cyanine-custom.css delete mode 100644 content/00-welcome/05-todos.md delete mode 100644 content/00-welcome/05-todos.txt delete mode 100644 content/00-welcome/05-todos.yaml delete mode 100644 data/css/cyanine-custom.css delete mode 100644 data/demo/demotest.yaml delete mode 100644 data/ebookproducts/ebookproducts.yaml delete mode 100644 data/navigation/navi-live.txt delete mode 100644 data/security/securitylog.txt delete mode 100644 public/.htaccess delete mode 100644 public/index.php delete mode 100644 system/Assets.php delete mode 100644 system/Controllers/ControllerAuthor.php delete mode 100644 system/Controllers/ControllerAuthorArticleApi.php delete mode 100644 system/Controllers/ControllerAuthorBlockApi.php delete mode 100644 system/Controllers/ControllerAuthorEditor.php delete mode 100644 system/Controllers/ControllerAuthorMediaApi.php delete mode 100644 system/Controllers/ControllerAuthorMetaApi.php delete mode 100644 system/Controllers/ControllerDownload.php delete mode 100644 system/Controllers/ControllerFrontendAuth.php delete mode 100644 system/Controllers/ControllerFrontendForms.php delete mode 100644 system/Controllers/ControllerFrontendSetup.php delete mode 100644 system/Controllers/ControllerFrontendWebsite.php delete mode 100644 system/Controllers/ControllerSettings.php delete mode 100644 system/Controllers/ControllerShared.php delete mode 100644 system/Events/BaseEvent.php delete mode 100644 system/Events/OnBreadcrumbLoaded.php delete mode 100644 system/Events/OnCacheUpdated.php delete mode 100644 system/Events/OnContentArrayLoaded.php delete mode 100644 system/Events/OnHtmlLoaded.php delete mode 100644 system/Events/OnItemLoaded.php delete mode 100644 system/Events/OnMarkdownLoaded.php delete mode 100644 system/Events/OnMetaDefinitionsLoaded.php delete mode 100644 system/Events/OnMetaLoaded.php delete mode 100644 system/Events/OnOriginalLoaded.php delete mode 100644 system/Events/OnPageDeleted.php delete mode 100644 system/Events/OnPagePublished.php delete mode 100644 system/Events/OnPageReady.php delete mode 100644 system/Events/OnPageSorted.php delete mode 100644 system/Events/OnPageUnpublished.php delete mode 100644 system/Events/OnPagetreeLoaded.php delete mode 100644 system/Events/OnPluginsLoaded.php delete mode 100644 system/Events/OnResourcesLoaded.php delete mode 100644 system/Events/OnRestrictionsLoaded.php delete mode 100644 system/Events/OnRolesPermissionsLoaded.php delete mode 100644 system/Events/OnSessionSegmentsLoaded.php delete mode 100644 system/Events/OnSettingsLoaded.php delete mode 100644 system/Events/OnShortcodeFound.php delete mode 100644 system/Events/OnSystemnaviLoaded.php delete mode 100644 system/Events/OnUserConfirmed.php delete mode 100644 system/Events/OnUserDeleted.php delete mode 100644 system/Events/OnUserfieldsLoaded.php delete mode 100644 system/Extensions/ParsedownExtension.php delete mode 100644 system/Extensions/TwigCaptchaExtension.php delete mode 100644 system/Extensions/TwigCsrfExtension.php delete mode 100644 system/Extensions/TwigLanguageExtension.php delete mode 100644 system/Extensions/TwigMarkdownExtension.php delete mode 100644 system/Extensions/TwigMetaExtension.php delete mode 100644 system/Extensions/TwigPagelistExtension.php delete mode 100644 system/Extensions/TwigUserExtension.php delete mode 100644 system/Extensions/license-parsedownattributes.md delete mode 100644 system/Extensions/license-parsedownmath.md delete mode 100644 system/Middleware/OldInputMiddleware.php delete mode 100644 system/Middleware/RedirectIfAuthenticated.php delete mode 100644 system/Middleware/RedirectIfUnauthenticated.php delete mode 100644 system/Middleware/RestrictApiAccess.php delete mode 100644 system/Middleware/SecurityMiddleware.php delete mode 100644 system/Middleware/ValidationErrorsMiddleware.php delete mode 100644 system/Middleware/accessMiddleware.php delete mode 100644 system/Middleware/assetMiddleware.php delete mode 100644 system/Models/Field.php delete mode 100644 system/Models/Fields.php delete mode 100644 system/Models/Folder.php delete mode 100644 system/Models/Helpers.php delete mode 100644 system/Models/ProcessAssets.php delete mode 100644 system/Models/ProcessFile.php delete mode 100644 system/Models/ProcessImage.php delete mode 100644 system/Models/User.php delete mode 100644 system/Models/Validation.php delete mode 100644 system/Models/Write.php delete mode 100644 system/Models/WriteCache.php delete mode 100644 system/Models/WriteMeta.php delete mode 100644 system/Models/WriteYaml.php delete mode 100644 system/Plugin.php delete mode 100644 system/Plugins.php delete mode 100644 system/Routes/Api.php delete mode 100644 system/Routes/Web.php delete mode 100644 system/Settings.php delete mode 100644 system/Translations.php delete mode 100644 system/author/auth/login.twig delete mode 100644 system/author/auth/recoverpw.twig delete mode 100644 system/author/auth/recoverpwnew.twig delete mode 100644 system/author/auth/recoverpwsend.twig delete mode 100644 system/author/auth/setup.twig delete mode 100644 system/author/auth/welcome.twig delete mode 100644 system/author/css/a11y-dark.min.css delete mode 100644 system/author/css/style.css delete mode 100644 system/author/css/tachyons.min.css delete mode 100644 system/author/css/visiblecontrol.woff delete mode 100644 system/author/css/visiblecontrol.woff2 delete mode 100644 system/author/editor/editor-blox.twig delete mode 100644 system/author/editor/editor-raw.twig delete mode 100644 system/author/editor/publish-controller.twig delete mode 100644 system/author/img/favicon-114.png delete mode 100644 system/author/img/favicon-144.png delete mode 100644 system/author/img/favicon-16.png delete mode 100644 system/author/img/favicon-180.png delete mode 100644 system/author/img/favicon-32.png delete mode 100644 system/author/img/favicon-72.png delete mode 100644 system/author/intern404.twig delete mode 100644 system/author/js/author.js delete mode 100644 system/author/js/autosize.min.js delete mode 100644 system/author/js/axios.min.js delete mode 100644 system/author/js/getLevel delete mode 100644 system/author/js/highlight.min.js delete mode 100644 system/author/js/sortable.min.js delete mode 100644 system/author/js/typemillutils.js delete mode 100644 system/author/js/vue-blox-config.js delete mode 100644 system/author/js/vue-blox.js delete mode 100644 system/author/js/vue-editor.js delete mode 100644 system/author/js/vue-meta.js delete mode 100644 system/author/js/vue-navi.js delete mode 100644 system/author/js/vue-posts.js delete mode 100644 system/author/js/vue-publishcontroller.js delete mode 100644 system/author/js/vue-shared.js delete mode 100644 system/author/js/vue-userlist.js delete mode 100644 system/author/js/vue.min.js delete mode 100644 system/author/languages/de.yaml delete mode 100644 system/author/languages/en.yaml delete mode 100644 system/author/languages/fr.yaml delete mode 100644 system/author/languages/it.yaml delete mode 100644 system/author/languages/nl.yaml delete mode 100644 system/author/languages/ru.yaml delete mode 100644 system/author/languages/updater.py delete mode 100644 system/author/layouts/layout.twig delete mode 100644 system/author/layouts/layoutAuth.twig delete mode 100644 system/author/layouts/layoutBlank.twig delete mode 100644 system/author/layouts/layoutBlox.twig delete mode 100644 system/author/layouts/layoutEditor.twig delete mode 100644 system/author/metatabs.yaml delete mode 100644 system/author/partials/aside.twig delete mode 100644 system/author/partials/editorNavi.twig delete mode 100644 system/author/partials/fields.twig delete mode 100644 system/author/partials/flash.twig delete mode 100644 system/author/partials/form.twig delete mode 100644 system/author/partials/navi.twig delete mode 100644 system/author/partials/symbols.twig delete mode 100644 system/author/settings/blank.twig delete mode 100644 system/author/settings/plugins.twig delete mode 100644 system/author/settings/system.twig delete mode 100644 system/author/settings/themes.twig delete mode 100644 system/author/settings/user.twig delete mode 100644 system/author/settings/userlist.twig delete mode 100644 system/author/settings/userlistvue.twig delete mode 100644 system/author/settings/usernew.twig delete mode 100644 system/system.php create mode 100644 typemill.png 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 ![youtube-video](media/live/youtube-7yvlwxjl9dc.jpeg "click to load video"){#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: ![youtube-video](media/live/youtube-uw-m-4g1kaa.jpeg "click to load video"){#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. ![youtube-video](media/live/youtube-6i2-uv88gke.jpeg "click to load video"){#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'] = '![' . $class . '-video](' . $imageUrl . ' "click to load video"){#' . $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.

'; - - return $this->render($response, 'settings/blank.twig', array( - 'settings' => $settings, - 'acl' => $this->c->acl, - 'navigation' => $navigation, - 'content' => $content, - 'route' => $route->getName() - )); - } - - /********************* - ** BASIC SETTINGS ** - *********************/ - - public function showSettings($request, $response, $args) - { - $user = new User(); - $settings = $this->c->get('settings'); - $defaultSettings = \Typemill\Settings::getDefaultSettings(); - $copyright = $this->getCopyright(); - $languages = $this->getLanguages(); - $locale = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? substr($_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2) : 'en'; - $route = $request->getAttribute('route'); - $navigation = $this->getMainNavigation(); - - # set navigation active - $navigation['System']['active'] = true; - - return $this->render($response, 'settings/system.twig', array( - 'settings' => $settings, - 'acl' => $this->c->acl, - 'navigation' => $navigation, - 'copyright' => $copyright, - 'languages' => $languages, - 'locale' => $locale, - 'formats' => $defaultSettings['formats'], - 'route' => $route->getName() - )); - } - - public function saveSettings($request, $response, $args) - { - if($request->isPost()) - { - - if( $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('settings.show')); - } - - $settings = \Typemill\Settings::getUserSettings(); - $defaultSettings = \Typemill\Settings::getDefaultSettings(); - $params = $request->getParams(); - $files = $request->getUploadedFiles(); - $newSettings = isset($params['settings']) ? $params['settings'] : false; - $validate = new Validation(); - $processImage = new ProcessImage(); - - if($newSettings) - { - # check for image settings - $imgwidth = isset($newSettings['images']['live']['width']) ? $newSettings['images']['live']['width'] : false; - $imgheight = isset($newSettings['images']['live']['height']) ? $newSettings['images']['live']['height'] : false; - - # make sure only allowed fields are stored - $newSettings = array( - 'title' => $newSettings['title'], - 'author' => $newSettings['author'], - 'copyright' => $newSettings['copyright'], - 'year' => $newSettings['year'], - 'language' => $newSettings['language'], - 'langattr' => $newSettings['langattr'], - 'editor' => $newSettings['editor'], - 'formats' => $newSettings['formats'], - 'access' => isset($newSettings['access']) ? true : null, - 'pageaccess' => isset($newSettings['pageaccess']) ? true : null, - 'hrdelimiter' => isset($newSettings['hrdelimiter']) ? true : null, - 'restrictionnotice' => $newSettings['restrictionnotice'], - 'wraprestrictionnotice' => isset($newSettings['wraprestrictionnotice']) ? true : null, - 'headlineanchors' => isset($newSettings['headlineanchors']) ? $newSettings['headlineanchors'] : null, - 'displayErrorDetails' => isset($newSettings['displayErrorDetails']) ? true : null, - 'twigcache' => isset($newSettings['twigcache']) ? true : null, - 'proxy' => isset($newSettings['proxy']) ? true : null, - 'trustedproxies' => $newSettings['trustedproxies'], - 'headersoff' => isset($newSettings['headersoff']) ? true : null, - 'urlschemes' => $newSettings['urlschemes'], - 'svg' => isset($newSettings['svg']) ? true : null, - 'recoverpw' => isset($newSettings['recoverpw']) ? true : null, - 'recoverfrom' => $newSettings['recoverfrom'], - 'recoversubject' => $newSettings['recoversubject'], - 'recovermessage' => $newSettings['recovermessage'], - 'securitylog' => isset($newSettings['securitylog']) ? true : null, - 'oldslug' => isset($newSettings['oldslug']) ? true : null, - 'refreshcache' => isset($newSettings['refreshcache']) ? true : null, - 'pingsitemap' => isset($newSettings['pingsitemap']) ? true : null, - ); - - # https://www.slimframework.com/docs/v3/cookbook/uploading-files.html; - - $copyright = $this->getCopyright(); - - $validate->settings($newSettings, $copyright, $defaultSettings['formats'], 'settings'); - - # use custom image settings - if( $imgwidth && ctype_digit($imgwidth) && (strlen($imgwidth) < 5) ) - { - $newSettings['images']['live']['width'] = $imgwidth; - } - if( $imgheight && ctype_digit($imgheight) && (strlen($imgheight) < 5) ) - { - $newSettings['images']['live']['height'] = $imgheight; - } - } - else - { - $this->c->flash->addMessage('error', 'Wrong Input'); - return $response->withRedirect($this->c->router->pathFor('settings.show')); - } - - if(isset($_SESSION['errors'])) - { - $this->c->flash->addMessage('error', 'Please correct the errors'); - return $response->withRedirect($this->c->router->pathFor('settings.show')); - } - - if(!$processImage->checkFolders()) - { - $this->c->flash->addMessage('error', 'Please make sure that your media folder exists and is writable.'); - return $response->withRedirect($this->c->router->pathFor('settings.show')); - } - - # handle single input with single file upload - $logo = $files['settings']['logo']; - $allowed = ['jpg', 'jpeg', 'png', 'svg']; - if($logo->getError() === UPLOAD_ERR_OK) - { - $extension = pathinfo($logo->getClientFilename(), PATHINFO_EXTENSION); - if(!in_array(strtolower($extension), $allowed)) - { - $_SESSION['errors']['settings']['logo'] = array('Only jpg, jpeg, png and svg allowed'); - $this->c->flash->addMessage('error', 'Please correct the errors'); - return $response->withRedirect($this->c->router->pathFor('settings.show')); - } - - foreach($allowed as $logoextension) - { - $processImage->deleteImage('logo.' . $logoextension); - } - - $newSettings['logo'] = $processImage->moveUploadedImage($logo, $overwrite = true, $name = 'logo'); - $processImage->copyImage('logo.' . $logoextension, $processImage->liveFolder, $processImage->thumbFolder); - } - elseif(isset($params['settings']['deletelogo']) && $params['settings']['deletelogo'] == 'delete') - { - foreach($allowed as $logoextension) - { - $processImage->deleteImage('logo.' . $logoextension); - } - $newSettings['logo'] = ''; - } - else - { - $newSettings['logo'] = isset($settings['logo']) ? $settings['logo'] : ''; - } - - # handle single input with single file upload - $favicon = $files['settings']['favicon']; - if ($favicon->getError() === UPLOAD_ERR_OK) - { - $extension = pathinfo($favicon->getClientFilename(), PATHINFO_EXTENSION); - if(strtolower($extension) != 'png') - { - $_SESSION['errors']['settings']['favicon'] = array('Only .png-files allowed'); - $this->c->flash->addMessage('error', 'Please correct the errors'); - return $response->withRedirect($this->c->router->pathFor('settings.show')); - } - - $processFavImage = new ProcessImage([ - '16' => ['width' => 16, 'height' => 16], - '32' => ['width' => 32, 'height' => 32], - '72' => ['width' => 72, 'height' => 72], - '114' => ['width' => 114, 'height' => 114], - '144' => ['width' => 144, 'height' => 144], - '180' => ['width' => 180, 'height' => 180], - ]); - $favicons = $processFavImage->generateSizesFromImageFile('favicon.png', $favicon->file); - - foreach($favicons as $key => $favicon) - { - imagepng( $favicon, $processFavImage->fileFolder . 'favicon-' . $key . '.png' ); - } - - $newSettings['favicon'] = 'favicon'; - } - elseif(isset($params['settings']['deletefav']) && $params['settings']['deletefav'] == 'delete') - { - $processFiles = new ProcessFile(); - $processFiles->deleteFileWithName('favicon-*.png'); - $newSettings['favicon'] = ''; - } - else - { - $newSettings['favicon'] = isset($settings['favicon']) ? $settings['favicon'] : ''; - } - - # store updated settings - \Typemill\Settings::updateSettings(array_merge($settings, $newSettings)); - - $this->c->flash->addMessage('info', 'Settings are stored'); - return $response->withRedirect($this->c->router->pathFor('settings.show')); - } - } - - /********************* - ** THEME SETTINGS ** - *********************/ - - public function showThemes($request, $response, $args) - { - $userSettings = $this->c->get('settings'); - $themes = $this->getThemes(); - $themedata = array(); - $fieldsModel = new Fields($this->c); - - foreach($themes as $themeName) - { - /* if theme is active, list it first */ - if($userSettings['theme'] == $themeName) - { - $themedata = array_merge(array($themeName => null), $themedata); - } - else - { - $themedata[$themeName] = null; - } - - $themeSettings = \Typemill\Settings::getObjectSettings('themes', $themeName); - - # add standard-textarea for custom css - $themeSettings['forms']['fields']['customcss'] = ['type' => 'textarea', 'label' => 'Custom CSS', 'rows' => 10, 'class' => 'codearea', 'description' => 'You can overwrite the theme-css with your own css here.']; - - # load custom css-file - $write = new write(); - $customcss = $write->getFile('cache', $themeName . '-custom.css'); - $themeSettings['settings']['customcss'] = $customcss; - - - if($themeSettings) - { - /* store them as default theme data with author, year, default settings and field-definitions */ - $themedata[$themeName] = $themeSettings; - } - - if(isset($themeSettings['forms']['fields'])) - { - $fields = $fieldsModel->getFields($userSettings, 'themes', $themeName, $themeSettings); - - /* overwrite original theme form definitions with enhanced form objects */ - $themedata[$themeName]['forms']['fields'] = $fields; - } - - /* add the preview image */ - $img = getcwd() . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeName . DIRECTORY_SEPARATOR . $themeName; - - $image = false; - if(file_exists($img . '.jpg')) - { - $image = $themeName . '.jpg'; - } - if(file_exists($img . '.png')) - { - $image = $themeName . '.png'; - } - - $themedata[$themeName]['img'] = $image; - } - - /* add the users for navigation */ - $route = $request->getAttribute('route'); - $navigation = $this->getMainNavigation(); - - # set navigation active - $navigation['Themes']['active'] = true; - - return $this->render($response, 'settings/themes.twig', array( - 'settings' => $userSettings, - 'acl' => $this->c->acl, - 'navigation' => $navigation, - 'themes' => $themedata, - 'route' => $route->getName() - )); - } - - public function showPlugins($request, $response, $args) - { - $userSettings = $this->c->get('settings'); - $plugins = array(); - $fieldsModel = new Fields($this->c); - $fields = array(); - - /* iterate through the plugins in the stored user settings */ - foreach($userSettings['plugins'] as $pluginName => $pluginUserSettings) - { - /* add plugin to plugin Data, if active, set it first */ - /* if plugin is active, list it first */ - if($userSettings['plugins'][$pluginName]['active'] == true) - { - $plugins = array_merge(array($pluginName => null), $plugins); - } - else - { - $plugins[$pluginName] = Null; - } - - /* Check if the user has deleted a plugin. Then delete it in the settings and store the updated settings. */ - if(!is_dir($userSettings['rootPath'] . 'plugins' . DIRECTORY_SEPARATOR . $pluginName)) - { - /* remove the plugin settings and store updated settings */ - \Typemill\Settings::removePluginSettings($pluginName); - continue; - } - - /* load the original plugin definitions from the plugin folder (author, version and stuff) */ - $pluginOriginalSettings = \Typemill\Settings::getObjectSettings('plugins', $pluginName); - if($pluginOriginalSettings) - { - /* store them as default plugin data with plugin author, plugin year, default settings and field-definitions */ - $plugins[$pluginName] = $pluginOriginalSettings; - } - - /* check, if the plugin has been disabled in the form-session-data */ - if(isset($_SESSION['old']) && !isset($_SESSION['old'][$pluginName]['active'])) - { - $plugins[$pluginName]['settings']['active'] = false; - } - - /* if the plugin defines forms and fields, so that the user can edit the plugin settings in the frontend */ - if(isset($pluginOriginalSettings['forms']['fields'])) - { - /* get all the fields and prefill them with the dafault-data, the user-data or old input data */ - $fields = $fieldsModel->getFields($userSettings, 'plugins', $pluginName, $pluginOriginalSettings); - - /* overwrite original plugin form definitions with enhanced form objects */ - $plugins[$pluginName]['forms']['fields'] = $fields; - } - } - - $route = $request->getAttribute('route'); - $navigation = $this->getMainNavigation(); - - # set navigation active - $navigation['Plugins']['active'] = true; - - return $this->render($response, 'settings/plugins.twig', array( - 'settings' => $userSettings, - 'acl' => $this->c->acl, - 'navigation' => $navigation, - 'plugins' => $plugins, - 'route' => $route->getName() - )); - } - - /************************************* - ** SAVE THEME- AND PLUGIN-SETTINGS ** - *************************************/ - - public function saveThemes($request, $response, $args) - { - if($request->isPost()) - { - if( $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('themes.show')); - } - - $userSettings = \Typemill\Settings::getUserSettings(); - $params = $request->getParams(); - $themeName = isset($params['theme']) ? $params['theme'] : false; - $userInput = isset($params[$themeName]) ? $params[$themeName] : false; - $validate = new Validation(); - $themeSettings = \Typemill\Settings::getObjectSettings('themes', $themeName); - - if(isset($themeSettings['settings']['images'])) - { - # get the default settings - $defaultSettings = \Typemill\Settings::getDefaultSettings(); - - # merge the default image settings with the theme image settings, delete all others (image settings from old theme) - $userSettings['images'] = array_merge($defaultSettings['images'], $themeSettings['settings']['images']); - } - - /* set theme name and delete theme settings from user settings for the case, that the new theme has no settings */ - $userSettings['theme'] = $themeName; - - # extract the custom css from user input - $customcss = isset($userInput['customcss']) ? $userInput['customcss'] : false; - - # delete custom css from userinput - unset($userInput['customcss']); - - $write = new write(); - - # make sure no file is set if there is no custom css - if(!$customcss OR $customcss == '') - { - # delete the css file if exists - $write->deleteFileWithPath('cache' . DIRECTORY_SEPARATOR . $themeName . '-custom.css'); - } - else - { - if ( $customcss != strip_tags($customcss) ) - { - $_SESSION['errors'][$themeName]['customcss'][] = 'custom css contains html'; - } - else - { - # store css - $write = new write(); - $write->writeFile('cache', $themeName . '-custom.css', $customcss); - } - } - - if($userInput) - { - # validate the user-input and return image-fields if they are defined - $imageFields = $this->validateInput('themes', $themeName, $userInput, $validate); - - /* set user input as theme settings */ - $userSettings['themes'][$themeName] = $userInput; - } - - # handle images - $images = $request->getUploadedFiles(); - - if(!isset($_SESSION['errors']) && isset($images[$themeName])) - { - $userInput = $this->saveImages($imageFields, $userInput, $userSettings, $images[$themeName]); - - # set user input as theme settings - $userSettings['themes'][$themeName] = $userInput; - } - - /* 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($this->c->router->pathFor('themes.show')); - } - - /* store updated settings */ - \Typemill\Settings::updateSettings($userSettings); - - $this->c->flash->addMessage('info', 'Settings are stored'); - return $response->withRedirect($this->c->router->pathFor('themes.show')); - } - } - - public function savePlugins($request, $response, $args) - { - if($request->isPost()) - { - - if( $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('plugins.show')); - } - - $userSettings = \Typemill\Settings::getUserSettings(); - $pluginSettings = array(); - $userInput = $request->getParams(); - $validate = new Validation(); - - /* use the stored user settings and iterate over all original plugin settings, so we do not forget any... */ - foreach($userSettings['plugins'] as $pluginName => $pluginUserSettings) - { - /* if there are no input-data for this plugin, then use the stored plugin settings */ - if(!isset($userInput[$pluginName])) - { - $pluginSettings[$pluginName] = $pluginUserSettings; - } - else - { - # fetch the original settings from the folder to get the field definitions - $originalSettings = \Typemill\Settings::getObjectSettings('plugins', $pluginName); - - # check if the plugin has dependencies - if(isset($userInput[$pluginName]['active']) && isset($originalSettings['dependencies'])) - { - foreach($originalSettings['dependencies'] as $dependency) - { - if(!isset($userInput[$dependency]['active']) OR !$userInput[$dependency]['active']) - { - $this->c->flash->addMessage('error', 'Activate the plugin ' . $dependency . ' before you activate the plugin ' . $pluginName); - return $response->withRedirect($this->c->router->pathFor('plugins.show')); - } - } - } - - /* validate the user-input */ - $imageFields = $this->validateInput('plugins', $pluginName, $userInput[$pluginName], $validate, $originalSettings); - - /* use the input data */ - $pluginSettings[$pluginName] = $userInput[$pluginName]; - } - - # handle images - $images = $request->getUploadedFiles(); - - if(!isset($_SESSION['errors']) && isset($images[$pluginName])) - { - $userInput[$pluginName] = $this->saveImages($imageFields, $userInput[$pluginName], $userSettings, $images[$pluginName]); - - # set user input as theme settings - $pluginSettings[$pluginName] = $userInput[$pluginName]; - } - - /* deactivate the plugin, if there is no active flag */ - if(!isset($userInput[$pluginName]['active'])) - { - $pluginSettings[$pluginName]['active'] = false; - } - } - - if(isset($_SESSION['errors'])) - { - $this->c->flash->addMessage('error', 'Please correct the errors below'); - } - else - { - /* if everything is valid, add plugin settings to base settings again */ - $userSettings['plugins'] = $pluginSettings; - - /* store updated settings */ - \Typemill\Settings::updateSettings($userSettings); - - $this->c->flash->addMessage('info', 'Settings are stored'); - } - - return $response->withRedirect($this->c->router->pathFor('plugins.show')); - } - } - - /*********************** - ** USER MANAGEMENT ** - ***********************/ - - public function showAccount($request, $response, $args) - { - $username = $_SESSION['user']; - - $validate = new Validation(); - - if($validate->username($username)) - { - # get settings - $settings = $this->c->get('settings'); - - # get user with userdata - $user = new User(); - $userdata = $user->getSecureUser($username); - - # instantiate field-builder - $fieldsModel = new Fields($this->c); - - # get the field-definitions - $fieldDefinitions = $this->getUserFields($userdata['userrole']); - - # prepare userdata for field-builder - $userSettings['users']['user'] = $userdata; - - # generate the input form - $userform = $fieldsModel->getFields($userSettings, 'users', 'user', $fieldDefinitions); - - $route = $request->getAttribute('route'); - $navigation = $this->getMainNavigation(); - - # set navigation active - $navigation['Account']['active'] = true; - - return $this->render($response, 'settings/user.twig', array( - 'settings' => $settings, - 'acl' => $this->c->acl, - 'navigation' => $navigation, - 'usersettings' => $userSettings, // needed for image url in form, will overwrite settings for field-template - 'userform' => $userform, // field model, needed to generate frontend-field - 'userdata' => $userdata, // needed to fill form with data -# 'userrole' => false, // not needed ? -# 'username' => $args['username'], // not needed ? - 'route' => $route->getName() // needed to set link active - )); - } - - $this->c->flash->addMessage('error', 'User does not exists'); - return $response->withRedirect($this->c->router->pathFor('home')); - } - - public function showUser($request, $response, $args) - { - # if user has no rights to watch userlist, then redirect to - if(!$this->c->acl->isAllowed($_SESSION['role'], 'userlist', 'view') && $_SESSION['user'] !== $args['username'] ) - { - return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] )); - } - - # get settings - $settings = $this->c->get('settings'); - - # get user with userdata - $user = new User(); - $userdata = $user->getSecureUser($args['username']); - - if(!$userdata) - { - $this->c->flash->addMessage('error', 'User does not exists'); - return $response->withRedirect($this->c->router->pathFor('user.account')); - } - - # instantiate field-builder - $fieldsModel = new Fields($this->c); - - # get the field-definitions - $fieldDefinitions = $this->getUserFields($userdata['userrole']); - - # prepare userdata for field-builder - $userSettings['users']['user'] = $userdata; - - # generate the input form - $userform = $fieldsModel->getFields($userSettings, 'users', 'user', $fieldDefinitions); - - $route = $request->getAttribute('route'); - $navigation = $this->getMainNavigation(); - - # set navigation active - $navigation['Users']['active'] = true; - - if(isset($userdata['lastlogin'])) - { - $userdata['lastlogin'] = date("d.m.Y H:i:s", $userdata['lastlogin']); - } - - return $this->render($response, 'settings/user.twig', array( - 'settings' => $settings, - 'acl' => $this->c->acl, - 'navigation' => $navigation, - 'usersettings' => $userSettings, // needed for image url in form, will overwrite settings for field-template - 'userform' => $userform, // field model, needed to generate frontend-field - 'userdata' => $userdata, // needed to fill form with data - 'route' => $route->getName() // needed to set link active - )); - } - - public function listUser($request, $response) - { - $user = new User(); - $users = $user->getUsers(); - $userdata = array(); - $route = $request->getAttribute('route'); - $settings = $this->c->get('settings'); - $navigation = $this->getMainNavigation(); - - # set navigation active - $navigation['Users']['active'] = true; - - # set standard template - $template = 'settings/userlist.twig'; - - # use vue template for many users - $totalusers = count($users); - - if($totalusers > 10) - { - $template = 'settings/userlistvue.twig'; - } - else - { - foreach($users as $username) - { - $newuser = $user->getSecureUser($username); - if($newuser) - { - $userdata[] = $newuser; - } - } - } - - return $this->render($response, $template, array( - 'settings' => $settings, - 'acl' => $this->c->acl, - 'navigation' => $navigation, - 'users' => $users, - 'userdata' => $userdata, - 'userroles' => $this->c->acl->getRoles(), - 'route' => $route->getName() - )); - } - - #returns userdata - public function getUsersByNames($request, $response, $args) - { - $params = $request->getParams(); - $user = new User(); - $userdata = []; - - if(isset($params['usernames'])) - { - foreach($params['usernames'] as $username) - { - $existinguser = $user->getSecureUser($username); - if($existinguser) - { - $userdata[] = $existinguser; - } - } - } - - return $response->withJson(['userdata' => $userdata]); - } - - # returns userdata - public function getUsersByEmail($request, $response, $args) - { - $params = $request->getParams(); - $user = new User(); - - $userdata = $user->findUsersByEmail($params['email']); - - return $response->withJson(['userdata' => $userdata ]); - } - - #returns userdata - public function getUsersByRole($request, $response, $args) - { - $params = $request->getParams(); - $user = new User(); - - $userdata = $user->findUsersByRole($params['role']); - - return $response->withJson(['userdata' => $userdata ]); - } - - public function newUser($request, $response, $args) - { - $user = new User(); - $users = $user->getUsers(); - $userroles = $this->c->acl->getRoles(); - $route = $request->getAttribute('route'); - $settings = $this->c->get('settings'); - $navigation = $this->getMainNavigation(); - - # set navigation active - $navigation['Users']['active'] = true; - - return $this->render($response, 'settings/usernew.twig', array( - 'settings' => $settings, - 'acl' => $this->c->acl, - 'navigation' => $navigation, - 'users' => $users, - 'userrole' => $userroles, - 'route' => $route->getName() - )); - } - - public function createUser($request, $response, $args) - { - if($request->isPost()) - { - if( $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('user.new')); - } - - $params = $request->getParams(); - $user = new User(); - $validate = new Validation(); - $userroles = $this->c->acl->getRoles(); - - if($validate->newUser($params, $userroles)) - { - $userdata = array( - 'username' => $params['username'], - 'email' => $params['email'], - 'userrole' => $params['userrole'], - 'password' => $params['password']); - - $user->createUser($userdata); - - $this->c->flash->addMessage('info', 'Welcome, there is a new user!'); - return $response->withRedirect($this->c->router->pathFor('user.list')); - } - - $this->c->flash->addMessage('error', 'Please correct your input'); - return $response->withRedirect($this->c->router->pathFor('user.new')); - } - } - - public function updateUser($request, $response, $args) - { - - if($request->isPost()) - { - if( $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('user.account')); - } - - $params = $request->getParams(); - $userdata = $params['user']; - $user = new User(); - $validate = new Validation(); - $userroles = $this->c->acl->getRoles(); - - $redirectRoute = ($userdata['username'] == $_SESSION['user']) ? $this->c->router->pathFor('user.account') : $this->c->router->pathFor('user.show', ['username' => $userdata['username']]); - - # check if user is allowed to view (edit) userlist and other users - if(!$this->c->acl->isAllowed($_SESSION['role'], 'userlist', 'write')) - { - # if an editor tries to update other userdata than its own */ - if($_SESSION['user'] !== $userdata['username']) - { - return $response->withRedirect($this->c->router->pathFor('user.account')); - } - - # non admins cannot change their userrole, so set it to session-value - $userdata['userrole'] = $_SESSION['role']; - } - - # validate standard fields for users - if($validate->existingUser($userdata, $userroles)) - { - # validate custom input fields and return images - $userfields = $this->getUserFields($userdata['userrole']); - $imageFields = $this->validateInput('users', 'user', $userdata, $validate, $userfields); - - if(!empty($imageFields)) - { - $images = $request->getUploadedFiles(); - - if(isset($images['user'])) - { - # set image size - $settings = $this->c->get('settings'); - $imageSizes = $settings['images']; - $imageSizes['live'] = ['width' => 500, 'height' => 500]; - $settings->replace(['images' => $imageSizes]); - $imageresult = $this->saveImages($imageFields, $userdata, $settings, $images['user']); - - if(isset($_SESSION['slimFlash']['error'])) - { - return $response->withRedirect($redirectRoute); - } - elseif(isset($imageresult['username'])) - { - $userdata = $imageresult; - } - } - } - - # 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($redirectRoute); - } - - if(empty($userdata['password']) AND empty($userdata['newpassword'])) - { - # make sure no invalid passwords go into model - unset($userdata['password']); - unset($userdata['newpassword']); - - $user->updateUser($userdata); - $this->c->flash->addMessage('info', 'Saved all changes'); - return $response->withRedirect($redirectRoute); - } - elseif($validate->newPassword($userdata)) - { - $userdata['password'] = $userdata['newpassword']; - unset($userdata['newpassword']); - - $user->updateUser($userdata); - $this->c->flash->addMessage('info', 'Saved all changes'); - return $response->withRedirect($redirectRoute); - } - } - - # change error-array for formbuilder - $errors = $_SESSION['errors']; - unset($_SESSION['errors']); - $_SESSION['errors']['user'] = $errors;# - - $this->c->flash->addMessage('error', 'Please correct your input'); - return $response->withRedirect($redirectRoute); - } - } - - public function deleteUser($request, $response, $args) - { - if($request->isPost()) - { - if( $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('user.account')); - } - - $params = $request->getParams(); - $validate = new Validation(); - $user = new User(); - - # check if user is allowed to view (edit) userlist and other users - if(!$this->c->acl->isAllowed($_SESSION['role'], 'userlist', 'write')) - { - # if an editor tries to delete other user than its own - if($_SESSION['user'] !== $params['username']) - { - return $response->withRedirect($this->c->router->pathFor('user.account')); - } - } - - if($validate->username($params['username'])) - { - $userdata = $user->getSecureUser($params['username']); - if(!$userdata) - { - $this->c->flash->addMessage('error', 'Ups, we did not find that user'); - return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']])); - } - - $user->deleteUser($params['username']); - - $this->c->dispatcher->dispatch('onUserDeleted', new OnUserDeleted($userdata)); - - # if user deleted his own account - if($_SESSION['user'] == $params['username']) - { - session_destroy(); - return $response->withRedirect($this->c->router->pathFor('auth.show')); - } - - $this->c->flash->addMessage('info', 'Say goodbye, the user is gone!'); - return $response->withRedirect($this->c->router->pathFor('user.list')); - } - - $this->c->flash->addMessage('error', 'Ups, it is not a valid username'); - return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']])); - } - } - - public function clearCache($request, $response, $args) - { - $this->uri = $request->getUri()->withUserInfo(''); - $dir = $this->settings['basePath'] . 'cache'; - - $error = $this->writeCache->deleteCacheFiles($dir); - if($error) - { - return $response->withJson(['errors' => $error], 500); - } - - # create a new draft structure - $this->setFreshStructureDraft(); - - # create a new draft structure - $this->setFreshStructureLive(); - - # create a new draft structure - $this->setFreshNavigation(); - - # update the sitemap - $this->updateSitemap(); - - return $response->withJson(array('errors' => false)); - } - - private function getUserFields($role) - { - # if a plugin with a role has been deactivated, then users with the role throw an error, so set them back to member... - if(!$this->c->acl->hasRole($role)) - { - $role = 'member'; - } - - $fields = []; - $fields['username'] = ['label' => 'Username (read only)', 'type' => 'text', 'readonly' => true]; - $fields['firstname'] = ['label' => 'First Name', 'type' => 'text']; - $fields['lastname'] = ['label' => 'Last Name', 'type' => 'text']; - $fields['email'] = ['label' => 'E-Mail', 'type' => 'text', 'required' => true]; - $fields['userrole'] = ['label' => 'Role', 'type' => 'text', 'readonly' => true]; - $fields['password'] = ['label' => 'Actual Password', 'type' => 'password']; - $fields['newpassword'] = ['label' => 'New Password', 'type' => 'password']; - - # dispatch fields; - $fields = $this->c->dispatcher->dispatch('onUserfieldsLoaded', new OnUserfieldsLoaded($fields))->getData(); - - # only roles who can edit content need profile image and description - if($this->c->acl->isAllowed($role, 'mycontent', 'create')) - { - $newfield['image'] = ['label' => 'Profile-Image', 'type' => 'image']; - $newfield['description'] = ['label' => 'Author-Description (Markdown)', 'type' => 'textarea']; - - $fields = array_slice($fields, 0, 1, true) + $newfield + array_slice($fields, 1, NULL, true); - # array_splice($fields,1,0,$newfield); - } - - # Only admin can change userroles - if($this->c->acl->isAllowed($_SESSION['role'], 'userlist', 'write')) - { - $userroles = $this->c->acl->getRoles(); - $options = []; - - # we need associative array to make select-field with key/value work - foreach($userroles as $userrole) - { - $options[$userrole] = $userrole; - } - - $fields['userrole'] = ['label' => 'Role', 'type' => 'select', 'options' => $options]; - } - - $userform = []; - $userform['forms']['fields'] = $fields; - return $userform; - } - - private function getThemes() - { - $themeFolder = $this->c->get('settings')['rootPath'] . $this->c->get('settings')['themeFolder']; - $themeFolderC = scandir($themeFolder); - $themes = array(); - foreach ($themeFolderC as $key => $theme) - { - if (!in_array($theme, array(".",".."))) - { - if (is_dir($themeFolder . DIRECTORY_SEPARATOR . $theme)) - { - $themes[] = $theme; - } - } - } - return $themes; - } - - private function getCopyright() - { - return array( - "©", - "CC-BY", - "CC-BY-NC", - "CC-BY-NC-ND", - "CC-BY-NC-SA", - "CC-BY-ND", - "CC-BY-SA", - "None" - ); - } - - private function getLanguages() - { - return array( - 'en' => 'English', - 'ru' => 'Russian', - 'nl' => 'Dutch, Flemish', - 'de' => 'German', - 'it' => 'Italian', - 'fr' => 'French', - ); - } - - private function getMainNavigation() - { - $navigation = [ - 'System' => ['routename' => 'settings.show', 'icon' => 'icon-wrench', 'aclresource' => 'system', 'aclprivilege' => 'view'], - 'Themes' => ['routename' => 'themes.show', 'icon' => 'icon-paint-brush', 'aclresource' => 'system', 'aclprivilege' => 'view'], - 'Plugins' => ['routename' => 'plugins.show', 'icon' => 'icon-plug', 'aclresource' => 'system', 'aclprivilege' => 'view'], - 'Account' => ['routename' => 'user.account', 'icon' => 'icon-user', 'aclresource' => 'user', 'aclprivilege' => 'view'], - 'Users' => ['routename' => 'user.list', 'icon' => 'icon-group', 'aclresource' => 'userlist', 'aclprivilege' => 'view'] - ]; - - # dispatch fields; - $navigation = $this->c->dispatcher->dispatch('onSystemnaviLoaded', new OnSystemnaviLoaded($navigation))->getData(); - - return $navigation; - } - - private function validateInput($objectType, $objectName, $userInput, $validate, $originalSettings = NULL) - { - if(!$originalSettings) - { - # fetch the original settings from the folder (plugin or theme) to get the field definitions - $originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName); - } - - # images get special treatment - $imageFieldDefinitions = array(); - - if(isset($originalSettings['forms']['fields'])) - { - /* flaten the multi-dimensional array with fieldsets to a one-dimensional array */ - $originalFields = array(); - foreach($originalSettings['forms']['fields'] as $fieldName => $fieldValue) - { - if(isset($fieldValue['fields'])) - { - foreach($fieldValue['fields'] as $subFieldName => $subFieldValue) - { - $originalFields[$subFieldName] = $subFieldValue; - } - } - else - { - $originalFields[$fieldName] = $fieldValue; - } - } - - # if plugin is not active, then skip required - $skiprequired = false; - if($objectType == 'plugins' && !isset($userInput['active'])) - { - $skiprequired = true; - } - - /* 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) - { - - # check if the field is a select field with dataset = userroles - if(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 */ - $validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition, $skiprequired); - - if($fieldDefinition['type'] == 'image') - { - # we want to return all images-fields for further processing - $imageFieldDefinitions[$fieldName] = $fieldDefinition; - } - } - if(!$fieldDefinition && $objectType != 'users' && $fieldName != 'active') - { - $_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!'); - } - } - } - - return $imageFieldDefinitions; - } - - protected function saveImages($imageFields, $userInput, $userSettings, $files) - { - # initiate image processor with standard image sizes - $processImages = new ProcessImage($userSettings['images']); - - if(!$processImages->checkFolders('images')) - { - $this->c->flash->addMessage('error', 'Please make sure that your media folder exists and is writable.'); - return false; - } - - foreach($imageFields as $fieldName => $imageField) - { - if(isset($userInput[$fieldName])) - { - # handle single input with single file upload - $image = $files[$fieldName]; - - if($image->getError() === UPLOAD_ERR_OK) - { - # not the most elegant, but createImage expects a base64-encoded string. - $imageContent = $image->getStream()->getContents(); - $imageData = base64_encode($imageContent); - $imageSrc = 'data: ' . $image->getClientMediaType() . ';base64,' . $imageData; - - if($processImages->createImage($imageSrc, $image->getClientFilename(), $userSettings['images'], $overwrite = NULL)) - { - # returns image path to media library - $userInput[$fieldName] = $processImages->publishImage(); - } - } - } - } - return $userInput; - } - -} \ No newline at end of file diff --git a/system/Controllers/ControllerShared.php b/system/Controllers/ControllerShared.php deleted file mode 100644 index 11cb20b..0000000 --- a/system/Controllers/ControllerShared.php +++ /dev/null @@ -1,477 +0,0 @@ -c = $c; - $this->settings = $this->c->get('settings'); - - # used everywhere so instantiate it - $this->writeCache = new writeCache(); - - $this->c->dispatcher->dispatch('onTwigLoaded'); - } - - # render page for frontend - protected function render($response, $route, $data) - { - # why commented this out?? - $data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData(); - - if(isset($_SESSION['old'])) - { - unset($_SESSION['old']); - } - - $response = $response->withoutHeader('Server'); - $response = $response->withAddedHeader('X-Powered-By', 'Typemill'); - - if(!isset($this->settings['headersoff']) or !$this->settings['headersoff']) - { - $response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff'); - $response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN'); - $response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block'); - $response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade'); - if($this->c->request->getUri()->getScheme() == 'https') - { - $response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000'); - } - } - - return $this->c->view->render($response, $route, $data); - } - - # render 404 for frontend - protected function render404($response, $data = NULL) - { - return $this->c->view->render($response->withStatus(404), '/404.twig', $data); - } - - # render page for authors (admin-area) - protected function renderIntern($response, $route, $data) - { - if(isset($_SESSION['old'])) - { - unset($_SESSION['old']); - } - - $response = $response->withoutHeader('Server'); - $response = $response->withAddedHeader('X-Powered-By', 'Typemill'); - - if(!isset($this->settings['headersoff']) or !$this->settings['headersoff']) - { - $response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff'); - $response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN'); - $response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block'); - $response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade'); - if($this->c->request->getUri()->getScheme() == 'https') - { - $response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000'); - } - } - - return $this->c->view->render($response, $route, $data); - } - - # render 404 for authors - protected function renderIntern404($response, $data = NULL) - { - return $this->c->view->render($response->withStatus(404), '/intern404.twig', $data); - } - - # reads the cached structure with published and non-published pages for the author - protected function setStructureDraft() - { - # get the cached structure - $this->structureDraft = $this->writeCache->getCache('cache', $this->structureDraftName); - - # if there is no cached structure - if(!$this->structureDraft) - { - return $this->setFreshStructureDraft(); - } - - return true; - } - - # creates a fresh structure with published and non-published pages for the author - protected function setFreshStructureDraft() - { - # scan the content of the folder - $pagetreeDraft = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft = true ); - - # if there is content, then get the content details - if(count($pagetreeDraft) > 0) - { - # get the extended structure files with changes like navigation title or hidden pages - $yaml = new writeYaml(); - $extended = $this->getExtended(); - - # create an array of object with the whole content of the folder and changes from extended file - $this->structureDraft = Folder::getFolderContentDetails($pagetreeDraft, $extended, $this->settings, $this->uri->getBaseUrl(), $this->uri->getBasePath()); - - # cache structure draft - $this->writeCache->updateCache('cache', $this->structureDraftName, 'lastCache.txt', $this->structureDraft); - - return true; - } - - return false; - } - - # reads the cached structure of published pages - protected function setStructureLive() - { - # get the cached structure - $this->structureLive = $this->writeCache->getCache('cache', $this->structureLiveName); - - # if there is no cached structure - if(!$this->structureLive) - { - return $this->setFreshStructureLive(); - } - - return true; - } - - # creates a fresh structure with published pages - protected function setFreshStructureLive() - { - # scan the content of the folder - $pagetreeLive = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft = false ); - - # if there is content, then get the content details - if($pagetreeLive && count($pagetreeLive) > 0) - { - # get the extended structure files with changes like navigation title or hidden pages - $yaml = new writeYaml(); - $extended = $this->getExtended(); - - # create an array of object with the whole content of the folder and changes from extended file - $this->structureLive = Folder::getFolderContentDetails($pagetreeLive, $extended, $this->settings, $this->uri->getBaseUrl(), $this->uri->getBasePath()); - - # cache structure live - $this->writeCache->updateCache('cache', $this->structureLiveName, 'lastCache.txt', $this->structureLive); - - return true; - } - - return false; - } - - # reads the live navigation from cache (live structure without hidden pages) - protected function setNavigation() - { - # get the cached structure - $this->navigation = $this->writeCache->getCache('cache', 'navigation.txt'); - - # if there is no cached structure - if(!$this->navigation) - { - return $this->setFreshNavigation(); - } - - return true; - } - - # creates a fresh live navigation (live structure without hidden pages) - protected function setFreshNavigation() - { - - if(!$this->extended) - { - $extended = $this->getExtended(); - } - - if($this->containsHiddenPages($this->extended)) - { - if(!$this->structureLive) - { - $this->setStructureLive(); - } - - $structureLive = $this->c->dispatcher->dispatch('onPagetreeLoaded', new OnPagetreeLoaded($this->structureLive))->getData(); - $this->navigation = $this->createNavigation($structureLive); - - # cache navigation - $this->writeCache->updateCache('cache', 'navigation.txt', false, $this->navigation); - - return true; - } - - # make sure no old navigation file is left - $this->writeCache->deleteFileWithPath('cache' . DIRECTORY_SEPARATOR . 'navigation.txt'); - - return false; - } - - # create navigation from structure - protected function createNavigation($structureLive) - { - foreach ($structureLive as $key => $element) - { - if($element->hide === true) - { - unset($structureLive[$key]); - } - elseif(isset($element->folderContent)) - { - $structureLive[$key]->folderContent = $this->createNavigation($element->folderContent); - } - } - - return $structureLive; - } - - # controllerFrontendWebsite, but not in use, makes no sense to check on each page load - public function checkSitemap() - { - if(!$this->writeCache->getCache('cache', 'sitemap.xml')) - { - if(!$this->structureLive) - { - $this->setStructureLive(); - } - - $this->updateSitemap(); - } - - return true; - } - - public function updateSitemap($ping = false) - { - $sitemap = '' . "\n"; - $sitemap .= '' . "\n"; - $sitemap = $this->addUrlSet($sitemap, $this->uri->getBaseUrl()); - $sitemap .= $this->generateUrlSets($this->structureLive); - $sitemap .= ''; - - $this->writeCache->writeFile('cache', 'sitemap.xml', $sitemap); - - if($ping && isset($this->settings['pingsitemap']) && $this->settings['pingsitemap']) - { - $sitemapUrl = $this->uri->getBaseUrl() . '/cache/sitemap.xml'; - - $pingGoogleUrl = 'http://www.google.com/ping?sitemap=' . urlencode($sitemapUrl); - $pingBingUrl = 'http://www.bing.com/ping?sitemap=' . urlencode($sitemapUrl); - - $opts = array( - 'http'=>array( - 'method'=>"GET", - 'ignore_errors' => true, - 'timeout' => 5 - ) - ); - - $context = stream_context_create($opts); - - $responseBing = file_get_contents($pingBingUrl, false, $context); - $responseGoogle = file_get_contents($pingGoogleUrl, false, $context); - } - - } - - public function generateUrlSets($structureLive) - { - $urlset = ''; - - foreach($structureLive as $item) - { - if($item->elementType == 'folder' && isset($item->noindex) && $item->noindex === true) - { - $urlset .= $this->generateUrlSets($item->folderContent, $urlset); - } - elseif($item->elementType == 'folder') - { - $urlset = $this->addUrlSet($urlset, $item->urlAbs); - $urlset .= $this->generateUrlSets($item->folderContent, $urlset); - } - elseif(isset($item->noindex) && $item->noindex === true ) - { - continue; - } - else - { - $urlset = $this->addUrlSet($urlset, $item->urlAbs); - } - } - return $urlset; - } - - public function addUrlSet($urlset, $url) - { - $urlset .= ' ' . "\n"; - $urlset .= ' ' . $url . '' . "\n"; - $urlset .= ' ' . "\n"; - return $urlset; - } - - protected function getExtended() - { - $yaml = new writeYaml(); - - if(!$this->extended) - { - $this->extended = $yaml->getYaml('cache', 'structure-extended.yaml'); - } - - if(!$this->extended) - { - # scan the content of the folder - $pagetreeDraft = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft = true ); - - # if there is content, then get the content details - if(count($pagetreeDraft) == 0) - { - return false; - } - - # create an array of object with the whole content of the folder and changes from extended file - $structureDraft = Folder::getFolderContentDetails($pagetreeDraft, $extended = false, $this->settings, $this->uri->getBaseUrl(), $this->uri->getBasePath()); - - $this->extended = $this->createExtended($this->settings['rootPath'] . $this->settings['contentFolder'], $yaml, $structureDraft); - - $yaml->updateYaml('cache', 'structure-extended.yaml', $this->extended); - } - - return $this->extended; - } - - # creates a file that holds all hide flags and navigation titles - # reads all meta-files and creates an array with url => ['hide' => bool, 'navtitle' => 'bla'] - public function createExtended($contentPath, $yaml, $structureLive, $extended = NULL) - { - if(!$extended) - { - $extended = []; - } - - foreach ($structureLive as $key => $item) - { - # $filename = ($item->elementType == 'folder') ? DIRECTORY_SEPARATOR . 'index.yaml' : $item->pathWithoutType . '.yaml'; - $filename = $item->pathWithoutType . '.yaml'; - - if(file_exists($contentPath . $filename)) - { - # read file - $meta = $yaml->getYaml('content', $filename); - - $extended[$item->urlRelWoF]['hide'] = isset($meta['meta']['hide']) ? $meta['meta']['hide'] : false; - $extended[$item->urlRelWoF]['navtitle'] = isset($meta['meta']['navtitle']) ? $meta['meta']['navtitle'] : ''; - } - - if ($item->elementType == 'folder') - { - $extended = $this->createExtended($contentPath, $yaml, $item->folderContent, $extended); - } - } - return $extended; - } - - # only backoffice - protected function renameExtended($item, $newFolder) - { - # get the extended structure files with changes like navigation title or hidden pages - $yaml = new writeYaml(); - $extended = $yaml->getYaml('cache', 'structure-extended.yaml'); - - if(isset($extended[$item->urlRelWoF])) - { - $newUrl = $newFolder->urlRelWoF . '/' . $item->slug; - - $entry = $extended[$item->urlRelWoF]; - - unset($extended[$item->urlRelWoF]); - - $extended[$newUrl] = $entry; - $yaml->updateYaml('cache', 'structure-extended.yaml', $extended); - } - - return true; - } - - # only backoffice - protected function deleteFromExtended() - { - # get the extended structure files with changes like navigation title or hidden pages - $yaml = new writeYaml(); - $extended = $yaml->getYaml('cache', 'structure-extended.yaml'); - - if($this->item->elementType == "file" && isset($extended[$this->item->urlRelWoF])) - { - unset($extended[$this->item->urlRelWoF]); - $yaml->updateYaml('cache', 'structure-extended.yaml', $extended); - } - - if($this->item->elementType == "folder") - { - $changed = false; - - # delete all entries with that folder url - foreach($extended as $url => $entries) - { - if( strpos($url, $this->item->urlRelWoF) !== false ) - { - $changed = true; - unset($extended[$url]); - } - } - - if($changed) - { - $yaml->updateYaml('cache', 'structure-extended.yaml', $extended); - } - } - } - - # checks if there is a hidden page, returns true on first find - protected function containsHiddenPages($extended) - { - foreach($extended as $element) - { - if(isset($element['hide']) && $element['hide'] === true) - { - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/system/Events/BaseEvent.php b/system/Events/BaseEvent.php deleted file mode 100644 index f902f4e..0000000 --- a/system/Events/BaseEvent.php +++ /dev/null @@ -1,25 +0,0 @@ -data = $data; - } - - public function getData() - { - return $this->data; - } - - public function setData($data) - { - $this->data = $data; - } -} \ No newline at end of file diff --git a/system/Events/OnBreadcrumbLoaded.php b/system/Events/OnBreadcrumbLoaded.php deleted file mode 100644 index 56c1fa9..0000000 --- a/system/Events/OnBreadcrumbLoaded.php +++ /dev/null @@ -1,14 +0,0 @@ -data = $data; - } - - public function getMarkdown() - { - return $this->data; - } - - public function getHTML($urlrel) - { - $parsedown = new ParsedownExtension(); - $contentArray = $parsedown->text($this->data); - $contentHTML = $parsedown->markup($contentArray, $urlrel); - - return $contentHTML; - } -} \ No newline at end of file diff --git a/system/Events/OnPageDeleted.php b/system/Events/OnPageDeleted.php deleted file mode 100644 index 0240baa..0000000 --- a/system/Events/OnPageDeleted.php +++ /dev/null @@ -1,14 +0,0 @@ -settings = $settings; - - $this->dispatcher = $dispatcher; - - # show anchor next to headline? - $this->showAnchor = isset($settings['headlineanchors']) ? $settings['headlineanchors'] : false; - - # extend link schemes - $urlschemes = ( isset($settings['urlschemes']) && !empty($settings['urlschemes']) ) ? explode(",", $settings['urlschemes']) : false; - if($urlschemes) - { - foreach($urlschemes as $urlschema) - { - $this->safeLinksWhitelist[] = $urlschema; - } - } - - # base url is needed for media/images and relative links (e.g. if www.mydomain.com/mywebsite) - $this->baseUrl = $baseUrl; - - # math support - $this->BlockTypes['\\'][] = 'Math'; - $this->BlockTypes['$'][] = 'Math'; - - $this->InlineTypes['\\'][] = 'Math'; - $this->InlineTypes['$'][] = 'Math'; - $this->InlineTypes['['][] = 'Shortcode'; - $this->inlineMarkerList .= '\\'; - $this->inlineMarkerList .= '$'; - - $this->BlockTypes['!'][] = 'Image'; - $this->BlockTypes['!'][] = "Notice"; - - $this->visualMode = false; - - # identify Shortcodes after footnotes and links - array_unshift($this->BlockTypes['['], 'Shortcode'); - - # identify Table Of contents after footnotes and links and shortcodes - array_unshift($this->BlockTypes['['], 'TableOfContents'); - } - - public function extendLinksWhitelist($linktypes) - { - /* - if($linktypes) - { - $this->safeLinksWhitelist[] = ; - } - */ - } - - public function setVisualMode() - { - $this->visualMode = true; - } - - public function text($text, $relurl = null) - { - $Elements = $this->textElements($text); - - return $Elements; - } - - public function markup($Elements) - { - # convert to markup - $markup = $this->elements($Elements); - - # trim line breaks - $markup = trim($markup, "\n"); - - # merge consecutive dl elements - $markup = preg_replace('/<\/dl>\s+
\s+/', '', $markup); - - # create table of contents - if(isset($this->DefinitionData['TableOfContents'])) - { - $TOC = $this->buildTOC($this->headlines); - - $markup = preg_replace('%(]*>\[TOC\]

)%i', $TOC, $markup); - } - - # add footnotes - if (isset($this->DefinitionData['Footnote'])) - { - $Element = $this->buildFootnoteElement(); - - $markup .= "\n" . $this->element($Element); - } - return $markup; - } - - protected $imageAttributes = true; - - public function withoutImageAttributes() - { - $this->imageAttributes = false; - } - - # BlockImages with html5 figure and figcaption - # No, this is not the most elegant code on planet earth!! - protected function blockImage($line, $block) - { - if (preg_match('/^\!\[/', $line['text'], $matches)) - { - - $Block = array( - 'element' => array( - 'name' => 'figure', - 'elements' => array( - ) - ), - ); - - $Elements = array( - 'handler' => array( - 'function' => 'lineElements', - 'argument' => $line['text'], - 'destination' => 'elements', - ) - ); - - if (preg_match('/[ ]*{('.$this->regexAttribute.'+)}/', $line['text'], $matches, PREG_OFFSET_CAPTURE)) - { - $attributeString = $matches[1][0]; - $dataAttributes = $this->parseAttributeData($attributeString); - - # move classes and ids from img to the figure element - $figureAttributes = array(); - if(isset($dataAttributes['class'])) - { - $figureAttributes['class'] = $dataAttributes['class']; - $classes = explode(' ', $dataAttributes['class']); - foreach($classes as $class) - { - $attributeString = str_replace('.'.$class, '', $attributeString); - } - } - if(isset($dataAttributes['id'])) - { - $figureAttributes['id'] = $dataAttributes['id']; - $attributeString = str_replace('#'.$dataAttributes['id'], '', $attributeString); - } - - $attributeString = trim(str_replace(' ', ' ', $attributeString)); - $line['text'] = substr($line['text'], 0, $matches[0][1]); - if(str_replace(' ', '', $attributeString) != '' && $this->imageAttributes) - { - $line['text'] .= '{' . $attributeString . '}'; - } - - $Block['element']['attributes'] = $figureAttributes; - - $Elements['handler']['argument'] = $line['text']; - } - - $Block['element']['elements'][] = $Elements; - - return $Block; - } - } - - protected function blockImageContinue($line, $block) - { - if (isset($block['complete'])) - { - return; - } - - # A blank newline has occurred, so it is a new content-block and not a caption - if (isset($block['interrupted'])) - { - return; - } - - $block['element']['elements'][] = array( - 'name' => 'figcaption', - 'handler' => array( - 'function' => 'lineElements', - 'argument' => $line['text'], - 'destination' => 'elements', - ) - ); - - $block['complete'] = true; - - return $block; - } - - protected function blockImageComplete($block) - { - return $block; - } - - protected function inlineImage($excerpt) - { - $image = parent::inlineImage($excerpt); - - if ( ! isset($image)) - { - return null; - } - - $image['element']['attributes']['loading'] = "lazy"; - - return $image; - } - - protected function blockTable($Line, array $Block = null) - { - - $Block = parent::blockTable($Line, $Block); - - if($Block) - { - $table = $Block['element']; - - $Block['element'] = [ - 'name' => 'div', - 'element' => $table, - 'attributes' => [ - 'class' => "tm-table", - ], - ]; - } - - return $Block; - } - - protected function blockTableContinue($Line, array $Block) - { - if (isset($Block['interrupted'])) - { - return; - } - - if (count($Block['alignments']) === 1 or $Line['text'][0] === '|' or strpos($Line['text'], '|')) - { - $Elements = array(); - - $row = $Line['text']; - - $row = trim($row); - $row = trim($row, '|'); - - preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]++`|`)++/', $row, $matches); - - $cells = array_slice($matches[0], 0, count($Block['alignments'])); - - foreach ($cells as $index => $cell) - { - $cell = trim($cell); - - $Element = array( - 'name' => 'td', - 'handler' => array( - 'function' => 'lineElements', - 'argument' => $cell, - 'destination' => 'elements', - ) - ); - - if (isset($Block['alignments'][$index])) - { - $Element['attributes'] = array( - 'style' => 'text-align: ' . $Block['alignments'][$index] . ';', - ); - } - - $Elements []= $Element; - } - - $Element = array( - 'name' => 'tr', - 'elements' => $Elements, - ); - - $Block['element']['element']['elements'][1]['elements'] []= $Element; - - return $Block; - } - } - - # Handle notice blocks - # adopted from grav: https://github.com/getgrav/grav-plugin-markdown-notices/blob/develop/markdown-notices.php - # and yellow / datenstrom: https://raw.githubusercontent.com/datenstrom/yellow-extensions/master/features/markdown/markdownx.php - protected function blockNotice($Line, $Block) - { - if (preg_match("/^!(?!\[)[ ]?+(.*+)/", $Line["text"], $matches)) - { - $level = strspn(str_replace(array("![", " "), "", $Line["text"]), "!"); - $text = substr($matches[0], $level); - - $Block = [ - 'element' => [ - 'name' => 'div', - 'handler' => array( - 'function' => 'linesElements', - 'argument' => (array) $text, - 'destination' => 'elements', - ), - 'attributes' => [ - 'class' => "notice$level", - ], - ], - ]; - - return $Block; - } - } - - # Handle notice blocks over multiple lines - # adopted from grav: https://github.com/getgrav/grav-plugin-markdown-notices/blob/develop/markdown-notices.php - # and yellow / datenstrom: https://raw.githubusercontent.com/datenstrom/yellow-extensions/master/features/markdown/markdownx.php - protected function blockNoticeContinue($Line, $Block) - { - if (isset($Block['interrupted'])) - { - return; - } - - if (preg_match("/^!(?!\[)[ ]?+(.*+)/", $Line["text"], $matches) ) - { - $level = strspn(str_replace(array("![", " "), "", $Line["text"]), "!"); - $text = substr($matches[0], $level); - - $Block['element']['handler']['argument'][] = $text; - return $Block; - } - } - - - # Headlines - public $headlines = array(); - - protected function blockHeader($Line) - { - if (isset($Line['text'][1])) - { - $level = strspn($Line['text'], '#'); - - if ($level > 6) - { - return; - } - - $text = trim($Line['text'], '#'); - $headline = Slug::createSlug($Line['text'], $this->settings); - - if ($this->strictMode and isset($text[0]) and $text[0] !== ' ') - { - return; - } - - $text = trim($text, ' '); - - $tocText = $text; - - if($this->showAnchor && $level > 1) - { - $text = "[#](#h-$headline){.tm-heading-anchor}" . $text; - } - - $Block = array( - 'element' => array( - 'name' => 'h' . min(6, $level), - 'attributes' => array( - 'id' => "h-$headline" - ), - 'handler' => array( - 'function' => 'lineElements', - 'argument' => $text, - 'destination' => 'elements', - ), - ) - ); - - $this->headlines[] = array('level' => $level, 'name' => $Block['element']['name'], 'attribute' => $Block['element']['attributes']['id'], 'text' => $tocText); - - return $Block; - } - } - - - # TableOfContents - protected function blockTableOfContents($line, $block) - { - if ($line['text'] == '[TOC]') - { - $this->DefinitionData['TableOfContents'] = true; - } - } - - - # build the markup for table of contents - public function buildTOC($headlines) - { - $markup = '