diff --git a/.gitignore b/.gitignore index 0f521b3..e731746 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ + /vendor/ +.docker_vols sftp.json \ No newline at end of file diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..e69de29 diff --git a/docker_dev/Dockerfile b/docker_dev/Dockerfile index 6440854..2060d63 100644 --- a/docker_dev/Dockerfile +++ b/docker_dev/Dockerfile @@ -2,7 +2,7 @@ FROM composer WORKDIR /app COPY www/composer.* . -RUN composer install +COPY www/vendor/* vendor/ FROM php:apache WORKDIR /var/www/html diff --git a/docker_dev/compose.yaml b/docker_dev/compose.yaml index 1b9493d..5a25529 100644 --- a/docker_dev/compose.yaml +++ b/docker_dev/compose.yaml @@ -24,7 +24,7 @@ services: - ../.git - mysql_schema.sql volumes: - - website_datavolume:/var/www/html/raw + - ../.docker_vols/web:/var/www/html/raw mysql: build: @@ -42,7 +42,4 @@ services: - path: mysql_schema.sql action: rebuild volumes: - - sqlvolume:/var/lib/mysql -volumes: - sqlvolume: {} - website_datavolume: {} + - ../.docker_vols/sql:/var/lib/mysql diff --git a/docker_dev/mysql_schema.sql b/docker_dev/mysql_schema.sql index baf0a58..97645e6 100644 --- a/docker_dev/mysql_schema.sql +++ b/docker_dev/mysql_schema.sql @@ -3,35 +3,59 @@ CREATE DATABASE dragon_fire; USE dragon_fire; +-- DROP TABLE posts; +-- DROP TABLE path_access_counts; +-- DROP TABLE path_errcodes; +-- DROP TABLE feed_cache; + CREATE TABLE posts ( post_id INTEGER AUTO_INCREMENT, host VARCHAR(64) NOT NULL, + post_path VARCHAR(255) NOT NULL, post_path_depth INTEGER NOT NULL DEFAULT 0, - post_create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - post_update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + post_created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + post_updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - post_access_count INTEGER DEFAULT 0, + post_view_count INTEGER DEFAULT 0, - post_metadata JSON NOT NULL, + post_title VARCHAR(1024), + post_tags VARCHAR(1024), + post_brief TEXT(2048), + + post_metadata JSON DEFAULT NULL, post_settings_cache JSON DEFAULT NULL, - post_content MEDIUMTEXT, - PRIMARY KEY(post_id), CONSTRAINT unique_post UNIQUE (host, post_path), INDEX(host, post_path), - INDEX(post_path_depth, post_path), - INDEX(post_create_time), - INDEX(post_update_time) + INDEX(host, post_path_depth, post_path), + + INDEX(host, post_created_at), + INDEX(host, post_updated_at), + + FULLTEXT(post_tags) +); + +CREATE TABLE post_markdown ( + post_id INTEGER, + + post_markdown TEXT, + + PRIMARY KEY(post_id), + FOREIGN KEY(post_id) REFERENCES posts(post_id) + ON DELETE CASCADE, + + FULLTEXT(post_markdown) ); CREATE TABLE path_access_counts ( access_time DATETIME NOT NULL, host VARCHAR(64) NOT NULL, + post_path VARCHAR(255), agent VARCHAR(255), referrer VARCHAR(255), @@ -42,6 +66,17 @@ CREATE TABLE path_access_counts ( PRIMARY KEY(access_time, host, post_path, agent, referrer) ); +CREATE TABLE path_errcodes ( + access_timestamp DATETIME NOT NULL, + + host VARCHAR(64) NOT NULL, + + post_path VARCHAR(255), + agent VARCHAR(255), + referrer VARCHAR(255), + error VARCHAR(1024), +); + CREATE TABLE feed_cache ( host VARCHAR(64) NOT NULL, search_path VARCHAR(255), diff --git a/www/.htaccess b/www/.htaccess index eb86f63..d17abcd 100644 --- a/www/.htaccess +++ b/www/.htaccess @@ -14,21 +14,20 @@ RewriteRule ^.*\.(flv|gif|ico|jpg|jpeg|mp4|mpeg|png|svg|swf|webp)$ raw/%{HTTP_HO RewriteRule ^/?raw/(.*)$ raw/%{HTTP_HOST}/$1 [L,END] -RewriteEngine On -RewriteCond %{HTTPS} !on -RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,END] +# RewriteCond %{HTTPS} !on +# RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,END] -RewriteCond %{REQUEST_URI} !^/?(static|raw|robots\.txt).* -RewriteRule (.*) router.php +RewriteCond %{REQUEST_URI} !^/?(src/dbtest\.php|static|raw|robots\.txt).* +RewriteRule (.*) src/router.php Allow from all Options +Indexes - Header set Cache-Control "max-age=315360, public" + Header set Cache-Control "max-age=60, public" - Header set Cache-Control "max-age=315360, public" + Header set Cache-Control "max-age=60, public" \ No newline at end of file diff --git a/www/composer.lock b/www/composer.lock index c157e3a..d41c77f 100644 --- a/www/composer.lock +++ b/www/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "dflydev/dot-access-data", - "version": "v3.0.2", + "version": "v3.0.3", "source": { "type": "git", "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "f41715465d65213d644d3141a6a93081be5d3549" + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", - "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", "shasum": "" }, "require": { @@ -77,9 +77,9 @@ ], "support": { "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" }, - "time": "2022-10-27T11:44:00+00:00" + "time": "2024-07-08T12:26:09+00:00" }, { "name": "erusev/parsedown", @@ -519,16 +519,16 @@ }, { "name": "league/commonmark", - "version": "2.4.1", + "version": "2.5.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5" + "reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/3669d6d5f7a47a93c08ddff335e6d945481a1dd5", - "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/ac815920de0eff6de947eac0a6a94e5ed0fb147c", + "reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c", "shasum": "" }, "require": { @@ -541,8 +541,8 @@ }, "require-dev": { "cebe/markdown": "^1.0", - "commonmark/cmark": "0.30.0", - "commonmark/commonmark.js": "0.30.0", + "commonmark/cmark": "0.31.0", + "commonmark/commonmark.js": "0.31.0", "composer/package-versions-deprecated": "^1.8", "embed/embed": "^4.4", "erusev/parsedown": "^1.0", @@ -551,10 +551,10 @@ "michelf/php-markdown": "^1.4 || ^2.0", "nyholm/psr7": "^1.5", "phpstan/phpstan": "^1.8.2", - "phpunit/phpunit": "^9.5.21", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0", + "symfony/finder": "^5.3 | ^6.0 || ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 || ^7.0", "unleashedtech/php-coding-standard": "^3.1.1", "vimeo/psalm": "^4.24.0 || ^5.0.0" }, @@ -564,7 +564,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "2.6-dev" } }, "autoload": { @@ -621,7 +621,7 @@ "type": "tidelift" } ], - "time": "2023-08-30T16:55:00+00:00" + "time": "2024-07-24T12:52:09+00:00" }, { "name": "league/config", @@ -707,31 +707,31 @@ }, { "name": "nette/schema", - "version": "v1.2.5", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a" + "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a", - "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a", + "url": "https://api.github.com/repos/nette/schema/zipball/a6d3a6d1f545f01ef38e60f375d1cf1f4de98188", + "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188", "shasum": "" }, "require": { - "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", - "php": "7.1 - 8.3" + "nette/utils": "^4.0", + "php": "8.1 - 8.3" }, "require-dev": { - "nette/tester": "^2.3 || ^2.4", + "nette/tester": "^2.4", "phpstan/phpstan-nette": "^1.0", - "tracy/tracy": "^2.7" + "tracy/tracy": "^2.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -763,26 +763,26 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.5" + "source": "https://github.com/nette/schema/tree/v1.3.0" }, - "time": "2023-10-05T20:37:59+00:00" + "time": "2023-12-11T11:54:22+00:00" }, { "name": "nette/utils", - "version": "v4.0.3", + "version": "v4.0.5", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015" + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015", - "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015", + "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", "shasum": "" }, "require": { - "php": ">=8.0 <8.4" + "php": "8.0 - 8.4" }, "conflict": { "nette/finder": "<3", @@ -849,9 +849,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.3" + "source": "https://github.com/nette/utils/tree/v4.0.5" }, - "time": "2023-10-29T21:02:13+00:00" + "time": "2024-08-07T15:39:19+00:00" }, { "name": "psr/event-dispatcher", @@ -983,16 +983,16 @@ }, { "name": "spatie/yaml-front-matter", - "version": "2.0.8", + "version": "2.0.9", "source": { "type": "git", "url": "https://github.com/spatie/yaml-front-matter.git", - "reference": "f2f1f749a405fafc9d6337067c92c062d51a581c" + "reference": "cbe67e1cdd0a29a96d74ccab9400fe663e078392" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/f2f1f749a405fafc9d6337067c92c062d51a581c", - "reference": "f2f1f749a405fafc9d6337067c92c062d51a581c", + "url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/cbe67e1cdd0a29a96d74ccab9400fe663e078392", + "reference": "cbe67e1cdd0a29a96d74ccab9400fe663e078392", "shasum": "" }, "require": { @@ -1029,7 +1029,7 @@ "yaml" ], "support": { - "source": "https://github.com/spatie/yaml-front-matter/tree/2.0.8" + "source": "https://github.com/spatie/yaml-front-matter/tree/2.0.9" }, "funding": [ { @@ -1041,20 +1041,20 @@ "type": "github" } ], - "time": "2023-12-04T10:02:52+00:00" + "time": "2024-06-13T10:20:51+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.4.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { @@ -1063,7 +1063,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.4-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1092,7 +1092,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -1108,20 +1108,20 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.28.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", "shasum": "" }, "require": { @@ -1135,9 +1135,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1174,7 +1171,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" }, "funding": [ { @@ -1190,20 +1187,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", "shasum": "" }, "require": { @@ -1217,9 +1214,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1257,7 +1251,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" }, "funding": [ { @@ -1273,20 +1267,20 @@ "type": "tidelift" } ], - "time": "2023-07-28T09:04:16+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.28.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", "shasum": "" }, "require": { @@ -1294,9 +1288,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1340,7 +1331,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" }, "funding": [ { @@ -1356,20 +1347,96 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { - "name": "symfony/yaml", - "version": "v7.0.0", + "name": "symfony/polyfill-php81", + "version": "v1.30.0", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "0055b230c408428b9b5cde7c55659555be5c0278" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/0055b230c408428b9b5cde7c55659555be5c0278", - "reference": "0055b230c408428b9b5cde7c55659555be5c0278", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af", + "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-19T12:30:46+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "fa34c77015aa6720469db7003567b9f772492bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/fa34c77015aa6720469db7003567b9f772492bf2", + "reference": "fa34c77015aa6720469db7003567b9f772492bf2", "shasum": "" }, "require": { @@ -1411,7 +1478,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.0.0" + "source": "https://github.com/symfony/yaml/tree/v7.1.1" }, "funding": [ { @@ -1427,24 +1494,25 @@ "type": "tidelift" } ], - "time": "2023-11-07T10:26:03+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "twig/markdown-extra", - "version": "v3.8.0", + "version": "v3.11.0", "source": { "type": "git", "url": "https://github.com/twigphp/markdown-extra.git", - "reference": "b6e4954ab60030233df5d293886b5404558daac8" + "reference": "504557d60d80478260ebd2221a2b3332a480865d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/b6e4954ab60030233df5d293886b5404558daac8", - "reference": "b6e4954ab60030233df5d293886b5404558daac8", + "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/504557d60d80478260ebd2221a2b3332a480865d", + "reference": "504557d60d80478260ebd2221a2b3332a480865d", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.5|^3", "twig/twig": "^3.0" }, "require-dev": { @@ -1456,6 +1524,9 @@ }, "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { "Twig\\Extra\\Markdown\\": "" }, @@ -1483,7 +1554,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/markdown-extra/tree/v3.8.0" + "source": "https://github.com/twigphp/markdown-extra/tree/v3.11.0" }, "funding": [ { @@ -1495,34 +1566,42 @@ "type": "tidelift" } ], - "time": "2023-11-21T14:02:01+00:00" + "time": "2024-08-07T17:34:09+00:00" }, { "name": "twig/twig", - "version": "v3.8.0", + "version": "v3.11.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" + "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", + "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" + "symfony/polyfill-php80": "^1.22", + "symfony/polyfill-php81": "^1.29" }, "require-dev": { "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -1555,7 +1634,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.8.0" + "source": "https://github.com/twigphp/Twig/tree/v3.11.0" }, "funding": [ { @@ -1567,7 +1646,7 @@ "type": "tidelift" } ], - "time": "2023-11-21T18:54:41+00:00" + "time": "2024-08-08T16:15:16+00:00" } ], "packages-dev": [], diff --git a/www/post.php b/www/post.php deleted file mode 100644 index 872b75f..0000000 --- a/www/post.php +++ /dev/null @@ -1,200 +0,0 @@ - - -1, - 'post_path' => '/404.md', - 'post_title' => '404 Page', - 'post_metadata' => [ - 'type' => '404' - ] - ]; - } - - public static function _deduce_type($path) { - $ext = pathinfo($path, PATHINFO_EXTENSION); - - if(preg_match("/\.(\w+)\.md$/", $path, $ext_match)) { - $ext = $ext_match[1]; - } - - $ext_mapping = [ - '' => 'directory', - 'md' => 'text/markdown', - 'png' => 'image', - 'jpg' => 'image', - 'jpeg' => 'image' - ]; - - return $ext_mapping[$ext] ?? '?'; - } - - public static function _deduce_icon($type) { - $icon_mapping = [ - '' => 'question', - 'text/markdown' => 'markdown', - 'blog' => 'markdown', - 'blog_list' => 'rectangle-list', - 'directory' => 'folder', - 'gallery' => 'images', - 'image' => 'image' - ]; - - return $icon_mapping[$type] ?? 'unknown'; - } - - function __construct($post_handler, $sql_row) { - $this->handler = $post_handler; - - $this->content_html = null; - $this->content_markdown = null; - - $sql_meta = null; - - if(!isset($sql_row) or !$sql_row['found']) { - $sql_row = $this->_generate_404($sql_row); - $sql_meta = $sql_row['post_metadata']; - } - - $sql_meta = $sql_row['post_metadata']; - if(is_string($sql_meta)) { - $sql_meta = json_decode($sql_meta, true); - } - - unset($sql_meta['settings']); - - $this->sql_row = $sql_row; - $this->sql_meta = $sql_meta ?? []; - - $sql_settings = json_decode($sql_row['post_settings_cache'], true) - ?? $this->handler->get_settings_for_path($sql_row['post_path']); - - $data = [ - 'id' => $sql_row['post_id'], - 'path' => $sql_row['post_path'], - 'url' => 'https://' . $sql_row['host'] . $sql_row['post_path'], - 'created_at' => $sql_row['post_create_time'] ?? '', - 'updated_at' => $sql_row['post_update_time'] ?? '', - 'view_count' => $sql_row['post_access_count'] ?? 0 - ]; - - $data['title'] = $sql_meta['title'] ?? $sql_row['title']; - unset($sql_meta['title']); - - $data['tags'] = $sql_meta['tags'] ?? []; - unset($sql_meta['tags']); - - $data['type'] = $sql_meta['type'] - ?? self::_deduce_type($sql_row['post_path']); - unset($sql_meta['type']); - - $data['icon'] = $sql_meta['icon'] - ?? self::_deduce_icon($data['type']); - unset($sql_meta['icon']); - - if(isset($sql_meta['media_url'])) { - $data['media_url'] = $sql_meta['media_url']; - $data['thumb_url'] = $sql_meta['thumb_url'] ?? $data['media_url']; - - unset($sql_meta['media_url']); - unset($sql_meta['thumb_url']); - } - - $data['banners'] = $sql_meta['banners'] - ?? $sql_settings['banners'] - ?? self::$default_banners; - - unset($sql_meta['banners']); - unset($sql_settings['banners']); - - $data['preview_image'] = $sql_meta['preview_image'] ?? $data['banners'][0]['src'] ?? null; - unset($sql_meta['preview_image']); - - $data['brief'] = $sql_meta['brief'] - ?? $data['title']; - unset($sql_meta['brief']); - - $data['excerpt'] = $sql_meta['excerpt'] - ?? substr($sql_row['post_content'], 0, 256); - - $data['metadata'] = $sql_meta; - $data['settings'] = $sql_settings; - - $this->data = $data; - } - - public function __get($name) { - if($name == 'html') { - return $this->get_html(); - } - if($name == 'markdown') { - return $this->get_markdown(); - } - if($name == 'json') { - return $this->to_json(); - } - - return $this->data[$name]; - } - - public function get_html() { - $fn = self::$markdown_engine; - $this->content_html ??= $fn($this); - - return $this->content_html; - } - public function get_markdown() { - $this->content_markdown ??= - $this->handler->get_markdown_for_id($this->data['id']); - - return $this->content_markdown; - } - - public function to_json($with_markdown = false, $with_html = false) { - $out_data = $this->data; - - if($with_markdown) { - $out_data['markdown'] = $this->get_markdown(); - } - if($with_html) { - $out_data['html'] = $this->get_html(); - } - - return json_encode($out_data); - } - - - public function get_parent_post() { - $parent_path = dirname($this->data['path']); - if($parent_path == '') - return null; - - $this->parent_post ??= new PostData($this->handler, - $this->handler->get_post_by_path($parent_path)); - - return $this->parent_post; - } - - public function get_child_posts() { - if(isset($this->child_posts)) - return $this->child_posts; - - $child_data = $this->handler->get_subposts_by_path($this->data['path']); - - $this->child_posts = array_map(function($data) { - return new PostData($this->handler, $data); - }, $child_data); - } -} - -?> \ No newline at end of file diff --git a/www/post_adapter.php b/www/post_adapter.php deleted file mode 100644 index 05927e4..0000000 --- a/www/post_adapter.php +++ /dev/null @@ -1,221 +0,0 @@ -data_directory = 'raw/' . $this->SITE_CONFIG['HTTP_HOST']; - } - - function deduce_post_type($post_path) { - $ext = pathinfo($post_path, PATHINFO_EXTENSION); - - if(preg_match("/\.(\w+)\.md$/", $post_path, $ext_match)) { - $ext = $ext_match[1]; - } - - $ext_mapping = [ - '' => 'directory', - 'md' => 'text/markdown', - 'png' => 'image', - 'jpg' => 'image', - 'jpeg' => 'image' - ]; - - return $ext_mapping[$ext] ?? '?'; - } - - function fill_in_post_meta($post_path, $meta) { - $icon_mapping = [ - '' => 'question', - 'text/markdown' => 'markdown', - 'blog' => 'markdown', - 'directory' => 'folder', - 'gallery' => 'images', - 'blog_list' => 'rectangle-list', - 'image' => 'image' - ]; - - $meta["title"] ??= basename($post_path); - - if($meta["title"] == "") { - $meta["title"] = "root"; - } - - if(!isset($meta['media_file']) and preg_match("/\.(\w+)\.md$/", $post_path)) { - $meta['media_file'] = "https://" . $this->SITE_CONFIG['HTTP_HOST'] . chop($post_path, ".md"); - } - - $meta['tags'] ??= []; - $meta['type'] ??= $this->deduce_post_type($post_path); - - $meta['icon'] ??= $icon_mapping[$meta['type']] ?? 'question'; - - return $meta; - } - - function _normalize_post_data($post_data) { - $post_data = parent::_normalize_post_data($post_data); - - if(!$post_data['found']) { - return $post_data; - } - - $post_data["post_basename"] = basename($post_data["post_path"]); - - $post_meta = $post_data['post_metadata']; - - $post_data['post_metadata'] = $this->fill_in_post_meta( - $post_data['post_path'], - $post_meta); - - $post_data["post_file_dir"] = '/raw' . $post_data["post_path"]; - - return $post_data; - } - - function make_post_directory($directory) { - $data_directory = $this->data_directory . $directory; - - is_dir($data_directory) || mkdir($data_directory, 0777, true); - - parent::make_post_directory($directory); - } - - function update_or_create_post(...$args) { - $this->_exec("TRUNCATE feed_cache"); - parent::update_or_create_post(...$args); - } - - function save_file($post_path, $file_path) { - move_uploaded_file($file_path, $this->data_directory . $post_path); - } - - function save_markdown_post($post_path, $post_data) { - if(basename($post_path) == "README.md") { - $post_path = dirname($post_path); - } - - $frontmatter_post = YamlFrontMatter::parse($post_data); - $post_path = $this->_sanitize_path($post_path); - - $post_content = $frontmatter_post->body(); - - $post_metadata = $frontmatter_post->matter(); - - $post_metadata = $this->fill_in_post_meta( - $post_path, - $post_metadata); - - $post_metadata['tags'][]= 'type:' . $post_metadata['type']; - - $this->update_or_create_post($post_path, $post_metadata, $post_content); - } - - function handle_upload($post_path, $file_path) { - $post_path = $this->_sanitize_path($post_path); - $ext = pathinfo($post_path, PATHINFO_EXTENSION); - - switch($ext) { - case "md": - $this->save_markdown_post($post_path, file_get_contents($file_path)); - - $this->make_post_directory(dirname($post_path)); - move_uploaded_file($file_path, $this->data_directory . $post_path); - break; - default: - $this->save_file($post_path, $file_path); - } - } - - function try_get_cached_feed($path, $export_opt) { - $post_cache = $this->_exec("SELECT feed_content, feed_created_on - FROM feed_cache - WHERE host=? AND search_path=? AND export_type=?", - "sss", $this->SITE_CONFIG['HTTP_HOST'], $path, $export_opt)->fetch_assoc(); - - if(!isset($post_cache)) { - return null; - } - - return ['feed' => $post_cache['feed_content'], 'feed_ts' => $post_cache['feed_created_on']]; - } - - function construct_feed($path) { - $path = $this->_sanitize_path($path); - - $feed = @new Feed; - - $feed->setTitle("DergFeed"); - $feed->setLink($this->SITE_CONFIG['uri_prefix'] . $path); - $feed->setFeedLink($this->SITE_CONFIG['uri_prefix'] . "/feeds/atom" . $path, "atom"); - - $feed->setDateModified(time()); - - $feed->setDescription("DergenFeed for all your " . $path . " needs"); - - $feed_posts = $this->_exec("SELECT - post_path, - post_create_time, post_update_time, - post_content, - post_metadata - FROM posts - WHERE (host = ?) AND ((post_path = ?) OR (post_path LIKE ?)) - ORDER BY post_create_time DESC LIMIT 200", - "sss", $this->SITE_CONFIG['HTTP_HOST'], $path, $path . '/%'); - - while($row = $feed_posts->fetch_array(MYSQLI_ASSOC)) { - $row = $this->_normalize_post_data($row); - $pmeta = $row['post_metadata']; - - if($pmeta['type'] == 'directory') { - continue; - } - - $entry = $feed->createEntry(); - - $entry->setTitle($row['post_path'] . '> ' . $pmeta['title']); - $entry->setLink($this->SITE_CONFIG['uri_prefix'] . $row['post_path']); - $entry->setDateModified(strtotime($row['post_update_time'])); - $entry->setDateCreated(strtotime($row['post_create_time'])); - - $entry->setDescription($pmeta['brief'] ?? $pmeta['title']); - - $feed->addEntry($entry); - } - - return $feed; - } - - function get_laminas_feed($path, $export_opt) { - $path = $this->_sanitize_path($path); - - $feed_cache = $this->try_get_cached_feed($path, $export_opt); - - if(isset($feed_cache)) { - return $feed_cache; - } - - $feed = $this->construct_feed($path); - - $this->_exec("INSERT INTO feed_cache - (host, search_path, export_type, feed_content) - VALUES - (?, ?, 'atom', ?), - (?, ?, 'rss', ?)", - "ssssss", - $this->SITE_CONFIG['HTTP_HOST'], $path, $feed->export('atom'), - $this->SITE_CONFIG['HTTP_HOST'], $path, $feed->export('rss')); - - return $this->try_get_cached_feed($path, $export_opt); - } -} - -?> \ No newline at end of file diff --git a/www/src/db_handler/analytics_interface.php b/www/src/db_handler/analytics_interface.php new file mode 100644 index 0000000..fbe3400 --- /dev/null +++ b/www/src/db_handler/analytics_interface.php @@ -0,0 +1,17 @@ + + \ No newline at end of file diff --git a/www/src/db_handler/db_interface.php b/www/src/db_handler/db_interface.php new file mode 100644 index 0000000..ea72459 --- /dev/null +++ b/www/src/db_handler/db_interface.php @@ -0,0 +1,86 @@ + +escape_tag($matches[2]) . $matches[3]; +} + +interface PostdataInterface { + /* Postdata format: + * + * The Postdata array is a simple intermediate data format + * for the Post content and metadata. + * It is slightly abstracted from the SQL format itself but will + * only reformat keys, *not* do any alteration of the data itself. + * + * Any supported fields will be integrated into the database. + * Other fields will be saved in a JSON structure, and will + * be restored afterward. + * + * The following fields are mandatory for *writing* + * - path: String, must be sanitized to consist of just alphanumeric + * characters, `_-./` + * used to identify the post itself + * + * The following fields may be returned by the database: + * - id + * - created_at + * - updated_at + * - view_count + * + * The following fields may be supported by the database: + * - markdown: String, markdown of the post. May be + * stored separately and won't be returned by default! + * - type: String, defining the type of the post + * - title: String, self-explanatory + * - tags: Array of strings + * - settings: Hash, recursively merged settings (calculated by DB!) + * + * The following fields are *recommended*, but nothing more: + * - icon: String, optionally defining + */ + + public function stub_postdata($path); + public function stub_postdata_tree($path); + + public function set_postdata($data); + public function set_post_markdown($id, $markdown); + + public function get_postdata($path); + // Returns a key-value pair of child paths => child data + public function get_post_children($path, + $limit = 50, $depth_start = 1, $depth_end = 1, + $order_by = 'path'); + + public function get_post_markdown($id); +} + +?> \ No newline at end of file diff --git a/www/src/db_handler/mysql_handler.php b/www/src/db_handler/mysql_handler.php new file mode 100644 index 0000000..def19df --- /dev/null +++ b/www/src/db_handler/mysql_handler.php @@ -0,0 +1,348 @@ + +sql_connection = $sql_connection; + $this->hostname = $hostname; + + $this->debugging = false; + } + + private function _dbg($message) { + if($this->debugging) { + echo $message; + } + } + + private function _exec($qery, $argtypes = '', ...$args) { + $stmt = $this->sql_connection->prepare($qery); + + if($argtypes != ""){ + $stmt->bind_param($argtypes, ...$args); + } + $stmt->execute(); + + return $stmt->get_result(); + } + + private function clear_post_settings_cache($post_path) { + $post_path = sanitize_post_path($post_path); + + $this->_exec(" + UPDATE posts + SET post_settings_cache=NULL + WHERE host = ? AND post_path LIKE ?; + ", "ss", $this->hostname, $post_path . "%"); + } + + public function stub_postdata($path) { + $post_path = sanitize_post_path($path); + $path_depth = substr_count($post_path, "/"); + + + $qry = " + INSERT INTO posts + (host, post_path, post_path_depth) + VALUES + ( ?, ?, ?) AS new + ON DUPLICATE KEY UPDATE post_path=new.post_path;"; + + $this->_exec($qry, "ssi", + $this->hostname, + $post_path, + $path_depth); + } + + public function stub_postdata_tree($path) { + $post_path = sanitize_post_path($path); + + while(true) { + if($post_path == '/') { + $post_path = ''; + } + + try { + $this->stub_postdata($post_path); + } + catch(Exception $e) { + } + + $post_path = dirname($post_path); + if(strlen($post_path) == 0) { + break; + } + } + } + + public function set_postdata($data) { + $data['path'] = sanitize_post_path($data['path']); + $post_path = $data['path']; + unset($data['path']); + + $this->stub_postdata_tree($post_path); + + $data['title'] ??= basename($post_path); + + $post_tags = $data['tags'] ?? []; + array_push($post_tags, + 'path:' . $post_path + ); + + $sql_args = [ + $this->hostname, + $post_path, + substr_count($post_path, "/"), + $data['title'], + taglist_to_sql_string($post_tags), + $data['brief'] ?? null + ]; + + unset($data['title']); + unset($data['brief']); + + $post_markdown = $data['markdown'] ?? null; + unset($data['markdown']); + unset($data['html']); + + array_push($sql_args, json_encode($data)); + + $qry = + "INSERT INTO posts + (host, + post_path, post_path_depth, + post_title, post_tags, post_brief, + post_metadata, post_settings_cache) + VALUES + ( ?, ?, ?, ?, ?, ?, ?, null) AS new + ON DUPLICATE KEY + UPDATE post_title=new.post_title, + post_tags=new.post_tags, + post_brief=new.post_brief, + post_metadata=new.post_metadata, + post_updated_at=CURRENT_TIMESTAMP; + "; + + $this->_exec($qry, "ssissss", ...$sql_args); + + if(isset($post_markdown)) { + $this->set_post_markdown($this->sql_connection->insert_id, $post_markdown); + } + + $this->clear_post_settings_cache($post_path); + } + + public function set_post_markdown($id, $markdown) { + $qry = + "INSERT INTO post_markdown ( post_id, post_markdown ) + VALUES (?, ?) AS new + ON DUPLICATE KEY UPDATE post_markdown=new.post_markdown; + "; + + $this->_exec($qry, "is", $id, $markdown); + } + + private function get_post_settings($post_path) { + $post_path = sanitize_post_path($post_path); + + $this->_dbg("-> gps: getting path " . $post_path . "\n"); + + $post_settings = $this->_exec(" + SELECT post_settings_cache + FROM posts + WHERE post_path = ? AND host = ? + ", "ss", $post_path, $this->hostname)->fetch_assoc(); + + if(!isset($post_settings)) { + $this->_dbg("-> gps: Returning because of no result\n"); + return []; + } + if(isset($post_settings['post_settings_cache'])) { + $result = json_decode($post_settings['post_settings_cache'], true); + if($this->debugging) { + echo "-> gps: Returning because of cached result:\n"; + echo "--> " . json_encode($result) . "\n"; + } + return $result; + } + + $parent_settings = []; + if($post_path != "") { + $parent_settings = $this->get_post_settings(dirname($post_path)); + } + + $post_settings = []; + $post_metadata = $this->_exec(" + SELECT post_path, post_metadata + FROM posts + WHERE post_path = ? AND host = ? + ", "ss", $post_path, $this->hostname)->fetch_assoc(); + + if(isset($post_metadata['post_metadata'])) { + $post_metadata = json_decode($post_metadata['post_metadata'], true); + + if(isset($post_metadata['settings'])) { + $post_settings = $post_metadata['settings']; + } + } + + $post_settings = array_merge($parent_settings, $post_settings); + + $this->_dbg("-> gps: Merged post settings are " . json_encode($post_settings) . ", saving...\n"); + + $this->_exec(" + UPDATE posts SET post_settings_cache=? WHERE post_path=? AND host=? + ", "sss", + json_encode($post_settings), $post_path, $this->hostname); + + return $post_settings; + } + + private function process_postdata($data) { + if(!isset($data)) { + return null; + } + + if(!isset($data['post_path'])) { + echo "ERROR, trying to get a post data package without path!"; + die(); + } + + $outdata = []; + foreach($this::SQL_READ_COLUMNS as $key) { + if(isset($data['post_' . $key])) { + $outdata[$key] = $data['post_' . $key]; + } + } + + $post_metadata = json_decode($data['post_metadata'] ?? '{}', true); + + $post_settings = []; + + if(isset($data['post_settings_cache'])) { + $post_settings = json_decode($data['post_settings_cache'], true); + } + else { + $post_settings = $this->get_post_settings($data['post_path']); + } + + $outdata = array_merge($post_settings, $post_metadata, $outdata); + + return $outdata; + } + + public function get_postdata($path) { + $path = sanitize_post_path($path); + + $qry = " + SELECT * + FROM posts + WHERE post_path = ? AND host = ?; + "; + + $data = $this->_exec($qry, "ss", $path, $this->hostname)->fetch_assoc(); + + return $this->process_postdata($data); + } + + public function get_post_children($path, + $limit = 50, $depth_start = 1, $depth_end = 1, + $order_by = 'path') { + + $path = sanitize_post_path($path); + + $path_depth = substr_count($path, "/"); + + $allowed_ordering = [ + 'path' => true, + 'path DESC' => true, + 'created_at' => true, + 'created_at DESC' => true, + 'modified_at' => true, + 'modified_at DESC' => true + ]; + + if(!isset($allowed_ordering[$order_by])) { + throw new Exception('Children ordering not allowed'); + } + $order_by = 'post_' . $order_by; + + if($this->debugging) { + echo "-> GPC: Getting children for path " . $path; + } + + $qry = " + SELECT * + FROM posts + WHERE post_path_depth BETWEEN ? AND ? + AND post_path LIKE ? + ORDER BY " . $order_by . + " LIMIT ?"; + + $data = $this->_exec($qry, "iisi", + $path_depth + $depth_start, $path_depth + $depth_end, + $path.'/%', $limit + )->fetch_all(MYSQLI_ASSOC); + $outdata = []; + + foreach($data AS $post_element) { + $outdata[$post_element['post_path']] = + $this->process_postdata($post_element); + } + + return $outdata; + } + + public function get_post_markdown($id) { + $qry = + "SELECT post_markdown + FROM post_markdown + WHERE post_id = ? + "; + + $data = $this->_exec($qry, "i", $id)->fetch_assoc(); + + if(!isset($data)) { + return ""; + } + + return $data['post_markdown']; + } +} + +?> \ No newline at end of file diff --git a/www/src/db_handler/post.php b/www/src/db_handler/post.php new file mode 100644 index 0000000..4e9494e --- /dev/null +++ b/www/src/db_handler/post.php @@ -0,0 +1,213 @@ + + -1, + 'path' => '/404.md', + 'title' => '404 Page', + 'metadata' => [ + 'type' => '404' + ] + ]; + + return $post_data; + } + + public static function _deduce_type($path) { + $ext = pathinfo($path, PATHINFO_EXTENSION); + + if(preg_match("/\.(\w+)\.md$/", $path, $ext_match)) { + $ext = $ext_match[1]; + } + + $ext_mapping = [ + '' => 'directory', + 'md' => 'text/markdown', + 'png' => 'image', + 'jpg' => 'image', + 'jpeg' => 'image' + ]; + + return $ext_mapping[$ext] ?? '?'; + } + + public static function _deduce_icon($type) { + $icon_mapping = [ + '' => 'question', + 'text/markdown' => 'markdown', + 'blog' => 'markdown', + 'blog_list' => 'rectangle-list', + 'directory' => 'folder', + 'gallery' => 'images', + 'image' => 'image' + ]; + + return $icon_mapping[$type] ?? 'unknown'; + } + + function __construct($post_handler, $post_data, $site_defaults) { + $this->handler = $post_handler; + + $this->content_html = null; + $this->content_markdown = null; + + $this->site_defaults = $site_defaults; + + if(!isset($post_data) or !isset($post_data['id'])) { + $post_data = $this->_generate_404($post_data); + } + + $data = $post_data; + + if($data['path'] == '') { + $data['path'] = '/'; + $data['title'] ??= 'root'; + $data['basename'] ??= 'root'; + } + + $post_data['host'] ??= 'localhost:8081'; + + $data['url'] ??= 'http://' . $post_data['host'] . $post_data['path']; + + $data['basename'] ??= basename($data['path']); + + $data['title'] ??= basename($data['path']); + + $data['tags'] ??= []; + + $data['type'] ??= self::_deduce_type($post_data['path']); + + $data['icon'] ??= self::_deduce_icon($data['type']); + + if(isset($sql_meta['media_url'])) { + $data['thumb_url'] ??= $data['media_url']; + } + + $data['preview_image'] ??= $data['banners'][0]['src'] ?? null; + + $data['brief'] ??= $data['title']; + + $this->data = $data; + } + + public function __get($name) { + if($name == 'html') { + return $this->get_html(); + } + if($name == 'markdown') { + return $this->get_markdown(); + } + if($name == 'json') { + return $this->to_json(); + } + if($name == 'child_posts') { + return $this->get_child_posts(); + } + if($name == 'parent') { + return $this->get_parent_post(); + } + + if(isset($this->data[$name])) { + return $this->data[$name]; + } + + if(is_null($this->site_defaults)) { + throw new RuntimeException("Post site defaults have not been set properly!"); + } + + return $this->site_defaults[$name] ?? null; + } + + public function offsetGet($offset) : mixed { + return $this->__get($offset) ?? null; + } + public function offsetExists($offset) : bool { + if(isset($this->data[$offset])) { + return true; + } + if(isset($this->site_defaults[$offset])) { + return true; + } + + return !is_null($this->offsetGet($offset)); + } + public function offsetSet($offset, $value) : void { + $this->data[$offset] = $value; + } + public function offsetUnset($offset) : void { + unset($this->data[$offset]); + } + + public function get_html() { + $this->content_html ??= $this->handler->render_post($this); + + return $this->content_html; + } + public function get_markdown() { + $this->content_markdown ??= + $this->handler->get_markdown_for($this); + + return $this->content_markdown; + } + + public function get_child_posts(...$search_args) { + if(count($search_args) == 0) { + $this->_child_posts ??= + $this->handler->get_children_for($this); + + return $this->_child_posts; + } + else { + return $this->handler->get_children_for($this, ...$search_args); + } + } + + public function to_array($options = []) { + $out_data = $this->data; + + if(isset($options['markdown'])) { + $out_data['markdown'] = $this->get_markdown(); + } + if(isset($options['html'])) { + $out_data['html'] = $this->get_html(); + } + if(isset($options['children'])) { + die(); + } + + return $out_data; + } + public function to_json($options = []) { + return json_encode($this->to_array($options)); + } + + + public function get_parent_post() { + $parent_path = dirname($this->data['path']); + if($parent_path == '') + return null; + + $this->_parent_post ??= $this->handler->get_post($parent_path); + + return $this->_parent_post; + } +} + +?> \ No newline at end of file diff --git a/www/src/db_handler/post_handler.php b/www/src/db_handler/post_handler.php new file mode 100644 index 0000000..06fd3fc --- /dev/null +++ b/www/src/db_handler/post_handler.php @@ -0,0 +1,61 @@ +db = $db_adapter; + $this->posts = []; + + $this->site_defaults = null; + + $this->markdown_engine = null; + } + + public function get_post($key) { + $key = sanitize_post_path($key); + + if(isset($this->posts[$key])) { + return $this->posts[$key]; + } + + $post_data = $this->db->get_postdata($key); + $post = null; + if(isset($post_data)) { + $post = new Post($this, $post_data, $this->site_defaults); + } + + $this->posts[$key] = $post; + + return $post; + } + + public function get_markdown_for($post) { + return $this->db->get_post_markdown($post->id); + } + + public function render_post($post) { + return ($this->markdown_engine)($post); + } + + public function get_children_for($post, ...$search_opts) { + $child_list = $this->db->get_post_children($post->path, ...$search_opts); + + $out_list = []; + foreach($child_list as $child_data) { + array_push($out_list, new Post($this, $child_data, $this->site_defaults)); + } + + return $out_list; + } +} + +?> \ No newline at end of file diff --git a/www/src/dbtest.php b/www/src/dbtest.php new file mode 100644 index 0000000..ffbc378 --- /dev/null +++ b/www/src/dbtest.php @@ -0,0 +1,191 @@ +'; + echo 'Error number: ' . mysqli_connect_errno() . '
'; + echo 'Error message: ' . mysqli_connect_error() . '
'; + die(); +} + +$db_connection->execute_query("DELETE FROM posts;"); + +$sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST); +$adapter = new PostHandler($sql_adapter); + +$sql_adapter->debugging = true; + +function test_accounce($title) { + echo "\n\n=========================================== + _______ ______ _____ _______ + |__ __| ____|/ ____|__ __| + | | | |__ | (___ | | (_) + | | | __| \___ \ | | + | | | |____ ____) | | | _ + |_| |______|_____/ |_| (_) + +"; + echo "==== " . $title . "\n"; + echo "===========================================\n"; +} + +function adapter_fetch($post_path) { + global $db_connection; + global $sql_adapter; + + echo "-> Fetching path " . $post_path . "\n"; + + echo json_encode($db_connection->execute_query("SELECT * FROM posts WHERE post_path=?", [ + $post_path + ])->fetch_assoc(), JSON_PRETTY_PRINT); + + echo "\n-> Adapter output:\n"; + + echo json_encode($sql_adapter->get_postdata($post_path), JSON_PRETTY_PRINT) . "\n"; +} + +echo "Starting test...\n"; + +echo "Trying just a stub...\n"; +$sql_adapter->stub_postdata_tree('/testing/stubtest/1/2/3.md'); +echo "Stubbed~\n\n"; + +echo "Getting the stub post...\n"; + +echo json_encode($sql_adapter->get_postdata('/testing'), JSON_PRETTY_PRINT); + +echo "\n\n"; + +test_accounce("Basic postdata setting"); + +$sql_adapter->set_postdata([ + 'path' => '/testing/settest/test.md', + 'title' => 'One heck of a test!', + 'type' => 'text/markdown', + 'tags' => [ + 'test', + 'type:text' + ], + 'overridetest' => 'metadata' +]); + +echo "\nDone!"; + +adapter_fetch('/testing/settest/test.md'); + +echo "Done!\n\n"; + +test_accounce("Setting post markdown..."); +$sql_adapter->set_postdata([ + 'path' => '/testing/markdowntest', + 'markdown' => 'Inline markdown test should work...', + 'title' => "A Markdown Test" +]); +$post = $sql_adapter->get_postdata('/testing/markdowntest'); +var_dump($sql_adapter->get_post_markdown($post['id'])); + +$sql_adapter->set_post_markdown($post['id'], + ' + This is one hell of a cute test! +> Just checking in... + ' +); +var_dump($sql_adapter->get_post_markdown($post['id'])); +unset($post); + +test_accounce("Settings inheritance test..."); +echo "Setting on a parent file...\n"; + +$sql_adapter->set_postdata([ + 'path' => '/testing/settest', + 'settings' => [ + 'nom' => true, + 'type' => 'frame', + 'overridetest' => 'settings' + ] +]); + +echo "\nAnd checking if that held!\n"; + +adapter_fetch('/testing/settest'); +adapter_fetch('/testing/settest/test.md'); + +test_accounce("Testing getting child posts"); + +echo json_encode($sql_adapter->get_post_children('/testing'), JSON_PRETTY_PRINT); + +echo "\n\n------------------------------------------------------\n"; + +echo "TEST PHASE: Adapter testing"; +echo "\n------------------------------------------------------\n\n"; + +$post = $adapter->get_post('/testing/markdowntest'); +echo "Post path is " . $post->path . "\n"; +echo "Post markdown is " . $post->markdown . "\n"; +echo $post->to_json(); + +echo "\n\n"; + +echo $post->to_json([ + 'markdown' => true +]); + +test_accounce("Fetching child posts"); +echo "Root children:\n". json_encode(array_map(function($data) { + return $data->to_array(); +}, $adapter->get_post('/')->child_posts), JSON_PRETTY_PRINT); +echo "\n\n"; + +echo "Root children, extended:\n" . json_encode(array_map(function($data) { + return $data->to_array(); +}, $adapter->get_post('/')->get_child_posts(depth_end: 3)), JSON_PRETTY_PRINT); +echo "\n\n"; +?> \ No newline at end of file diff --git a/www/dergdown.php b/www/src/dergdown.php similarity index 100% rename from www/dergdown.php rename to www/src/dergdown.php diff --git a/www/fontawesome.php b/www/src/fontawesome.php similarity index 100% rename from www/fontawesome.php rename to www/src/fontawesome.php diff --git a/www/mysql_adapter.php b/www/src/old_mysql_adapter.php similarity index 100% rename from www/mysql_adapter.php rename to www/src/old_mysql_adapter.php diff --git a/www/router.php b/www/src/old_router.php similarity index 91% rename from www/router.php rename to www/src/old_router.php index 8e0d1e7..e15593d 100644 --- a/www/router.php +++ b/www/src/old_router.php @@ -4,8 +4,8 @@ $data_time_start = microtime(true); require_once 'vendor/autoload.php'; -require_once 'post_adapter.php'; - +require_once 'db_handler/mysql_handler.php'; +require_once 'db_handler/post_handler.php'; require_once 'post.php'; require_once 'fontawesome.php'; @@ -24,7 +24,30 @@ $SITE_CONFIG = Yaml::parseFile('secrets/' . $SERVER_HOST . '.config.yml'); $SITE_CONFIG['uri_prefix'] = $SERVER_PREFIX; $SITE_CONFIG['HTTP_HOST'] = $SERVER_HOST; -$adapter = new PostHandler($SITE_CONFIG); +$db_params = $SITE_CONFIG['db']; +$db_connection = null; +try { + if(false !== getenv('MYSQL_HOST')) { + $db_connection = mysqli_connect(getenv('MYSQL_HOST'), + getenv('MYSQL_USER'), getenv('MYSQL_PASSWORD'), + getenv('MYSQL_DATABASE'), + getenv('MYSQL_PORT')); + } + else { + $db_connection = mysqli_connect($db_params['host'], + $db_params['user'], $db_params['password'], + $db_params['database'], + $db_params['port']); + } +} catch (\Throwable $th) { + echo 'Connection failed
'; + echo 'Error number: ' . mysqli_connect_errno() . '
'; + echo 'Error message: ' . mysqli_connect_error() . '
'; + die(); +} + +$sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST); +$adapter = new PostHandler($sql_adapter); $loader = new \Twig\Loader\FilesystemLoader(['./templates', './user_content']); diff --git a/www/src/router.php b/www/src/router.php new file mode 100644 index 0000000..c2f3dc7 --- /dev/null +++ b/www/src/router.php @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/www/src/serve/ajax.php b/www/src/serve/ajax.php new file mode 100644 index 0000000..679afe0 --- /dev/null +++ b/www/src/serve/ajax.php @@ -0,0 +1,26 @@ +get_post($REQUEST_QUERY['page']); +} + + +$ajax_args['fa'] = $FONT_AWESOME_ARRAY; +$ajax_args['page'] ??= $SITE_CONFIG['site_defaults']; + +echo $twig->render('/ajax/' . $AJAX_REQUEST_TEMPLATE, $ajax_args); + +?> \ No newline at end of file diff --git a/www/src/serve/post.php b/www/src/serve/post.php new file mode 100644 index 0000000..6430550 --- /dev/null +++ b/www/src/serve/post.php @@ -0,0 +1,48 @@ +get_post($REQUEST_PATH); + +function render_root_template($template, $args = []) { + global $twig; + global $FONT_AWESOME_ARRAY; + global $SITE_CONFIG; + + $args['fa'] = $FONT_AWESOME_ARRAY; + $args['page'] ??= $SITE_CONFIG['site_defaults']; + + $page = $args['page']; + $page['base'] ??= $page['url']; + + $args['opengraph'] = [ + "site_name" => $page['site_name'] ?? 'Nameless Site', + "title" => $page['title'] ?? 'Titleless', + "url" => $page['url'] ?? $page['path'] ?? 'No URL set', + "description" => $page['description'] ?? 'No description set' + ]; + + $args['banners'] = json_encode($page['banners'] ?? []); + + $args['age_gate'] = (!isset($_COOKIE['AgeConfirmed'])) + && isset($SITE_CONFIG['age_gate']); + + echo $twig->render($template, $args); +} + +function render_pathed_content_template($template, $args = []) { + render_root_template($template, $args); +} + +render_pathed_content_template('post_types/markdown.html', [ + 'page' => $post +]); +die(); + +if(!isset($post)) { + render_404(); + die(); +} + + +?> \ No newline at end of file diff --git a/www/src/setup_db.php b/www/src/setup_db.php new file mode 100644 index 0000000..70b3d88 --- /dev/null +++ b/www/src/setup_db.php @@ -0,0 +1,45 @@ +'; + echo 'Error number: ' . mysqli_connect_errno() . '
'; + echo 'Error message: ' . mysqli_connect_error() . '
'; + die(); +} + +$sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST); +$adapter = new PostHandler($sql_adapter); + +require_once 'dergdown.php'; + +function dergdown_to_html($text) { + $Parsedown = new Dergdown(); + + return $Parsedown->text($text); +} +function post_to_html($post) { + return dergdown_to_html($post->markdown); +} +$adapter->markdown_engine = "post_to_html"; + +$adapter->site_defaults = $SITE_CONFIG['site_defaults']; + +?> \ No newline at end of file diff --git a/www/src/setup_site_config.php b/www/src/setup_site_config.php new file mode 100644 index 0000000..683cb91 --- /dev/null +++ b/www/src/setup_site_config.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/www/src/setup_twig.php b/www/src/setup_twig.php new file mode 100644 index 0000000..e674428 --- /dev/null +++ b/www/src/setup_twig.php @@ -0,0 +1,24 @@ + true, + 'cache' => 'twig_cache' +]); +$twig->addExtension(new Twig\Extra\Markdown\MarkdownExtension()); + +use Twig\Extra\Markdown\DefaultMarkdown; +use Twig\Extra\Markdown\MarkdownRuntime; +use Twig\RuntimeLoader\RuntimeLoaderInterface; + +$twig->addRuntimeLoader(new class implements RuntimeLoaderInterface { + public function load($class) { + if (MarkdownRuntime::class === $class) { + return new MarkdownRuntime(new DefaultMarkdown()); + } + } +}); + + +?> \ No newline at end of file diff --git a/www/static/dergstyle.css b/www/static/dergstyle.css index 2a4c1f9..6ccc0f6 100644 --- a/www/static/dergstyle.css +++ b/www/static/dergstyle.css @@ -311,12 +311,21 @@ body.htmx-request::before { .folder-listing input { display: none; } -.folder-listing input + ul { +.folder-listing input ~ ul { display: none; } -.folder-listing input:checked + ul { +.folder-listing input:checked ~ ul { display: block; } +.folder-listing label > :nth-child(2) { + display: none; +} +.folder-listing input:checked ~ label > :nth-child(1) { + display: none; +} +.folder-listing input:checked ~ label > :nth-child(2) { + display: inline-block; +} #navbar-expand-label { cursor: pointer; diff --git a/www/templates/ajax/closed_folder_listing.html b/www/templates/ajax/closed_folder_listing.html deleted file mode 100644 index 399894f..0000000 --- a/www/templates/ajax/closed_folder_listing.html +++ /dev/null @@ -1,13 +0,0 @@ - -
  • - - {{ fa[post.post_metadata.icon] | raw }} - - - - {{ post.post_metadata.title }} - -
  • \ No newline at end of file diff --git a/www/templates/ajax/compact_filelist/entry.html b/www/templates/ajax/compact_filelist/entry.html new file mode 100644 index 0000000..6c63345 --- /dev/null +++ b/www/templates/ajax/compact_filelist/entry.html @@ -0,0 +1,27 @@ + + +
  • + {% set folder_key = random() %} + + + + + + {{ post.basename }} - {{ post.title }} + + + +
  • \ No newline at end of file diff --git a/www/templates/ajax/compact_filelist/listing.html b/www/templates/ajax/compact_filelist/listing.html new file mode 100644 index 0000000..ccdc7c9 --- /dev/null +++ b/www/templates/ajax/compact_filelist/listing.html @@ -0,0 +1,4 @@ + +{% for post in page.child_posts %} +{{ include('ajax/compact_filelist/entry.html') }} +{% endfor %} \ No newline at end of file diff --git a/www/templates/ajax/folder_listing.html b/www/templates/ajax/folder_listing.html deleted file mode 100644 index b90fb59..0000000 --- a/www/templates/ajax/folder_listing.html +++ /dev/null @@ -1,4 +0,0 @@ - -{% for post in subposts %} -{{ include('ajax/folder_listing_entry.html') }} -{% endfor %} \ No newline at end of file diff --git a/www/templates/ajax/folder_listing_entry.html b/www/templates/ajax/folder_listing_entry.html deleted file mode 100644 index e47bf9b..0000000 --- a/www/templates/ajax/folder_listing_entry.html +++ /dev/null @@ -1,26 +0,0 @@ - - -
  • - {% set folder_key = random() %} - - - - - {{ post.post_metadata.title }} - - - - -
  • \ No newline at end of file diff --git a/www/templates/ajax/open_folder_listing.html b/www/templates/ajax/open_folder_listing.html deleted file mode 100644 index 9bd6414..0000000 --- a/www/templates/ajax/open_folder_listing.html +++ /dev/null @@ -1,20 +0,0 @@ - - -
  • - - {{ fa[post.post_metadata.icon] | raw }} - - - - {{ post.post_metadata.title }} - - - -
  • \ No newline at end of file diff --git a/www/templates/fragments/filepath_bar.html b/www/templates/fragments/filepath_bar.html index 314e019..f4a43a2 100644 --- a/www/templates/fragments/filepath_bar.html +++ b/www/templates/fragments/filepath_bar.html @@ -1,11 +1,11 @@
  • - {{ post.post_metadata.title }} + {{ page.basename }}
  • - {% set split_post = post.post_path |split('/') %} + {% set split_post = page.path |split('/') %} {% for i in range(0, split_post|length - 1) %}
  • {% if i != 0 %} @@ -22,7 +22,7 @@
  • + style="padding-left: 0.3rem;" href="/feed/rss{{page.path}}"> {{ fa['rss']|raw }}
  • @@ -32,7 +32,7 @@
    diff --git a/www/templates/pathed_content.html b/www/templates/pathed_content.html index 90a0350..c8a3ffa 100644 --- a/www/templates/pathed_content.html +++ b/www/templates/pathed_content.html @@ -6,11 +6,11 @@ {{ parent() }} - + {% endblock %} {% block second_title %} -

    {{ post.post_metadata.title }}

    +

    {{ page.title }}

    {% endblock %} {%block main_content%} @@ -22,7 +22,7 @@ {%endblock%} - This article was created on {{ post.post_create_time }}, last edited {{ post.post_update_time }}, and was viewed {{ post.post_access_count }} times~ + This page was created on {{ page.created_at }}, last edited {{ page.updated_at }}, and was viewed {{ page.view_count }} times~ diff --git a/www/templates/post_types/markdown.html b/www/templates/post_types/markdown.html index b9d147c..f00142c 100644 --- a/www/templates/post_types/markdown.html +++ b/www/templates/post_types/markdown.html @@ -9,5 +9,9 @@ {%endblock %} {%block content_article%} - {{ content_html|raw }} + {% if page.title %} +

    {{ page.title }}

    + {% endif %} + + {{ page.html|raw }} {% endblock %} \ No newline at end of file diff --git a/www/templates/root.html b/www/templates/root.html index 0919f0e..08c3de4 100644 --- a/www/templates/root.html +++ b/www/templates/root.html @@ -1,7 +1,7 @@ - {{og.site_name}} - {{og.title}} + {{opengraph.site_name}} - {{opengraph.title}} @@ -12,7 +12,6 @@ - @@ -20,27 +19,31 @@ + {% if page.base %} + + {% endif %} + {% block feed_links %} - + {% endblock %} {% block extra_head %}{% endblock %} {% block opengraph_tags %} - + - + - - + + - + - - + + - - + + @@ -71,25 +74,29 @@ full picture -

    {% block big_title %}{{og.site_name}}{%endblock%}

    +

    {% block big_title %}{{opengraph.site_name}}{%endblock%}

    {% block second_title %}{% endblock %}
    -
  • About
  • -
  • Blog
  • -
  • Projects
  • -
  • Artworks
  • + {% for link in page.navbar_links %} +
  • {{link.text}}
  • + {% endfor %}
    - {% block main_content %}

    Soon there shall be content!

    {% endblock %} + {% block main_content %} +

    This here should have been replaced by content. +

    + + If you can see this, complain to your nearest dragon. + {% endblock %}