diff --git a/composer.lock b/composer.lock index cbf3ebf..4d8ba9f 100644 --- a/composer.lock +++ b/composer.lock @@ -346,16 +346,16 @@ }, { "name": "laminas/laminas-permissions-acl", - "version": "2.12.0", + "version": "2.14.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-permissions-acl.git", - "reference": "0d88f430953fbcbce382f09090db28905b90d60f" + "reference": "86cecb540cf8f2e088d70d8acef1fc9203ed5023" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-permissions-acl/zipball/0d88f430953fbcbce382f09090db28905b90d60f", - "reference": "0d88f430953fbcbce382f09090db28905b90d60f", + "url": "https://api.github.com/repos/laminas/laminas-permissions-acl/zipball/86cecb540cf8f2e088d70d8acef1fc9203ed5023", + "reference": "86cecb540cf8f2e088d70d8acef1fc9203ed5023", "shasum": "" }, "require": { @@ -366,11 +366,12 @@ "zendframework/zend-permissions-acl": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "~2.4.0", + "laminas/laminas-coding-standard": "~2.5.0", "laminas/laminas-servicemanager": "^3.19", - "phpunit/phpunit": "^9.5.25", - "psalm/plugin-phpunit": "^0.17.0", - "vimeo/psalm": "^4.29" + "phpbench/phpbench": "^1.2", + "phpunit/phpunit": "^9.5.26", + "psalm/plugin-phpunit": "^0.18.0", + "vimeo/psalm": "^5.0" }, "suggest": { "laminas/laminas-servicemanager": "To support Laminas\\Permissions\\Acl\\Assertion\\AssertionManager plugin manager usage" @@ -405,20 +406,20 @@ "type": "community_bridge" } ], - "time": "2022-10-17T04:26:35+00:00" + "time": "2023-02-01T16:19:54+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.2.2", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "47afb7fae28ed29057fdca37e16a84f90cc62fae" + "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/47afb7fae28ed29057fdca37e16a84f90cc62fae", - "reference": "47afb7fae28ed29057fdca37e16a84f90cc62fae", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", + "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", "shasum": "" }, "require": { @@ -465,7 +466,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2022-09-08T13:45:54+00:00" + "time": "2023-01-30T18:31:20+00:00" }, { "name": "nikic/fast-route", @@ -692,22 +693,22 @@ }, { "name": "php-di/slim-bridge", - "version": "3.2.0", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/PHP-DI/Slim-Bridge.git", - "reference": "1644a2f31079e92a14cebbf90c7f71ebcbe39ee6" + "reference": "9374b67ebf2f135b32c34907b7891b02b935d845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/Slim-Bridge/zipball/1644a2f31079e92a14cebbf90c7f71ebcbe39ee6", - "reference": "1644a2f31079e92a14cebbf90c7f71ebcbe39ee6", + "url": "https://api.github.com/repos/PHP-DI/Slim-Bridge/zipball/9374b67ebf2f135b32c34907b7891b02b935d845", + "reference": "9374b67ebf2f135b32c34907b7891b02b935d845", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", "php-di/invoker": "^2.0.0", - "php-di/php-di": "^6.0.0", + "php-di/php-di": "^6.0|^7.0", "slim/slim": "^4.2.0" }, "require-dev": { @@ -727,9 +728,9 @@ "description": "PHP-DI integration in Slim", "support": { "issues": "https://github.com/PHP-DI/Slim-Bridge/issues", - "source": "https://github.com/PHP-DI/Slim-Bridge/tree/3.2.0" + "source": "https://github.com/PHP-DI/Slim-Bridge/tree/3.3.0" }, - "time": "2021-11-01T16:14:12+00:00" + "time": "2023-01-13T15:49:44+00:00" }, { "name": "psr/container", @@ -1147,30 +1148,30 @@ }, { "name": "slim/csrf", - "version": "1.2.1", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/slimphp/Slim-Csrf.git", - "reference": "ee811a258ecee807846aefc51aabc1963ae0a400" + "reference": "ebaaf295fd6d7224078d8ae3bba45329b31798c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim-Csrf/zipball/ee811a258ecee807846aefc51aabc1963ae0a400", - "reference": "ee811a258ecee807846aefc51aabc1963ae0a400", + "url": "https://api.github.com/repos/slimphp/Slim-Csrf/zipball/ebaaf295fd6d7224078d8ae3bba45329b31798c7", + "reference": "ebaaf295fd6d7224078d8ae3bba45329b31798c7", "shasum": "" }, "require": { - "php": "^7.3|^8.0", + "php": "^7.4 || ^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0", "psr/http-server-handler": "^1.0", "psr/http-server-middleware": "^1.0" }, "require-dev": { - "phpspec/prophecy": "^1.12", + "phpspec/prophecy": "^1.15", "phpspec/prophecy-phpunit": "^2.0", "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "^3.5.8" + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { @@ -1190,7 +1191,7 @@ } ], "description": "Slim Framework 4 CSRF protection PSR-15 middleware", - "homepage": "http://slimframework.com", + "homepage": "https://www.slimframework.com", "keywords": [ "csrf", "framework", @@ -1199,9 +1200,9 @@ ], "support": { "issues": "https://github.com/slimphp/Slim-Csrf/issues", - "source": "https://github.com/slimphp/Slim-Csrf/tree/1.2.1" + "source": "https://github.com/slimphp/Slim-Csrf/tree/1.3.0" }, - "time": "2021-02-04T15:37:21+00:00" + "time": "2022-11-05T19:27:53+00:00" }, { "name": "slim/flash", @@ -1257,40 +1258,40 @@ }, { "name": "slim/psr7", - "version": "1.5", + "version": "1.6", "source": { "type": "git", "url": "https://github.com/slimphp/Slim-Psr7.git", - "reference": "a47b43a8da7c0208b4c228af0cb29ea36080635a" + "reference": "3471c22c1a0d26c51c78f6aeb06489d38cf46a4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/a47b43a8da7c0208b4c228af0cb29ea36080635a", - "reference": "a47b43a8da7c0208b4c228af0cb29ea36080635a", + "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/3471c22c1a0d26c51c78f6aeb06489d38cf46a4d", + "reference": "3471c22c1a0d26c51c78f6aeb06489d38cf46a4d", "shasum": "" }, "require": { "fig/http-message-util": "^1.1.5", - "php": "^7.3 || ^8.0", + "php": "^7.4 || ^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0", "ralouphie/getallheaders": "^3.0", - "symfony/polyfill-php80": "^1.23" + "symfony/polyfill-php80": "^1.26" }, "provide": { "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { - "adriansuter/php-autoload-override": "^1.2", + "adriansuter/php-autoload-override": "^1.3", "ext-json": "*", "http-interop/http-factory-tests": "^0.9.0", "php-http/psr7-integration-tests": "dev-master", - "phpspec/prophecy": "^1.14", + "phpspec/prophecy": "^1.15", "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpstan": "^0.12.99", + "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "^3.6" + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { @@ -1333,22 +1334,22 @@ ], "support": { "issues": "https://github.com/slimphp/Slim-Psr7/issues", - "source": "https://github.com/slimphp/Slim-Psr7/tree/1.5" + "source": "https://github.com/slimphp/Slim-Psr7/tree/1.6" }, - "time": "2021-09-22T04:33:00+00:00" + "time": "2022-11-05T18:50:24+00:00" }, { "name": "slim/slim", - "version": "4.10.0", + "version": "4.11.0", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0" + "reference": "b0f4ca393ea037be9ac7292ba7d0a34d18bac0c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0", - "reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/b0f4ca393ea037be9ac7292ba7d0a34d18bac0c7", + "reference": "b0f4ca393ea037be9ac7292ba7d0a34d18bac0c7", "shasum": "" }, "require": { @@ -1363,21 +1364,21 @@ "psr/log": "^1.1 || ^2.0 || ^3.0" }, "require-dev": { - "adriansuter/php-autoload-override": "^1.2", + "adriansuter/php-autoload-override": "^1.3", "ext-simplexml": "*", - "guzzlehttp/psr7": "^2.1", + "guzzlehttp/psr7": "^2.4", "httpsoft/http-message": "^1.0", "httpsoft/http-server-request": "^1.0", - "laminas/laminas-diactoros": "^2.8", + "laminas/laminas-diactoros": "^2.17", "nyholm/psr7": "^1.5", "nyholm/psr7-server": "^1.0", "phpspec/prophecy": "^1.15", "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpstan": "^1.4", + "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.5", "slim/http": "^1.2", "slim/psr7": "^1.5", - "squizlabs/php_codesniffer": "^3.6" + "squizlabs/php_codesniffer": "^3.7" }, "suggest": { "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", @@ -1450,7 +1451,7 @@ "type": "tidelift" } ], - "time": "2022-03-14T14:18:23+00:00" + "time": "2022-11-06T16:33:39+00:00" }, { "name": "slim/twig-view", @@ -1519,16 +1520,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v6.0.9", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "5c85b58422865d42c6eb46f7693339056db098a8" + "reference": "2eaf8e63bc5b8cefabd4a800157f0d0c094f677a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/5c85b58422865d42c6eb46f7693339056db098a8", - "reference": "5c85b58422865d42c6eb46f7693339056db098a8", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2eaf8e63bc5b8cefabd4a800157f0d0c094f677a", + "reference": "2eaf8e63bc5b8cefabd4a800157f0d0c094f677a", "shasum": "" }, "require": { @@ -1582,7 +1583,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.9" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.19" }, "funding": [ { @@ -1598,7 +1599,7 @@ "type": "tidelift" } ], - "time": "2022-05-05T16:45:52+00:00" + "time": "2023-01-01T08:36:10+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -1681,16 +1682,16 @@ }, { "name": "symfony/finder", - "version": "v6.0.11", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "09cb683ba5720385ea6966e5e06be2a34f2568b1" + "reference": "5cc9cac6586fc0c28cd173780ca696e419fefa11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/09cb683ba5720385ea6966e5e06be2a34f2568b1", - "reference": "09cb683ba5720385ea6966e5e06be2a34f2568b1", + "url": "https://api.github.com/repos/symfony/finder/zipball/5cc9cac6586fc0c28cd173780ca696e419fefa11", + "reference": "5cc9cac6586fc0c28cd173780ca696e419fefa11", "shasum": "" }, "require": { @@ -1722,7 +1723,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.0.11" + "source": "https://github.com/symfony/finder/tree/v6.0.19" }, "funding": [ { @@ -1738,20 +1739,20 @@ "type": "tidelift" } ], - "time": "2022-07-29T07:39:48+00:00" + "time": "2023-01-20T17:44:14+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { @@ -1766,7 +1767,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1804,7 +1805,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { @@ -1820,20 +1821,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -1848,7 +1849,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1887,7 +1888,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -1903,20 +1904,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "shasum": "" }, "require": { @@ -1925,7 +1926,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1970,7 +1971,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" }, "funding": [ { @@ -1986,20 +1987,20 @@ "type": "tidelift" } ], - "time": "2022-05-10T07:21:04+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", - "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", "shasum": "" }, "require": { @@ -2008,7 +2009,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2049,7 +2050,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" }, "funding": [ { @@ -2065,20 +2066,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/yaml", - "version": "v6.0.14", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "e65020d137ad54beb85a67ffe6435e980f35ccf3" + "reference": "deec3a812a0305a50db8ae689b183f43d915c884" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e65020d137ad54beb85a67ffe6435e980f35ccf3", - "reference": "e65020d137ad54beb85a67ffe6435e980f35ccf3", + "url": "https://api.github.com/repos/symfony/yaml/zipball/deec3a812a0305a50db8ae689b183f43d915c884", + "reference": "deec3a812a0305a50db8ae689b183f43d915c884", "shasum": "" }, "require": { @@ -2123,7 +2124,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.0.14" + "source": "https://github.com/symfony/yaml/tree/v6.0.19" }, "funding": [ { @@ -2139,20 +2140,20 @@ "type": "tidelift" } ], - "time": "2022-10-07T08:02:12+00:00" + "time": "2023-01-11T11:50:03+00:00" }, { "name": "twig/twig", - "version": "v3.4.3", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58" + "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58", - "reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6e0510cc793912b451fd40ab983a1d28f611c15", + "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15", "shasum": "" }, "require": { @@ -2167,7 +2168,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "3.5-dev" } }, "autoload": { @@ -2203,7 +2204,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.4.3" + "source": "https://github.com/twigphp/Twig/tree/v3.5.1" }, "funding": [ { @@ -2215,7 +2216,7 @@ "type": "tidelift" } ], - "time": "2022-09-28T08:42:51+00:00" + "time": "2023-02-08T07:49:20+00:00" }, { "name": "vlucas/valitron", diff --git a/data/navigation/navi-draft.txt b/data/navigation/navi-draft.txt new file mode 100644 index 0000000..253d492 --- /dev/null +++ b/data/navigation/navi-draft.txt @@ -0,0 +1 @@ +a:3:{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:0;}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;}i:2;O:8:"stdClass":21:{s:12:"originalName";s:10:"02-docs-v2";s:11:"elementType";s:6:"folder";s:8:"contains";s:5:"pages";s:6:"status";s:9:"published";s:8:"fileType";s:0:"";s:5:"order";s:2:"02";s:4:"name";s:7:"docs v2";s:4:"slug";s:7:"docs-v2";s:4:"path";s:11:"/02-docs-v2";s:15:"pathWithoutType";s:17:"/02-docs-v2/index";s:9:"urlRelWoF";s:8:"/docs-v2";s:6:"urlRel";s:17:"/typemill/docs-v2";s:6:"urlAbs";s:33:"http://localhost/typemill/docs-v2";s:3:"key";i:2;s:7:"keyPath";i:2;s:12:"keyPathArray";a:1:{i:0;s:1:"2";}s:7:"chapter";i:3;s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;s:13:"folderContent";a:1:{i:0;O:8:"stdClass":19:{s:12:"originalName";s:18:"01-installation.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:12:"installation";s:4:"slug";s:12:"installation";s:4:"path";s:30:"/02-docs-v2/01-installation.md";s:15:"pathWithoutType";s:27:"/02-docs-v2/01-installation";s:3:"key";i:0;s:7:"keyPath";s:3:"2.0";s:12:"keyPathArray";a:2:{i:0;s:1:"2";i:1;s:1:"0";}s:7:"chapter";s:3:"3.1";s:9:"urlRelWoF";s:21:"/docs-v2/installation";s:6:"urlRel";s:30:"/typemill/docs-v2/installation";s:6:"urlAbs";s:46:"http://localhost/typemill/docs-v2/installation";s:6:"active";b:0;s:12:"activeParent";b:0;s:4:"hide";b:0;}}}} \ No newline at end of file diff --git a/data/navigation/navi-extended.txt b/data/navigation/navi-extended.txt new file mode 100644 index 0000000..2204443 --- /dev/null +++ b/data/navigation/navi-extended.txt @@ -0,0 +1,66 @@ +/welcome: + navtitle: '' + hide: false + noindex: false + path: /00-welcome + keyPath: 0 +/welcome/setup-your-website: + navtitle: '' + hide: false + noindex: false + path: /00-welcome/00-setup-your-website.md + keyPath: '0.0' +/welcome/manage-access: + navtitle: '' + hide: false + noindex: false + path: /00-welcome/01-manage-access.md + keyPath: '0.1' +/welcome/write-content: + navtitle: '' + hide: false + noindex: false + path: /00-welcome/02-write-content.md + keyPath: '0.2' +/welcome/get-help: + navtitle: '' + hide: false + noindex: false + path: /00-welcome/03-get-help.md + keyPath: '0.3' +/welcome/markdown-test: + navtitle: '' + hide: false + noindex: false + path: /00-welcome/04-markdown-test.md + keyPath: '0.4' +/cyanine-theme: + navtitle: '' + hide: false + noindex: false + path: /01-cyanine-theme + keyPath: 1 +/cyanine-theme/landingpage: + navtitle: '' + hide: false + noindex: false + path: /01-cyanine-theme/00-landingpage.md + keyPath: '1.0' +/cyanine-theme/colors-and-fonts: + navtitle: '' + hide: false + noindex: false + path: /01-cyanine-theme/01-colors-and-fonts.md + keyPath: '1.1' +/cyanine-theme/footer: + navtitle: '' + hide: false + noindex: false + path: /01-cyanine-theme/02-footer.md + keyPath: '1.2' +/cyanine-theme/content-elements: + navtitle: '' + hide: false + noindex: false + path: /01-cyanine-theme/03-content-elements.md + keyPath: '1.3' diff --git a/system/typemill/Controllers/ControllerData.php b/system/typemill/Controllers/ControllerData.php index 2826522..5f2d05c 100644 --- a/system/typemill/Controllers/ControllerData.php +++ b/system/typemill/Controllers/ControllerData.php @@ -90,7 +90,6 @@ class ControllerData extends Controller return $allowedsystemnavi; } - protected function getThemeDetails() { $themes = $this->getThemes(); @@ -239,4 +238,157 @@ class ControllerData extends Controller return $userfields; } + + + +########################################################################################## +# GET STUFF FOR EDITOR AREA +########################################################################################## + + # reads the cached structure with published and non-published pages for the author + # setStructureDraft + protected function getStructureForAuthors($userrole, $username) + { + # 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 + # setFreshStrutureDraft + protected function createNewStructureForAuthors() + { + # 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 + # setStrutureLive + protected function getStructureForReaders() + { + # 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; + } } \ No newline at end of file diff --git a/system/typemill/Controllers/ControllerWebAuthor.php b/system/typemill/Controllers/ControllerWebAuthor.php new file mode 100644 index 0000000..9680017 --- /dev/null +++ b/system/typemill/Controllers/ControllerWebAuthor.php @@ -0,0 +1,210 @@ +c->get('urlinfo'); + $fullUrl = $urlinfo['baseurl'] . $url; + $langattr = $this->settings['langattr']; + + $navigation = new Navigation(); + + $extendedNavigation = $navigation->getExtendedNavigation($urlinfo, $langattr); + $pageinfo = $extendedNavigation[$url] ?? false; + + if(!$pageinfo) + { + return $this->c->get('view')->render($response->withStatus(404), '404.twig', [ + 'title' => 'Typemill Author Area', + 'description' => 'Typemill Version 2 wird noch besser als Version 1.' + ]); + } + + # extend : $request->getAttribute('c_userrole') + $draftNavigation = $navigation->getDraftNavigation($urlinfo, $langattr); + + $item = $navigation->getItemWithKeyPath($draftNavigation, explode(".", $pageinfo['keyPath'])); + + $mainNavigation = $navigation->getMainNavigation($request->getAttribute('c_userrole'), $this->c->get('acl'), $urlinfo, $this->settings['editor']); + + return $this->c->get('view')->render($response, 'content/blox-editor.twig', [ + 'settings' => $this->settings, + 'mainnavi' => $mainNavigation, + 'jsdata' => [ + 'settings' => $this->settings, + 'navigation' => $draftNavigation, + 'item' => $item, + 'urlinfo' => $urlinfo + ] + ]); + + echo '
';
+	    print_r($draftNavigation);
+	    die();
+
+
+
+		# 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 templates
+		$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 
+		));
+	}
+
+	public function showContent(Request $request, Response $response, $args)
+	{
+		# get params from call
+#		$this->uri 		= $request->getUri()->withUserInfo('');
+#		$this->params	= isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()];
+		
+		# 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
+		));
+	}	
+}
\ No newline at end of file
diff --git a/system/typemill/Models/Folder.php b/system/typemill/Models/Folder.php
new file mode 100644
index 0000000..158190f
--- /dev/null
+++ b/system/typemill/Models/Folder.php
@@ -0,0 +1,652 @@
+ $item)
+		{
+			if (!in_array($item, array(".","..")))
+			{
+				$nameParts 					= $this->getStringParts($item);
+				$fileType 					= array_pop($nameParts);
+				
+				if($fileType == 'md' OR $fileType == 'txt' )
+				{
+					$folderContent[] 			= $item;						
+				}
+			}
+		}
+		return $folderContent;
+	}
+	
+	/*
+	* scans content of a folder recursively
+	* vars: folder path as string
+	* returns: multi-dimensional array with names of folders and files
+	*/
+	public function scanFolder($folderPath, $draft = false)
+	{
+		$folderItems 	= scandir($folderPath);
+		$folderContent 	= array();
+
+		# if it is the live version and if it is a folder that is not published, then do not show the folder and its content.
+		if(!$draft && !in_array('index.md', $folderItems)){ return false; }
+
+		foreach ($folderItems as $key => $item)
+		{
+			if (!in_array($item, array(".","..")) && substr($item, 0, 1) != '.')
+			{
+				if (is_dir($folderPath . DIRECTORY_SEPARATOR . $item))
+				{
+
+					$subFolder 		 	= $item;
+					$folderPublished 	= file_exists($folderPath . DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . 'index.md');
+
+					# scan that folder only if it is a draft or if the folder is published (contains index.md)
+					if($draft OR $folderPublished)
+					{
+						$folderContent[$subFolder] 	= $this->scanFolder($folderPath . DIRECTORY_SEPARATOR . $subFolder, $draft);
+					}
+				}
+				else
+				{
+					$nameParts 					= $this->getStringParts($item);
+					$fileType 					= array_pop($nameParts);
+					
+					if($fileType == 'md')
+					{
+						$folderContent[] 			= $item;		
+					}
+					
+					if($draft === true && $fileType == 'txt')
+					{
+						if(isset($last) && ($last == implode($nameParts)) )
+						{
+							array_pop($folderContent);
+							$item = $item . 'md';
+						}
+						$folderContent[] = $item;
+					}
+					
+					/* store the name of the last file */
+					$last = implode($nameParts);
+				}
+			}
+		}
+		return $folderContent;
+	}
+	
+
+	/*
+	* Transforms array of folder item into an array of item-objects with additional information for each item
+	* vars: multidimensional array with folder- and file-names
+	* returns: array of objects. Each object contains information about an item (file or folder).
+	*/
+
+	public function getFolderContentDetails(array $folderContent, $language, $baseUrl, $fullSlugWithFolder = NULL, $fullSlugWithoutFolder = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL)
+	{
+		$contentDetails 	= [];
+		$iteration 			= 0;
+		$chapternr 			= 1;
+
+		foreach($folderContent as $key => $name)
+		{
+			$item = new \stdClass();
+
+			if(is_array($name))
+			{
+				$nameParts = $this->getStringParts($key);
+				
+				$fileType = '';
+				if(in_array('index.md', $name))
+				{
+					$fileType 		= 'md';
+					$status 		= 'published';
+				}
+				if(in_array('index.txt', $name))
+				{
+					$fileType 		= 'txt';
+					$status 		= 'unpublished';
+				}
+				if(in_array('index.txtmd', $name))
+				{
+					$fileType 		= 'txt';
+					$status 		= 'modified';
+				}
+
+				$item->originalName 	= $key;
+				$item->elementType		= 'folder';
+				$item->contains			= $this->getFolderContentType($name, $fullPath . DIRECTORY_SEPARATOR . $key . DIRECTORY_SEPARATOR . 'index.yaml');
+				$item->status			= $status;
+				$item->fileType			= $fileType;
+				$item->order 			= count($nameParts) > 1 ? array_shift($nameParts) : NULL;
+				$item->name 			= implode(" ",$nameParts);
+				$item->name				= iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name);
+				$item->slug				= implode("-",$nameParts);
+				$item->slug				= $this->createSlug($item->slug, $language);
+				$item->path				= $fullPath . DIRECTORY_SEPARATOR . $key;
+				$item->pathWithoutType	= $fullPath . DIRECTORY_SEPARATOR . $key . DIRECTORY_SEPARATOR . 'index';
+				$item->urlRelWoF		= $fullSlugWithoutFolder . '/' . $item->slug;
+				$item->urlRel			= $fullSlugWithFolder . '/' . $item->slug;
+				$item->urlAbs			= $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug;
+				$item->key				= $iteration;
+				$item->keyPath			= isset($keyPath) ? $keyPath . '.' . $iteration : $iteration;
+				$item->keyPathArray		= explode('.', $item->keyPath);
+				$item->chapter			= $chapter ? $chapter . '.' . $chapternr : $chapternr;
+				$item->active			= false;
+				$item->activeParent		= false;
+				$item->hide 			= false;
+
+				# sort posts in descending order
+				if($item->contains == "posts")
+				{
+					rsort($name);
+				}
+
+				$item->folderContent = $this->getFolderContentDetails($name, $language, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter);
+			}
+			elseif($name)
+			{
+				# do not use files in base folder (only folders are allowed)
+				# if(!isset($keyPath)) continue;
+
+				# do not use index files
+				if($name == 'index.md' || $name == 'index.txt' || $name == 'index.txtmd' ) continue;
+
+				$nameParts 				= $this->getStringParts($name);
+				$fileType 				= array_pop($nameParts);
+				$nameWithoutType		= $this->getNameWithoutType($name);
+
+				if($fileType == 'md')
+				{
+					$status = 'published';
+				}
+				elseif($fileType == 'txt')
+				{
+					$status = 'unpublished';
+				}
+				else
+				{
+					$fileType = 'txt';
+					$status = 'modified';
+				}
+
+				$item->originalName 	= $name;
+				$item->elementType		= 'file';
+				$item->status 			= $status;
+				$item->fileType			= $fileType;
+				$item->order 			= count($nameParts) > 1 ? array_shift($nameParts) : NULL;
+				$item->name 			= implode(" ",$nameParts);
+				$item->name				= iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name);				
+				$item->slug				= implode("-",$nameParts);
+				$item->slug				= $this->createSlug($item->slug, $language);
+				$item->path				= $fullPath . DIRECTORY_SEPARATOR . $name;
+				$item->pathWithoutType	= $fullPath . DIRECTORY_SEPARATOR . $nameWithoutType;
+				$item->key				= $iteration;
+				$item->keyPath			= isset($keyPath) ? $keyPath . '.' . $iteration : $iteration;
+				$item->keyPathArray		= explode('.',$item->keyPath);
+				$item->chapter			= $chapter . '.' . $chapternr;
+				$item->urlRelWoF		= $fullSlugWithoutFolder . '/' . $item->slug;
+				$item->urlRel			= $fullSlugWithFolder . '/' . $item->slug;
+				$item->urlAbs			= $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug;
+				$item->active			= false;
+				$item->activeParent		= false;
+				$item->hide 			= false;
+			}
+
+			$iteration++;
+			$chapternr++;
+			$contentDetails[]		= $item;
+		}
+		return $contentDetails;	
+	}
+
+	public function getFolderContentType($folder, $yamlpath)
+	{
+		# check if folder is empty or has only index.yaml-file. This is a rare case so make it quick and dirty
+		if(count($folder) == 1)
+		{
+			# check if in folder yaml file contains "posts", then return posts
+			$folderyamlpath = getcwd() . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . $yamlpath;
+			
+			$fileContent = false;
+			if(file_exists($folderyamlpath))
+			{
+				$fileContent = file_get_contents($folderyamlpath);
+			}
+
+			if($fileContent && strpos($fileContent, 'contains: posts') !== false)
+			{
+				return 'posts';
+			}
+			return 'pages';
+		}
+		else
+		{
+			$file 			= $folder[0];
+			$nameParts 		= $this->getStringParts($file);
+			$order 			= count($nameParts) > 1 ? array_shift($nameParts) : NULL;
+			$order 			= substr($order, 0, 7);
+			
+			if(\DateTime::createFromFormat('Ymd', $order) !== FALSE)
+			{
+				return "posts";
+			}
+			else
+			{
+				return "pages";
+			}
+		}
+	}
+
+	public function getHomepageItem($baseUrl)
+	{
+		# return a standard item-object
+		$item 					= new \stdClass;
+
+		$item->status 			= 'published';
+		$item->originalName 	= 'home';
+		$item->elementType 		= 'folder';
+		$item->fileType			= 'md';
+		$item->order 			= false;
+		$item->name 			= 'home';
+		$item->slug				= '';
+		$item->path				= '';
+		$item->pathWithoutType	= DIRECTORY_SEPARATOR . 'index';
+		$item->key				= false;
+		$item->keyPath			= false;
+		$item->keyPathArray		= false;
+		$item->chapter			= false;
+		$item->urlRel			= '/';
+		$item->urlRelWoF		= '/';
+		$item->urlAbs			= $baseUrl;
+		$item->name 			= 'home';
+		$item->active			= false;
+		$item->activeParent		= false;
+		$item->hide 			= false;
+
+		return $item;
+	}
+
+	public function getItemForUrl($folderContentDetails, $url, $baseUrl, $result = NULL, $home = NULL )
+	{
+		# if we are on the homepage
+		if($home)
+		{
+			return $this->getHomepageItem($baseUrl);
+		}
+
+		foreach($folderContentDetails as $key => $item)
+		{
+			# set item active, needed to move item in navigation
+			if($item->urlRel === $url)
+			{
+				$item->active = true;
+				$result = $item;
+			}
+			elseif($item->elementType === "folder")
+			{
+				$result = $this->getItemForUrl($item->folderContent, $url, $baseUrl, $result);
+			}
+		}
+
+		return $result;
+	}
+
+	public function getItemForUrlFrontend($folderContentDetails, $url, $result = NULL)
+	{
+		foreach($folderContentDetails as $key => $item)
+		{
+			# set item active, needed to move item in navigation
+			if($item->urlRelWoF === $url)
+			{
+				$item->active = true;
+				$result = $item;
+			}
+			elseif($item->elementType === "folder")
+			{
+				$result = $this->getItemForUrlFrontend($item->folderContent, $url, $result);
+			}
+		}
+
+		return $result;
+	}	
+
+	public function getPagingForItem($content, $item)
+	{
+		# if page is home
+		if(trim($item->pathWithoutType, DIRECTORY_SEPARATOR) == 'index')
+		{
+			return $item;
+		}
+
+		$keyPos 			= count($item->keyPathArray)-1;
+		$thisChapArray		= $item->keyPathArray;
+		$nextItemArray 		= $item->keyPathArray;
+		$prevItemArray 		= $item->keyPathArray;
+		
+		$item->thisChapter 	= false;
+		$item->prevItem 	= false;
+		$item->nextItem 	= false;
+		
+		
+		/************************
+		* 	ADD THIS CHAPTER 	*
+		************************/
+
+		if($keyPos > 0)
+		{
+			array_pop($thisChapArray);
+			$item->thisChapter = $this->getItemWithKeyPath($content, $thisChapArray);
+		}
+		
+		/************************
+		* 	ADD NEXT ITEM	 	*
+		************************/
+				
+		if($item->elementType == 'folder')
+		{
+			# get the first element in the folder
+			$item->nextItem = isset($item->folderContent[0]) ? clone($item->folderContent[0]) : false;
+		}
+		
+		# the item is a file or an empty folder
+		if(!$item->nextItem)
+		{
+			# walk to the next file in the same hierarchy
+			$nextItemArray[$keyPos]++;
+
+			# get the key of the last element in this hierarchy level
+			# if there is no chapter, then it is probably an empty first-level-folder. Count content to get the number of first level items
+			$lastKey = $item->thisChapter ? array_key_last($item->thisChapter->folderContent) : count($content);
+
+			# as long as the nextItemArray is smaller than the last key in this hierarchy level, search for the next item
+			# this ensures that it does not stop if key is missing (e.g. if the next page is hidden)
+			while( ($nextItemArray[$keyPos] <= $lastKey) && !$item->nextItem = $this->getItemWithKeyPath($content, $nextItemArray) )
+			{
+				$nextItemArray[$keyPos]++;
+			}
+		}
+		
+		# there is no next file or folder in this level, so walk up the hierarchy to the next folder or file
+		while(!$item->nextItem)
+		{
+			# delete the array level with the current item, so you are in the parent folder
+			array_pop($nextItemArray);
+
+			# if the array is empty now, then you where in the base level already, so break
+			if(empty($nextItemArray)) break; 
+
+			# define the key position where you are right now
+			$newKeyPos = count($nextItemArray)-1;
+
+			# go to the next position
+			$nextItemArray[$newKeyPos]++;
+
+			# search for 5 items in case there are some hidden elements
+			$maxlength = $nextItemArray[$newKeyPos]+5;
+			while( ($nextItemArray[$newKeyPos] <= $maxlength) && !$item->nextItem = $this->getItemWithKeyPath($content, $nextItemArray) )
+			{
+				$nextItemArray[$newKeyPos]++;
+			}
+		}
+
+		/************************
+		* 	ADD PREVIOUS ITEM	*
+		************************/
+		
+		# check if element is the first in the array
+		$first = ($prevItemArray[$keyPos] == 0) ? true : false;
+
+		if(!$first)
+		{
+			$prevItemArray[$keyPos]--;
+			
+			while($prevItemArray[$keyPos] >= 0 && !$item->prevItem = $this->getItemWithKeyPath($content, $prevItemArray))
+			{
+				$prevItemArray[$keyPos]--;
+			}
+			
+			# if no item is found, then all previous items are hidden, so set first item to true and it will walk up the array later
+			if(!$item->prevItem)
+			{
+				$first = true;
+			}
+			elseif($item->prevItem && $item->prevItem->elementType == 'folder' && !empty($item->prevItem->folderContent))
+			{
+				# if the previous item is a folder, the get the last item of that folder
+				$item->prevItem = $this->getLastItemOfFolder($item->prevItem);
+			}
+		}
+
+		# if it is the first item in the folder (or all other files are hidden)
+		if($first)
+		{
+			# then the previous item is the containing chapter
+			$item->prevItem = $item->thisChapter;
+		}
+		
+		if($item->prevItem && $item->prevItem->elementType == 'folder'){ unset($item->prevItem->folderContent); }
+		if($item->nextItem && $item->nextItem->elementType == 'folder'){ unset($item->nextItem->folderContent); }
+		if($item->thisChapter){unset($item->thisChapter->folderContent); }
+		
+		return $item;
+	}
+
+	/*
+	 * Gets a copy of an item with a key
+	 * @param array $content with the full structure of the content as multidimensional array
+	 * @param array $searchArray with the key as a one-dimensional array like array(0,3,4)
+	 * @return array $item
+	 */
+
+
+	# copy this to navigation
+	# add keypath to the extended index
+	 
+	public function getItemWithKeyPath($content, array $searchArray)
+	{
+		$item = false;
+
+		foreach($searchArray as $key => $itemKey)
+		{
+			$item = isset($content[$itemKey]) ? clone($content[$itemKey]) : false;
+			
+			unset($searchArray[$key]);
+			if(!empty($searchArray) && $item)
+			{
+				return $this->getItemWithKeyPath($item->folderContent, $searchArray);
+			}
+		}
+		return $item;
+	}
+
+	# https://www.quora.com/Learning-PHP-Is-there-a-way-to-get-the-value-of-multi-dimensional-array-by-specifying-the-key-with-a-variable
+	# NOT IN USE
+	public function getItemWithKeyPathNew($array, array $keys)
+	{
+		$item = $array;
+		
+        foreach ($keys as $key)
+		{
+			$item = isset($item[$key]->folderContent) ? $item[$key]->folderContent : $item[$key];
+		}
+		
+		return $item;
+    }
+
+	/*
+	 * Extracts an item with a key https://stackoverflow.com/questions/52097092/php-delete-value-of-array-with-dynamic-key
+	 * @param array $content with the full structure of the content as multidimensional array
+	 * @param array $searchArray with the key as a one-dimensional array like array(0,3,4)
+	 * @return array $item
+	 * NOT IN USE ??
+	 */
+	 
+	public function extractItemWithKeyPath($structure, array $keys)
+	{
+		$result = &$structure;
+		$last = array_pop($keys);
+
+		foreach ($keys as $key) {
+			if(isset($result[$key]->folderContent))
+			{
+				$result = &$result[$key]->folderContent;
+			}
+			else
+			{
+				$result = &$result[$key];
+			}
+		}
+
+		$item = $result[$last];
+		unset($result[$last]);
+		
+		return array('structure' => $structure, 'item' => $item);
+	}
+
+	# NOT IN USE
+	public function deleteItemWithKeyPathNOTINUSE($structure, array $keys)
+	{
+		$result = &$structure;
+		$last = array_pop($keys);
+
+		foreach ($keys as $key)
+		{
+			if(isset($result[$key]->folderContent))
+			{
+				$result = &$result[$key]->folderContent;
+			}
+			else
+			{
+				$result = &$result[$key];
+			}
+		}
+
+		$item = $result[$last];
+		unset($result[$last]);
+		
+		return $structure;
+	}
+	
+	# get breadcrumb as copied array, 
+	# set elements active in original 
+	# mark parent element in original
+	public function getBreadcrumb($content, $searchArray, $i = NULL, $breadcrumb = NULL)
+	{
+		# if it is the first round, create an empty array
+		if(!$i){ $i = 0; $breadcrumb = array();}
+
+		if(!$searchArray){ return $breadcrumb;}
+
+		while($i < count($searchArray))
+		{
+			if(!isset($content[$searchArray[$i]])){ return false; }
+			$item = $content[$searchArray[$i]];
+
+			if($i == count($searchArray)-1)
+			{
+				$item->active = true;
+			}
+			else
+			{
+				$item->activeParent = true;
+			}
+
+			/*
+			$item->active = true;
+			if($i == count($searchArray)-2)
+			{
+				$item->activeParent = true; 
+			}
+			*/
+
+			$copy = clone($item);
+			if($copy->elementType == 'folder')
+			{
+				unset($copy->folderContent);
+				$content = $item->folderContent;
+			}
+			$breadcrumb[] = $copy;
+			
+			$i++;
+			return $this->getBreadcrumb($content, $searchArray, $i++, $breadcrumb);
+		}
+		return $breadcrumb;
+	}
+	
+	public function getParentItem($content, $searchArray, $iteration = NULL)
+	{
+		if(!$iteration){ $iteration = 0; }
+		while($iteration < count($searchArray)-2)
+		{
+			$content = $content[$searchArray[$iteration]]->folderContent;
+			$iteration++;
+			return $this->getParentItem($content, $searchArray, $iteration);
+		}
+		return $content[$searchArray[$iteration]];
+	}
+	
+	private function getLastItemOfFolder($folder)
+	{	
+		$lastItem = end($folder->folderContent);
+		if(is_object($lastItem) && $lastItem->elementType == 'folder' && !empty($lastItem->folderContent))
+		{
+			return $this->getLastItemOfFolder($lastItem);
+		}
+		return $lastItem;
+	}
+		
+	public function getStringParts($name)
+	{
+		return preg_split('/[\-\.\_\=\+\?\!\*\#\(\)\/ ]/',$name);
+	}
+	
+	public function getFileType($fileName)
+	{
+		$parts = preg_split('/\./',$fileName);
+		return end($parts);
+	}
+	
+	public function splitFileName($fileName)
+	{
+		$parts = preg_split('/\./',$fileName);
+		return $parts;
+	}
+
+	public function getNameWithoutType($fileName)
+	{
+		$parts = preg_split('/\./',$fileName);
+		return $parts[0];
+	}
+
+	public function createSlug($name, $language = null)
+	{
+		$name 		= iconv(mb_detect_encoding($name, mb_detect_order(), true), "UTF-8", $name);
+		$language 	= $language ? $language : "";
+
+		return URLify::filter(
+						$name,
+						$length = 60, 
+						$language, 
+						$file_name = false, 
+						$use_remove_list = false,
+						$lower_case = true, 
+						$treat_underscore_as_space = true 
+					);	
+	}
+}
\ No newline at end of file
diff --git a/system/typemill/Models/Navigation.php b/system/typemill/Models/Navigation.php
new file mode 100644
index 0000000..8d952a9
--- /dev/null
+++ b/system/typemill/Models/Navigation.php
@@ -0,0 +1,341 @@
+storage 				= new StorageWrapper('\Typemill\Models\Storage');
+
+		$this->naviFolder 			= 'data' . DIRECTORY_SEPARATOR . 'navigation';
+
+		$this->liveNaviName 		= 'navi-live.txt';
+
+		$this->draftNaviName 		= 'navi-draft.txt';
+
+		$this->extendedNaviName 	= 'navi-extended.txt';
+	}
+
+	public function getMainNavigation($userrole, $acl, $urlinfo, $editor)
+	{
+		$mainnavi 		= $this->storage->getYaml('system/typemill/settings', 'mainnavi.yaml');
+
+		$allowedmainnavi = [];
+
+		foreach($mainnavi as $name => $naviitem)
+		{
+			if($acl->isAllowed($userrole, $naviitem['aclresource'], $naviitem['aclprivilege']))
+			{
+				# set the navi of current route active
+				$thisRoute = '/tm/' . $name;
+				if(strpos($urlinfo['route'], $thisRoute) !== false)
+				{
+					$naviitem['active'] = true;
+				}
+
+				$allowedmainnavi[$name] = $naviitem;
+			}
+		}
+
+		# if system is there, then we do not need the account item
+		if(isset($allowedmainnavi['system']))
+		{
+			unset($allowedmainnavi['account']);
+		}
+
+		# set correct editor mode according to user settings
+		if(isset($allowedmainnavi['content']) && $editor == 'raw')
+		{
+			$allowedmainnavi['content']['routename'] = "content.raw";
+		}
+
+		return $allowedmainnavi;
+	}
+
+
+	# get the navigation with draft files for author environment
+	public function getDraftNavigation($urlinfo, $language, $userrole = null, $username = null)
+	{
+		# todo: filter for userrole or username 
+
+		$this->draftNavigation = $this->storage->getFile($this->naviFolder, $this->draftNaviName, 'unserialize');
+
+		if($this->draftNavigation)
+		{
+			return $this->draftNavigation;
+		}
+
+		# if there is no cached navi, create a basic new draft navi
+		$basicDraftNavigation = $this->getBasicDraftNavigation($urlinfo, $language);
+
+		# get the extended navigation with additional infos from the meta-files like title or hidden pages
+		$extendedNavigation = $this->getExtendedNavigation($urlinfo, $language);
+
+		# merge the basic draft navi with the extended infos from meta-files
+		$draftNavigation = $this->mergeNavigationWithExtended($basicDraftNavigation, $extendedNavigation);
+
+		# cache it
+		$this->storage->writeFile($this->naviFolder, $this->draftNaviName, $draftNavigation, 'serialize');
+
+		return $draftNavigation;
+	}
+
+	public function getBasicDraftNavigation($urlinfo, $language)
+	{
+		if(!$this->basicDraftNavigation)
+		{
+			$this->basicDraftNavigation = $this->createBasicDraftNavigation($urlinfo, $language);
+		}
+		return $this->basicDraftNavigation;
+	}
+
+	# creates a fresh structure with published and non-published pages for the author
+	public function createBasicDraftNavigation($urlinfo, $language)
+	{
+		$folder = new Folder();
+
+		# scan the content of the folder
+		$draftContentTree = $folder->scanFolder($this->storage->getStorageInfo('contentFolder'), $draft = true);
+
+		# if there is content, then get the content details
+		if(count($draftContentTree) > 0)
+		{
+			$draftNavigation = $folder->getFolderContentDetails($draftContentTree, $language, $urlinfo['baseurl'], $urlinfo['basepath']);
+			
+			return $draftNavigation;
+		}
+
+		return false;
+	}
+
+	# get the extended navigation with additional infos from the meta-files like title or hidden pages
+	public function getExtendedNavigation($urlinfo, $language)
+	{
+		if(!$this->extendedNavigation)
+		{
+			# read the extended navi file
+			$this->extendedNavigation = $this->storage->getYaml($this->naviFolder, $this->extendedNaviName);
+		}
+
+		if(!$this->extendedNavigation)
+		{
+			$basicDraftNavigation = $this->getBasicDraftNavigation($urlinfo, $language);
+
+			$this->extendedNavigation = $this->createExtendedNavigation($basicDraftNavigation, $extended = NULL);
+		
+			# cache it
+			$this->storage->updateYaml($this->naviFolder, $this->extendedNaviName, $this->extendedNavigation);
+		}
+
+		return $this->extendedNavigation;
+	}
+
+	# reads all meta-files and creates an array with url => ['hide' => bool, 'navtitle' => 'bla']
+	public function createExtendedNavigation($navigation, $extended = NULL)
+	{
+		if(!$extended)
+		{
+			$extended = [];
+		}
+
+		$contentFolder = $this->storage->getStorageInfo('contentFolder');
+
+		foreach ($navigation as $key => $item)
+		{
+			# $filename = ($item->elementType == 'folder') ? DIRECTORY_SEPARATOR . 'index.yaml' : $item->pathWithoutType . '.yaml';
+			$filename = $item->pathWithoutType . '.yaml';
+
+			if(file_exists($contentFolder . $filename))
+			{
+				# read file
+				$meta = $this->storage->getYaml($contentFolder, $filename);
+
+				$extended[$item->urlRelWoF]['navtitle'] 	= isset($meta['meta']['navtitle']) ? $meta['meta']['navtitle'] : '';
+				$extended[$item->urlRelWoF]['hide'] 		= isset($meta['meta']['hide']) ? $meta['meta']['hide'] : false;
+				$extended[$item->urlRelWoF]['noindex'] 		= isset($meta['meta']['noindex']) ? $meta['meta']['noindex'] : false;
+				$extended[$item->urlRelWoF]['path']			= $item->path;
+				$extended[$item->urlRelWoF]['keyPath']		= $item->keyPath;
+			}
+
+			if ($item->elementType == 'folder')
+			{
+				$extended = $this->createExtendedNavigation($item->folderContent, $extended);
+			}
+		}
+
+		return $extended;
+	}
+
+	# merge a basic navigation (live or draft) with extended information from meta
+	public function mergeNavigationWithExtended($navigation, $extended)
+	{
+		$mergedNavigation = [];
+
+		foreach($navigation as $key => $item)
+		{
+			if($extended && isset($extended[$item->urlRelWoF]))
+			{
+				$item->name 		= ($extended[$item->urlRelWoF]['navtitle'] != '') ? $extended[$item->urlRelWoF]['navtitle'] : $item->name;
+				$item->hide 		= ($extended[$item->urlRelWoF]['hide'] === true) ? true : false;
+				$item->noindex		= (isset($extended[$item->urlRelWoF]['noindex']) && $extended[$item->urlRelWoF]['noindex'] === true) ? true : false;
+			}
+
+			if($item->elementType == 'folder')
+			{
+				$item->folderContent = $this->mergeNavigationWithExtended($item->folderContent, $extended);
+			}
+
+			$mergedNavigation[] = $item;
+		}
+
+		return $mergedNavigation;
+	}
+
+	public function getItemWithKeyPath($navigation, array $searchArray)
+	{
+		$item = false;
+
+		foreach($searchArray as $key => $itemKey)
+		{
+			$item = isset($navigation[$itemKey]) ? clone($navigation[$itemKey]) : false;
+			
+			unset($searchArray[$key]);
+			if(!empty($searchArray) && $item)
+			{
+				return $this->getItemWithKeyPath($item->folderContent, $searchArray);
+			}
+		}
+
+		return $item;
+	}
+
+	# reads the cached structure with published pages
+	public function getLiveNavigation()
+	{
+		# get the cached navi
+		$liveNavi = $this->storage->getFile($this->naviFolder, $this->liveNaviName, 'unserialize');
+
+		# if there is no cached structure
+		if(!$liveNavi)
+		{
+			return $this->createNewLiveNavigation();
+		}
+
+		return $liveNavi;
+	}
+
+	# creates a fresh structure with published pages
+	private function createNewLiveNavigation($urlinfo, $language)
+	{
+		$folder = new Folder();
+
+		# scan the content of the folder
+		$draftNavi = $folder->scanFolder($this->storage->contentFolder, $draft = false);
+
+		# if there is content, then get the content details
+		if($draftNavi && count($draftNavi) > 0)
+		{
+			# get the extended structure files with changes like navigation title or hidden pages
+			$extended = $this->getExtendedNavi();
+
+			# create an array of object with the whole content of the folder and changes from extended file
+			$liveNavi = $folder->getFolderContentDetails($liveNavi, $extended, $this->settings, $this->uri->getBaseUrl(), $this->uri->getBasePath());
+			
+			# cache structure live
+			$this->storage->writeFile($this->naviFolder, $this->liveNaviName, $liveNavi, 'serialize');
+
+			return $liveNavi;
+		}
+
+		return false;
+	}
+
+
+
+
+
+
+
+
+
+
+	# only backoffice
+	protected function renameExtended($item, $newFolder)
+	{
+		# get the extended structure files with changes like navigation title or hidden pages
+		$yaml = new writeYaml();
+		$extended = $yaml->getYaml('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);
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/system/typemill/Models/Storage.php b/system/typemill/Models/Storage.php
index 5f1c661..548efe1 100644
--- a/system/typemill/Models/Storage.php
+++ b/system/typemill/Models/Storage.php
@@ -4,21 +4,23 @@ namespace Typemill\Models;
 
 class Storage
 {
-	public $error = false;
+	public $error 				= false;
 
-	protected $basepath = false;
+	protected $basepath 		= false;
 
-	protected $tmpFolder = false;
+	protected $tmpFolder 		= false;
 
-	protected $originalFolder = false;
+	protected $originalFolder 	= false;
 
-	protected $liveFolder = false;
+	protected $liveFolder 		= false;
 
-	protected $thumbsFolder = false;
+	protected $thumbsFolder 	= false;
 
-	protected $customFolder = false;
+	protected $customFolder 	= false;
 
-	protected $fileFolder = false;
+	protected $fileFolder 		= false;
+
+	protected $contentFolder 	= false;
 
 	public function __construct()
 	{
@@ -35,6 +37,8 @@ class Storage
 		$this->customFolder		= $this->basepath . 'media' . DIRECTORY_SEPARATOR . 'custom' . DIRECTORY_SEPARATOR;
 
 		$this->fileFolder 		= $this->basepath . 'media' . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR;
+	
+		$this->contentFolder 	= $this->basepath . 'content';
 	}
 
 	public function getError()
@@ -42,6 +46,15 @@ class Storage
 		return $this->error;
 	}
 
+	public function getStorageInfo($item)
+	{
+		if(isset($this->$item))
+		{
+			return $this->$item;
+		}
+		return false;
+	}
+
 	public function checkFolder($folder)
 	{
 		$folderpath = $this->basepath . $folder;
@@ -87,7 +100,7 @@ class Storage
 		return true;
 	}
 
-	public function writeFile($folder, $filename, $data)
+	public function writeFile($folder, $filename, $data, $method = NULL)
 	{
 		if(!$this->checkFolder($folder))
 		{
@@ -107,6 +120,12 @@ class Storage
 			return false;
 		}
 
+		# serialize, json_decode
+		if($method && is_callable($method))
+		{
+			$data = $method($data);
+		}
+
 		$writefile = fwrite($openfile, $data);
 		if(!$writefile)
 		{
@@ -120,13 +139,19 @@ class Storage
 		return true;
 	}
 
-	public function getFile($folder, $filename)
+	public function getFile($folder, $filename, $method = NULL)
 	{
 		if($this->checkFile($folder, $filename))
 		{
 			# ??? should be with basepath???
 			$fileContent = file_get_contents($folder . DIRECTORY_SEPARATOR . $filename);
 		
+			# use unserialise or json_decode
+			if($method && is_callable($method))
+			{
+				$fileContent = $method($fileContent);
+			}
+
 			return $fileContent;
 		}
 
@@ -207,6 +232,8 @@ class Storage
 	}
 
 
+
+
 	public function createUniqueImageName($filename, $extension)
 	{
 		$defaultfilename = $filename;
diff --git a/system/typemill/Models/Yaml.php b/system/typemill/Models/Yaml.php
deleted file mode 100644
index a21db7a..0000000
--- a/system/typemill/Models/Yaml.php
+++ /dev/null
@@ -1,42 +0,0 @@
-getFile($folderName, $yamlFileName);
-		
-		if($yaml)
-		{
-			return \Symfony\Component\Yaml\Yaml::parse($yaml);
-		}
-
-		return false;
-	}
-
-	/**
-	 * Writes a yaml file.
-	 * @param string $fileName is the name of the Yaml Folder.
-	 * @param string $yamlFileName is the name of the Yaml File.
-	 * @param array $contentArray is the content as an array.
-	 */	
-	public function updateYaml($folderName, $yamlFileName, $contentArray)
-	{
-		die('Yaml class outdated. Use storage instead.');
-		$yaml = \Symfony\Component\Yaml\Yaml::dump($contentArray,6);
-		if($this->writeFile($folderName, $yamlFileName, $yaml))
-		{
-			return true;
-		}
-
-		return false;
-	}
-}
\ No newline at end of file
diff --git a/system/typemill/author/content/blox-editor.twig b/system/typemill/author/content/blox-editor.twig
new file mode 100644
index 0000000..2764e8d
--- /dev/null
+++ b/system/typemill/author/content/blox-editor.twig
@@ -0,0 +1,22 @@
+{% extends 'layouts/layoutContent.twig' %}
+{% block title %}{{ translate('Visual Editor') }}{% endblock %}
+
+{% block content %}
+	
+	

{{ translate('Visual Editor') }}

+ +
+ +{% endblock %} + +{% block javascript %} + + + + + + + +{% endblock %} diff --git a/system/typemill/author/layouts/layoutContent.twig b/system/typemill/author/layouts/layoutContent.twig new file mode 100644 index 0000000..71da7aa --- /dev/null +++ b/system/typemill/author/layouts/layoutContent.twig @@ -0,0 +1,68 @@ + + + + + {% block title %}{% endblock %} + + + + + + + + + + + + + + + + + {% block stylesheet %}{% endblock %} + + {{ assets.renderCSS() }} + + + + + {% include 'partials/symbols.twig' %} + +
+ {% include 'partials/mainNavi.twig' %} +
+ + {% include 'partials/flash.twig' %} + +
+ +
+ {% block content %}{% endblock %} +
+
+ + {{ csrf() | raw }} + + + + + + + + {% block javascript %}{% endblock %} + + {{ assets.renderJS() }} + + + \ No newline at end of file diff --git a/system/typemill/author/partials/contentNavi.twig b/system/typemill/author/partials/contentNavi.twig new file mode 100644 index 0000000..9ef5187 --- /dev/null +++ b/system/typemill/author/partials/contentNavi.twig @@ -0,0 +1,13 @@ + + +{% block javascript %} + + + +{% endblock %} diff --git a/system/typemill/author/partials/editorNavi.twig b/system/typemill/author/partials/editorNavi.twig new file mode 100644 index 0000000..a7d4a0c --- /dev/null +++ b/system/typemill/author/partials/editorNavi.twig @@ -0,0 +1,114 @@ + + +{% verbatim %} + +{% endverbatim %} \ No newline at end of file diff --git a/system/typemill/routes/web.php b/system/typemill/routes/web.php index 2859f39..b11f816 100644 --- a/system/typemill/routes/web.php +++ b/system/typemill/routes/web.php @@ -6,6 +6,7 @@ use Typemill\Middleware\WebRedirectIfUnauthenticated; use Typemill\Middleware\WebAuthorization; use Typemill\Controllers\ControllerWebAuth; use Typemill\Controllers\ControllerWebSystem; +use Typemill\Controllers\ControllerWebAuthor; use Typemill\Controllers\ControllerWebFrontend; #use Slim\Views\TwigMiddleware; @@ -20,6 +21,7 @@ $app->group('/tm', function (RouteCollectorProxy $group) { # author and editor area, requires authentication $app->group('/tm', function (RouteCollectorProxy $group) use ($routeParser,$acl) { + # Admin Area $group->get('/logout', ControllerWebAuth::class . ':logout')->setName('auth.logout'); $group->get('/system', ControllerWebSystem::class . ':showSettings')->setName('settings.show')->add(new WebAuthorization($routeParser, $acl, 'system', 'show')); # admin; $group->get('/license', ControllerWebSystem::class . ':showLicense')->setName('license.show')->add(new WebAuthorization($routeParser, $acl, 'system', 'show')); # admin; @@ -30,8 +32,8 @@ $app->group('/tm', function (RouteCollectorProxy $group) use ($routeParser,$acl) $group->get('/user/new', ControllerWebSystem::class . ':newUser')->setName('user.new')->add(new WebAuthorization($routeParser, $acl, 'user', 'create')); # admin; $group->get('/user/{username}', ControllerWebSystem::class . ':showUser')->setName('user.show')->add(new WebAuthorization($routeParser, $acl, 'user', 'show')); # admin;; - # REFACTOR - $group->get('/content/visual[/{params:.*}]', ControllerAuthorEditor::class . ':showBlox')->setName('content.visual'); + # Author Area + $group->get('/content/visual[/{route:.*}]', ControllerWebAuthor::class . ':showBlox')->setName('content.visual')->add(new WebAuthorization($routeParser, $acl, 'mycontent', 'view')); })->add(new WebRedirectIfUnauthenticated($routeParser));