everything(everything): simply saving work
This commit is contained in:
parent
0f2761cd61
commit
76ca7b9c32
25 changed files with 1330 additions and 561 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
|
||||
/vendor/
|
||||
.docker_vols
|
||||
|
||||
sftp.json
|
0
Rakefile
Normal file
0
Rakefile
Normal file
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
||||
<filesMatch ".(flv|gif|ico|jpg|jpeg|mp4|mpeg|png|svg|swf|webp)$">
|
||||
Header set Cache-Control "max-age=315360, public"
|
||||
Header set Cache-Control "max-age=60, public"
|
||||
</filesMatch>
|
||||
|
||||
<filesMatch ".(js)$">
|
||||
Header set Cache-Control "max-age=315360, public"
|
||||
Header set Cache-Control "max-age=60, public"
|
||||
</filesMatch>
|
273
www/composer.lock
generated
273
www/composer.lock
generated
|
@ -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": [],
|
||||
|
|
200
www/post.php
200
www/post.php
|
@ -1,200 +0,0 @@
|
|||
|
||||
<?php
|
||||
|
||||
class PostData {
|
||||
public static $default_banners = [];
|
||||
public static $markdown_engine = null;
|
||||
|
||||
public $data;
|
||||
|
||||
public $html_data;
|
||||
public $raw_data;
|
||||
|
||||
public static function _generate_404($sql_row) {
|
||||
$sql_row ??= [
|
||||
'post_id' => -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);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,221 +0,0 @@
|
|||
<?php
|
||||
|
||||
require_once 'mysql_adapter.php';
|
||||
|
||||
use Spatie\YamlFrontMatter\YamlFrontMatter;
|
||||
use Laminas\Feed\Writer\Feed;
|
||||
|
||||
class PostHandler extends MySQLAdapter {
|
||||
public $data_directory;
|
||||
|
||||
function __construct($SITE_CONFIG) {
|
||||
parent::__construct($SITE_CONFIG);
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
17
www/src/db_handler/analytics_interface.php
Normal file
17
www/src/db_handler/analytics_interface.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
<?php
|
||||
|
||||
interface AnalyticsInterface {
|
||||
public function log_path_access($path,
|
||||
$agent,
|
||||
$time,
|
||||
$referrer);
|
||||
|
||||
public function log_path_errcode(
|
||||
$path,
|
||||
$agent,
|
||||
$referrer,
|
||||
$code);
|
||||
}
|
||||
|
||||
?>
|
86
www/src/db_handler/db_interface.php
Normal file
86
www/src/db_handler/db_interface.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
|
||||
<?php
|
||||
|
||||
function sanitize_post_path($post_path) {
|
||||
$post_path = chop($post_path, '/');
|
||||
|
||||
if($post_path == "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if(!preg_match('/^(?:\/[\w-]+)+(?:\.[\w-]+)*$/', $post_path)) {
|
||||
echo "Post path match against " . $post_path . " failed!";
|
||||
die();
|
||||
}
|
||||
|
||||
return $post_path;
|
||||
}
|
||||
|
||||
function escape_tag($tag) {
|
||||
return preg_replace_callback('/[\WZ]/', function($match) {
|
||||
return "Z" . ord($match[0]);
|
||||
}, strtolower($tag));
|
||||
}
|
||||
|
||||
function escape_search_tag($tag) {
|
||||
preg_match("/^([\+\-]?)(.*?)(\*?)$/", $tag, $matches);
|
||||
|
||||
if(!isset($matches[1])) {
|
||||
echo "Problem with tag!";
|
||||
var_dump($tag);
|
||||
}
|
||||
|
||||
return $matches[1] . $this->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);
|
||||
}
|
||||
|
||||
?>
|
348
www/src/db_handler/mysql_handler.php
Normal file
348
www/src/db_handler/mysql_handler.php
Normal file
|
@ -0,0 +1,348 @@
|
|||
|
||||
<?php
|
||||
|
||||
require_once 'analytics_interface.php';
|
||||
require_once 'db_interface.php';
|
||||
|
||||
function taglist_escape_tag($tag) {
|
||||
return preg_replace_callback('/[\WZ]/', function($match) {
|
||||
return "Z" . ord($match[0]);
|
||||
}, strtolower($tag));
|
||||
}
|
||||
|
||||
function taglist_to_sql_string($post_tags) {
|
||||
$post_tags = array_unique($post_tags);
|
||||
$post_tags = array_map(function($val) {
|
||||
return taglist_escape_tag($val);
|
||||
}, $post_tags);
|
||||
|
||||
asort($post_tags);
|
||||
$post_tags = join(' ', $post_tags);
|
||||
|
||||
return $post_tags;
|
||||
}
|
||||
|
||||
class MySQLHandler
|
||||
implements PostdataInterface {
|
||||
|
||||
CONST SQL_READ_COLUMNS = [
|
||||
'id', 'path', 'created_at', 'updated_at',
|
||||
'title', 'view_count', 'brief'];
|
||||
|
||||
CONST SQL_WRITE_COLUMNS = ['path', 'title', 'brief'];
|
||||
|
||||
private $sql_connection;
|
||||
|
||||
public $hostname;
|
||||
|
||||
public $debugging;
|
||||
|
||||
function __construct($sql_connection, $hostname) {
|
||||
$this->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'];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
205
www/src/db_handler/post.php
Normal file
205
www/src/db_handler/post.php
Normal file
|
@ -0,0 +1,205 @@
|
|||
|
||||
<?php
|
||||
|
||||
class Post implements ArrayAccess {
|
||||
public $handler;
|
||||
|
||||
private $content_html;
|
||||
private $content_markdown;
|
||||
|
||||
public $site_defaults;
|
||||
|
||||
public $data;
|
||||
|
||||
public $html_data;
|
||||
public $raw_data;
|
||||
|
||||
private $_child_posts;
|
||||
|
||||
public static function _generate_404($post_data) {
|
||||
$post_data ??= [
|
||||
'id' => -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) {
|
||||
$this->handler = $post_handler;
|
||||
|
||||
$this->content_html = null;
|
||||
$this->content_markdown = null;
|
||||
|
||||
$this->site_defaults = null;
|
||||
|
||||
if(!isset($post_data) or !isset($post_data['id'])) {
|
||||
$post_data = $this->_generate_404($post_data);
|
||||
}
|
||||
|
||||
$data = $post_data;
|
||||
|
||||
$post_data['host'] ??= 'localhost:8081';
|
||||
|
||||
$data['url'] ??= 'https://' . $post_data['host'] . $post_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(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 {
|
||||
throw RuntimeError("Setting of post data is not allowed!");
|
||||
}
|
||||
public function offsetUnset($offset) : void {
|
||||
throw RuntimeError("Unsetting of post data is not allowed!");
|
||||
}
|
||||
|
||||
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) {
|
||||
var_dump($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 ??= new PostData($this->handler,
|
||||
$this->handler->get_post_by_path($parent_path));
|
||||
|
||||
return $this->parent_post;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
57
www/src/db_handler/post_handler.php
Normal file
57
www/src/db_handler/post_handler.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
require_once 'db_interface.php';
|
||||
require_once 'db_handler/post.php';
|
||||
|
||||
class PostHandler {
|
||||
private $db;
|
||||
private $posts;
|
||||
|
||||
public $markdown_engine;
|
||||
|
||||
function __construct($db_adapter) {
|
||||
$this->db = $db_adapter;
|
||||
$this->posts = [];
|
||||
|
||||
$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->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));
|
||||
}
|
||||
|
||||
return $out_list;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
187
www/src/dbtest.php
Normal file
187
www/src/dbtest.php
Normal file
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
header("content-type: text/plain; charset=UTF-8; imeanit=yes");
|
||||
header("X-Content-Type-Options: nosniff");
|
||||
header('Content-Disposition: inline');
|
||||
|
||||
// Reporting E_NOTICE can be good too (to report uninitialized
|
||||
// variables or catch variable name misspellings ...)
|
||||
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
|
||||
|
||||
$data_time_start = microtime(true);
|
||||
|
||||
require_once '../vendor/autoload.php';
|
||||
|
||||
require_once 'db_handler/mysql_handler.php';
|
||||
require_once 'db_handler/post_handler.php';
|
||||
|
||||
require_once 'fontawesome.php';
|
||||
require_once 'dergdown.php';
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
$SERVER_HOST = $_SERVER['HTTP_HOST'];
|
||||
if(!preg_match('/^[\w\.\:]+$/', $SERVER_HOST)) {
|
||||
http_response_code(500);
|
||||
|
||||
echo "Not a valid server host (was " . $SERVER_HOST . ")";
|
||||
die();
|
||||
}
|
||||
|
||||
$SERVER_PREFIX = "https://" . $SERVER_HOST;
|
||||
|
||||
$SITE_CONFIG = Yaml::parseFile('../secrets/' . $SERVER_HOST . '.config.yml');
|
||||
$SITE_CONFIG['uri_prefix'] = $SERVER_PREFIX;
|
||||
$SITE_CONFIG['HTTP_HOST'] = $SERVER_HOST;
|
||||
|
||||
$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<br>';
|
||||
echo 'Error number: ' . mysqli_connect_errno() . '<br>';
|
||||
echo 'Error message: ' . mysqli_connect_error() . '<br>';
|
||||
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...'
|
||||
]);
|
||||
$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!'
|
||||
);
|
||||
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";
|
||||
?>
|
|
@ -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<br>';
|
||||
echo 'Error number: ' . mysqli_connect_errno() . '<br>';
|
||||
echo 'Error message: ' . mysqli_connect_error() . '<br>';
|
||||
die();
|
||||
}
|
||||
|
||||
$sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST);
|
||||
$adapter = new PostHandler($sql_adapter);
|
||||
|
||||
$loader = new \Twig\Loader\FilesystemLoader(['./templates', './user_content']);
|
||||
|
19
www/src/router.php
Normal file
19
www/src/router.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
$data_time_start = microtime(true);
|
||||
|
||||
require_once '../vendor/autoload.php';
|
||||
|
||||
require_once 'setup_site_config.php';
|
||||
require_once 'setup_db.php';
|
||||
|
||||
require_once 'fontawesome.php';
|
||||
require_once 'dergdown.php';
|
||||
|
||||
require_once 'setup_twig.php';
|
||||
|
||||
$PARSED_URL = parse_url($_SERVER['REQUEST_URI']);
|
||||
|
||||
require_once 'serve_post.php';
|
||||
|
||||
?>
|
46
www/src/serve_post.php
Normal file
46
www/src/serve_post.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
require_once 'fontawesome.php';
|
||||
|
||||
$post = $adapter->get_post($PARSED_URL['path']);
|
||||
if(isset($post)) {
|
||||
$post->site_defaults = $SITE_CONFIG['site_defaults'];
|
||||
}
|
||||
|
||||
function render_root_template($template, $args = []) {
|
||||
global $twig;
|
||||
global $FONT_AWESOME_ARRAY;
|
||||
global $SITE_CONFIG;
|
||||
|
||||
$args['fontawesome'] = $FONT_AWESOME_ARRAY;
|
||||
$args['page'] ??= $SITE_CONFIG['site_defaults'];
|
||||
|
||||
$page = $args['page'];
|
||||
|
||||
$args['opengraph'] = [
|
||||
"site_name" => $page['site_name'] ?? 'UNSET SITE NAME',
|
||||
"title" => $page['title'] ?? 'UNSET TITLE',
|
||||
"url" => $page['url'] ?? 'UNSET URL',
|
||||
"description" => $page['description'] ?? 'UNSET DESCRIPTION'
|
||||
];
|
||||
|
||||
$args['banners'] = json_encode($page['banners'] ?? []);
|
||||
|
||||
$args['age_gate'] = (!isset($_COOKIE['AgeConfirmed']))
|
||||
&& isset($SITE_CONFIG['age_gate']);
|
||||
|
||||
echo $twig->render($template, $args);
|
||||
}
|
||||
|
||||
render_root_template('root.html', [
|
||||
'page' => $post
|
||||
]);
|
||||
die();
|
||||
|
||||
if(!isset($post)) {
|
||||
render_404();
|
||||
die();
|
||||
}
|
||||
|
||||
|
||||
?>
|
43
www/src/setup_db.php
Normal file
43
www/src/setup_db.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
require_once 'db_handler/mysql_handler.php';
|
||||
require_once 'db_handler/post_handler.php';
|
||||
|
||||
$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<br>';
|
||||
echo 'Error number: ' . mysqli_connect_errno() . '<br>';
|
||||
echo 'Error message: ' . mysqli_connect_error() . '<br>';
|
||||
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";
|
||||
|
||||
?>
|
19
www/src/setup_site_config.php
Normal file
19
www/src/setup_site_config.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
$SERVER_HOST = $_SERVER['HTTP_HOST'];
|
||||
if(!preg_match('/^[\w\.\:]+$/', $SERVER_HOST)) {
|
||||
http_response_code(500);
|
||||
|
||||
echo "Not a valid server host (was " . $SERVER_HOST . ")";
|
||||
die();
|
||||
}
|
||||
|
||||
$SERVER_PREFIX = "https://" . $SERVER_HOST;
|
||||
|
||||
$SITE_CONFIG = Yaml::parseFile('../secrets/' . $SERVER_HOST . '.config.yml');
|
||||
$SITE_CONFIG['uri_prefix'] = $SERVER_PREFIX;
|
||||
$SITE_CONFIG['HTTP_HOST'] = $SERVER_HOST;
|
||||
|
||||
?>
|
24
www/src/setup_twig.php
Normal file
24
www/src/setup_twig.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
$loader = new \Twig\Loader\FilesystemLoader(['../templates']);
|
||||
|
||||
$twig = new \Twig\Environment($loader,[
|
||||
'debug' => 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());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
?>
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{og.site_name}} - {{og.title}}</title>
|
||||
<title>{{opengraph.site_name}} - {{opengraph.title}}</title>
|
||||
|
||||
<link rel="stylesheet" href="/static/dergstyle.css">
|
||||
|
||||
|
@ -21,26 +21,26 @@
|
|||
<script id="main_banner_script" src="/static/banner.js"></script>
|
||||
|
||||
{% block feed_links %}
|
||||
<link rel="alternate" type="application/rss+xml" title="{{og.site_name}} Global Feed" href="{{site_config.uri_prefix}}/feed">
|
||||
<link rel="alternate" type="application/rss+xml" title="{{opengraph.site_name}} Global Feed" href="{{site_config.uri_prefix}}/feed">
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_head %}{% endblock %}
|
||||
|
||||
{% block opengraph_tags %}
|
||||
<meta property="og:site_name" content="{{og.site_name}}">
|
||||
<meta property="og:site_name" content="{{opengraph.site_name}}">
|
||||
|
||||
<meta property="og:url" content="{{og.url}}" />
|
||||
<meta property="og:url" content="{{opengraph.url}}" />
|
||||
|
||||
<meta property="og:title" content="{{ og.title|e }}" />
|
||||
<meta name="twitter:title" content="{{ og.title|e }}" />
|
||||
<meta property="og:title" content="{{ opengraph.title|e }}" />
|
||||
<meta name="twitter:title" content="{{ opengraph.title|e }}" />
|
||||
|
||||
<meta property="og:type" content="{{og.type}}" />
|
||||
<meta property="og:type" content="{{opengraph.type}}" />
|
||||
|
||||
<meta property="og:description" content="{{ og.description|e }}" />
|
||||
<meta name="twitter:description" content="{{ og.description|e }}" />
|
||||
<meta property="og:description" content="{{ opengraph.description|e }}" />
|
||||
<meta name="twitter:description" content="{{ opengraph.description|e }}" />
|
||||
|
||||
<meta property="og:image" content="{{og.image}}" />
|
||||
<meta name="twitter:image" content="{{og.image}}" />
|
||||
<meta property="og:image" content="{{opengraph.image}}" />
|
||||
<meta name="twitter:image" content="{{opengraph.image}}" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="robots" content="max-image-preview:large">
|
||||
|
@ -71,25 +71,29 @@
|
|||
<a id="main_banner_img_link" href="/gallery" hx-preserve> full picture</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.dergBannerOptions = JSON.parse('{{banner|raw}}');
|
||||
window.dergBannerOptions = JSON.parse('{{banners|raw}}');
|
||||
|
||||
startBanner();
|
||||
</script>
|
||||
|
||||
<h1 id="big_title">{% block big_title %}{{og.site_name}}{%endblock%}</h1>
|
||||
<h1 id="big_title">{% block big_title %}{{opengraph.site_name}}{%endblock%}</h1>
|
||||
{% block second_title %}{% endblock %}
|
||||
<div id="title_separator"></div>
|
||||
|
||||
<menu id="nav_bar">
|
||||
<li><a href="/about"> About </a></li>
|
||||
<li><a href="/blog"> Blog </a></li>
|
||||
<li><a href="/projects"> Projects </a></li>
|
||||
<li><a href="/artwork"> Artworks </a></li>
|
||||
{% for link in page.navbar_links %}
|
||||
<li><a href="{{link.href}}"> {{link.text}} </a></li>
|
||||
{% endfor %}
|
||||
</menu>
|
||||
</header>
|
||||
|
||||
<main id="main_content_wrapper">
|
||||
{% block main_content %}<h3>Soon there shall be content!</h3>{% endblock %}
|
||||
{% block main_content %}
|
||||
<h3>This here should have been replaced by content.
|
||||
</h3>
|
||||
|
||||
If you can see this, complain to your nearest dragon.
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer id="main_footer">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue