1
0
mirror of https://github.com/typemill/typemill.git synced 2025-07-30 19:00:32 +02:00

refactored navigation model

This commit is contained in:
trendschau
2023-03-13 21:47:40 +01:00
parent 64fddf7da4
commit b66fc088a2
14 changed files with 1788 additions and 161 deletions

213
composer.lock generated
View File

@@ -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",

File diff suppressed because one or more lines are too long

View File

@@ -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'

View File

@@ -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;
}
}

View File

@@ -0,0 +1,210 @@
<?php
namespace Typemill\Controllers;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Slim\Routing\RouteContext;
# use Typemill\Models\Folder;
# use Typemill\Extensions\ParsedownExtension;
# use Typemill\Models\StorageWrapper;
# use Typemill\Models\User;
# use Typemill\Models\License;
use Typemill\Models\Navigation;
class ControllerWebAuthor extends Controller
{
public function showBlox(Request $request, Response $response, $args)
{
# get url for requested page
$url = isset($args['route']) ? '/' . $args['route'] : '/';
$urlinfo = $this->c->get('urlinfo');
$fullUrl = $urlinfo['baseurl'] . $url;
$langattr = $this->settings['langattr'];
$navigation = new Navigation();
$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 '<pre>';
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
));
}
}

View File

@@ -0,0 +1,652 @@
<?php
namespace Typemill\Models;
use \URLify;
class Folder
{
/*
* scans content of a folder (without recursion)
* vars: folder path as string
* returns: one-dimensional array with names of folders and files
*/
public function scanFolderFlat($folderPath)
{
$folderItems = scandir($folderPath);
$folderContent = array();
foreach ($folderItems as $key => $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
);
}
}

View File

@@ -0,0 +1,341 @@
<?php
namespace Typemill\Models;
use Typemill\Models\StorageWrapper;
use Typemill\Models\Folder;
class Navigation
{
private $storage;
private $naviFolder;
private $liveNaviName;
private $draftNaviName;
private $extendedNaviName;
private $extendedNavigation = false;
private $draftNavigation = false;
private $basicDraftNavigation = false;
private $liveNavigation = false;
private $basicLiveNavigation = false;
public function __construct()
{
$this->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);
}
}
}
}

View File

@@ -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;

View File

@@ -1,42 +0,0 @@
<?php
namespace Typemill\Models;
class Yaml extends StorageWrapper
{
/**
* Get the a yaml file.
* @param string $fileName is the name of the Yaml Folder.
* @param string $yamlFileName is the name of the Yaml File.
*/
public function getYaml($folderName, $yamlFileName)
{
die('Yaml class outdated. Use storage instead.');
$yaml = $this->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;
}
}

View File

@@ -0,0 +1,22 @@
{% extends 'layouts/layoutContent.twig' %}
{% block title %}{{ translate('Visual Editor') }}{% endblock %}
{% block content %}
<h1 class="text-3xl font-bold mb-4">{{ translate('Visual Editor') }} </h1>
<div id="veditor" v-cloak></div>
{% endblock %}
{% block javascript %}
<script src="{{ base_url() }}/system/typemill/author/js/highlight.min.js?v={{ settings.version }}"></script>
<script src="{{ base_url() }}/system/typemill/author/js/vue-system.js?v={{ settings.version }}"></script>
<script src="{{ base_url() }}/system/typemill/author/js/vue-translate.js?v={{ settings.version }}"></script>
<script src="{{ base_url() }}/system/typemill/author/js/vue-shared.js?v={{ settings.version }}"></script>
<script>
app.mount('#veditor');
</script>
{% endblock %}

View File

@@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Edit your content with typemill"/>
<meta name="msapplication-TileColor" content="#F9F8F6" />
<meta name="msapplication-TileImage" content="{{ base_url() }}/system/author/img/favicon-144.png" />
<link rel="icon" type="image/png" href="{{ base_url() }}/system/author/img/favicon-16.png" sizes="16x16" />
<link rel="icon" type="image/png" href="{{ base_url() }}/system/author/img/favicon-32.png" sizes="32x32" />
<link rel="apple-touch-icon" sizes="72x72" href="{{ base_url() }}/system/author/img/favicon-72.png" />
<link rel="apple-touch-icon" sizes="114x114" href="{{ base_url() }}/system/author/img/favicon-114.png" />
<link rel="apple-touch-icon" sizes="144x144" href="{{ base_url() }}/system/author/img/favicon-144.png" />
<link rel="apple-touch-icon" sizes="180x180" href="{{ base_url() }}/system/author/img/favicon-180.png" />
<link rel="stylesheet" href="{{ base_url() }}/system/typemill/author/css/output.css?v={{ settings.version }}" />
<link rel="stylesheet" href="{{ base_url() }}/system/typemill/author/css/custom.css?v={{ settings.version }}" />
{% block stylesheet %}{% endblock %}
{{ assets.renderCSS() }}
</head>
<body class="bg-stone-100">
{% include 'partials/symbols.twig' %}
<header class="border-b-2 border-stone-200">
{% include 'partials/mainNavi.twig' %}
</header>
{% include 'partials/flash.twig' %}
<div class="max-w-6xl m-auto mt-7 flex justify-between" id="main" data-url="{{ base_url() }}">
<aside class="w-1/4">
{% include 'partials/contentNavi.twig' %}
</aside>
<article class="w-3/4 bg-stone-50 shadow-md p-8">
{% block content %}{% endblock %}
</article>
</div>
{{ csrf() | raw }}
<script>
const data = {{ jsdata | json_encode() | raw }};
const labels = {{ translations|json_encode() }};
</script>
<script src="{{ base_url() }}/system/typemill/author/js/axios.min.js?v={{ settings.version }}"></script>
<script>
const tmaxios = axios.create();
tmaxios.defaults.baseURL = "{{ base_url() }}";
tmaxios.defaults.headers.common['X-Session-Auth'] = "true";
</script>
<script src="{{ base_url() }}/system/typemill/author/js/vue.js?v={{ settings.version }}"></script>
<script src="{{ base_url() }}/system/typemill/author/js/vue-eventbus.js?v={{ settings.version }}"></script>
{% block javascript %}{% endblock %}
{{ assets.renderJS() }}
</body>
</html>

