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' %} + ++ ++ + {{ 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 %} + ++ {% block content %}{% endblock %} + ++ + + {{ name }} + + +{% 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));+ + ++