View File

@@ -0,0 +1,13 @@
<nav class="max-w-6xl m-auto flex justify-between">
<ul class="flex border-l-2 border-stone-200">
<div id="contentNavigation" v-cloak></div>
</ul>
</nav>
{% block javascript %}
<script>
app.mount('#contentNavigation');
</script>
{% endblock %}

View File

@@ -0,0 +1,114 @@
<nav id="sidebar-menu" class="sidebar-menu--content">
<div id="data-navi" data-homepage='{{ homepage|json_encode() }}' data-editormode="{{settings.editor}}"></div>
<div id="mobile-menu" class="menu-action">{{ __('Menu') }} <span class="button-arrow"></span></div>
<div id="navi" class="content-navi" :value.sync="freeze" v-cloak>
<div class="navi-list">
<div class="navi-item folder">
<div class="foldertoggle" @click="clearToggle"><svg class="icon icon-enlarge2"><use xlink:href="#icon-enlarge2"></use></svg></div>
<div class="status" :class="homepage.status"></div>
<a href="{{ base_url }}/tm/content/{{ settings.editor }}" :class="homepage.active"><span><span class="iconwrapper"><svg class="icon icon-home"><use xlink:href="#icon-home"></use></svg></span><span class="level-1">{{ __('Homepage') }}</span></a>
</div>
</div>
<draggable class="navi-list" tag="ul"
@start="onStart"
@end="onEnd"
:list="items"
:move="checkMove"
group="file"
animation="150"
:disabled="freeze">
<navigation
v-for="item in items"
ref="draggit"
:freeze="freeze"
:name="item.name"
:hide="item.hide"
:active="item.active"
:parent="item.activeParent"
:level="item.keyPath"
:root="root"
:url="item.urlRelWoF"
:id="item.keyPath"
:key="item.keyPath"
:elementtype="item.elementType"
:contains="item.contains"
:filetype="item.fileType"
:status="item.status"
:folder="item.folderContent"
:collapse="collapse"
></navigation>
</draggable>
<ul class="navi-list addBaseItem">
<li class="navi-item file">
<span class="iconwrapper">
<svg class="icon icon-plus"><use xlink:href="#icon-plus"></use></svg>
</span>
<span class="addNaviItem">
<a class="addNaviLink" href="#" @click.prevent="toggleForm">{{ __('add item') }}</a>
</span>
<transition name="fade">
<div v-if="showForm" class="addNaviForm">
<input v-model="newItem">
<button class="b-left" @click="addFile('file')">{{ __('add file') }}</button><button class="b-right" @click="addFile('folder')">{{ __('add folder') }}</button>
</div>
</transition>
</li>
</ul>
</div>
</nav>
{% verbatim %}
<template id="navigation-template">
<li class="navi-item" :class="elementtype">
<div v-if="folder" class="foldertoggle" @click="callToggle(name)"><svg v-if="isCollapsed(name)" class="icon icon-shrink2"><use xlink:href="#icon-shrink2"></use></svg><svg v-else class="icon icon-enlarge2"><use xlink:href="#icon-enlarge2"></use></svg></div>
<div class="status" :class="status"></div>
<a v-bind:href="getUrl(root, url)" :class="checkActive(active,parent)"><span class="iconwrapper"><svg class="icon" :class="getIconClass(elementtype, filetype, hide)"><use :xlink:href="getIcon(elementtype, filetype, hide)"></use></svg></span><span :class="getLevel(level)">{{ name }}</span><span class="movewrapper"><svg class="icon icon-arrows-v"><use xlink:href="#icon-arrows-v"></use></svg></span></a>
<draggable v-if="folder" v-show="!isCollapsed(name)" class="navi-list" tag="ul"
@start="onStart"
@end="onEnd"
:list="folder"
:move="checkMove"
group="file"
animation="150"
:disabled="freeze">
<navigation
v-for="item in folder"
v-if="contains == 'pages'"
ref="draggit"
:freeze="freeze"
:name="item.name"
:hide="item.hide"
:active="item.active"
:parent="item.activeParent"
:level="item.keyPath"
:url="item.urlRelWoF"
:root="root"
:id="item.keyPath"
:key="item.keyPath"
:filetype="item.fileType"
:status="item.status"
:elementtype="item.elementType"
:contains="item.contains"
:folder="item.folderContent"
:collapse="collapse"
></navigation>
</draggable>
<ul v-if="folder && contains == 'pages'" class="navi-list">
<li class="navi-item file">
<span class="iconwrapper">
<svg class="icon icon-plus"><use xlink:href="#icon-plus"></use></svg>
</span>
<span :class="getLevel(level + '.0')" class="addNaviItem">
<a class="addNaviLink" href="#" @click.prevent="toggleForm">{% endverbatim %}{{ __('add item') }}{% verbatim %}</a>
</span>
<transition name="fade">
<div v-if="showForm" class="addNaviForm">
<input v-model="newItem">
<button class="b-left" @click="addFile('file')">{% endverbatim %}{{ __('add file') }}{% verbatim %}</button><button class="b-right" @click="addFile('folder')">{% endverbatim %}{{ __('add folder') }}{% verbatim %}</button>
</div>
</transition>
</li>
</ul>
</li>
</template>
{% endverbatim %}

View File

@@ -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));