Compare commits

...

12 commits

22 changed files with 1408 additions and 212 deletions

View file

@ -6,6 +6,7 @@ USE dragon_fire;
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,
@ -20,9 +21,9 @@ CREATE TABLE posts (
post_content MEDIUMTEXT,
PRIMARY KEY(post_id),
CONSTRAINT unique_post_path UNIQUE (post_path),
CONSTRAINT unique_post UNIQUE (host, post_path),
INDEX(post_path),
INDEX(host, post_path),
INDEX(post_path_depth, post_path),
INDEX(post_create_time),
INDEX(post_update_time)
@ -30,6 +31,7 @@ CREATE TABLE posts (
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),
@ -37,10 +39,11 @@ CREATE TABLE path_access_counts (
path_access_count INTEGER DEFAULT 0,
path_processing_time DOUBLE PRECISION DEFAULT 0,
PRIMARY KEY(access_time, post_path, agent, referrer)
PRIMARY KEY(access_time, host, post_path, agent, referrer)
);
CREATE TABLE feed_cache (
host VARCHAR(64) NOT NULL,
search_path VARCHAR(255),
export_type VARCHAR(255),
@ -48,7 +51,7 @@ CREATE TABLE feed_cache (
feed_content MEDIUMTEXT,
PRIMARY KEY(search_path, export_type)
PRIMARY KEY(host, search_path, export_type)
);
INSERT INTO posts (post_path, post_path_depth, post_metadata, post_content)

View file

@ -3,12 +3,21 @@ AddType text/plain .md
AddType text/plain .atom
AddType text/plain .rss
php_value upload_max_filesize 40M
php_value post_max_size 42M
# php_value upload_max_filesize 40M
# php_value post_max_size 42M
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !^/(raw|static)/
RewriteRule ^.*\.(flv|gif|ico|jpg|jpeg|mp4|mpeg|png|svg|swf|webp)$ raw/%{HTTP_HOST}%{REQUEST_URI} [L,END]
RewriteRule ^/?raw/(.*)$ raw/%{HTTP_HOST}/$1 [L,END]
RewriteEngine On
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,END]
RewriteCond %{REQUEST_URI} !^/?(static|raw|robots\.txt).*
RewriteRule (.*) router.php

View file

@ -4,6 +4,9 @@
"twig/markdown-extra": "^3.6",
"league/commonmark": "^2.4",
"spatie/yaml-front-matter": "^2.0",
"laminas/laminas-feed": "^2.6"
"laminas/laminas-feed": "^2.6",
"erusev/parsedown": "^1.7",
"erusev/parsedown-extra": "^0.8.1",
"scrivo/highlight.php": "v9.18.1.10"
}
}

604
www/composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ae80529cc2442878a51f38de1f7017a4",
"content-hash": "34ef4bdab7508bbe6f60a1a56e33d08c",
"packages": [
{
"name": "dflydev/dot-access-data",
@ -81,6 +81,442 @@
},
"time": "2022-10-27T11:44:00+00:00"
},
{
"name": "erusev/parsedown",
"version": "1.7.4",
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown.git",
"reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
"reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
},
"type": "library",
"autoload": {
"psr-0": {
"Parsedown": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "Parser for Markdown.",
"homepage": "http://parsedown.org",
"keywords": [
"markdown",
"parser"
],
"support": {
"issues": "https://github.com/erusev/parsedown/issues",
"source": "https://github.com/erusev/parsedown/tree/1.7.x"
},
"time": "2019-12-30T22:54:17+00:00"
},
{
"name": "erusev/parsedown-extra",
"version": "0.8.1",
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown-extra.git",
"reference": "91ac3ff98f0cea243bdccc688df43810f044dcef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/91ac3ff98f0cea243bdccc688df43810f044dcef",
"reference": "91ac3ff98f0cea243bdccc688df43810f044dcef",
"shasum": ""
},
"require": {
"erusev/parsedown": "^1.7.4"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
},
"type": "library",
"autoload": {
"psr-0": {
"ParsedownExtra": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "An extension of Parsedown that adds support for Markdown Extra.",
"homepage": "https://github.com/erusev/parsedown-extra",
"keywords": [
"markdown",
"markdown extra",
"parsedown",
"parser"
],
"support": {
"issues": "https://github.com/erusev/parsedown-extra/issues",
"source": "https://github.com/erusev/parsedown-extra/tree/0.8.x"
},
"time": "2019-12-30T23:20:37+00:00"
},
{
"name": "laminas/laminas-escaper",
"version": "2.13.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-escaper.git",
"reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/af459883f4018d0f8a0c69c7a209daef3bf973ba",
"reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-mbstring": "*",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0"
},
"conflict": {
"zendframework/zend-escaper": "*"
},
"require-dev": {
"infection/infection": "^0.27.0",
"laminas/laminas-coding-standard": "~2.5.0",
"maglnet/composer-require-checker": "^3.8.0",
"phpunit/phpunit": "^9.6.7",
"psalm/plugin-phpunit": "^0.18.4",
"vimeo/psalm": "^5.9"
},
"type": "library",
"autoload": {
"psr-4": {
"Laminas\\Escaper\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs",
"homepage": "https://laminas.dev",
"keywords": [
"escaper",
"laminas"
],
"support": {
"chat": "https://laminas.dev/chat",
"docs": "https://docs.laminas.dev/laminas-escaper/",
"forum": "https://discourse.laminas.dev",
"issues": "https://github.com/laminas/laminas-escaper/issues",
"rss": "https://github.com/laminas/laminas-escaper/releases.atom",
"source": "https://github.com/laminas/laminas-escaper"
},
"funding": [
{
"url": "https://funding.communitybridge.org/projects/laminas-project",
"type": "community_bridge"
}
],
"time": "2023-10-10T08:35:13+00:00"
},
{
"name": "laminas/laminas-feed",
"version": "2.6.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-feed.git",
"reference": "c1594cb32b117d3b409d4beee12c724cb26daa71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-feed/zipball/c1594cb32b117d3b409d4beee12c724cb26daa71",
"reference": "c1594cb32b117d3b409d4beee12c724cb26daa71",
"shasum": ""
},
"require": {
"laminas/laminas-escaper": "~2.5",
"laminas/laminas-stdlib": "~2.5",
"laminas/laminas-zendframework-bridge": "^1.0",
"php": ">=5.5"
},
"replace": {
"zendframework/zend-feed": "self.version"
},
"require-dev": {
"fabpot/php-cs-fixer": "1.7.*",
"laminas/laminas-cache": "~2.5",
"laminas/laminas-db": "~2.5",
"laminas/laminas-http": "~2.5",
"laminas/laminas-validator": "~2.5",
"phpunit/phpunit": "~4.0",
"psr/http-message": "^1.0"
},
"suggest": {
"laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests",
"laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub",
"laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader",
"laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations",
"laminas/laminas-validator": "Laminas\\Validator component, for validating feeds and Atom entries in the Writer subcomponent",
"psr/http-message": "PSR-7 ^1.0, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.6-dev",
"dev-develop": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Laminas\\Feed\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "provides functionality for consuming RSS and Atom feeds",
"homepage": "https://laminas.dev",
"keywords": [
"feed",
"laminas"
],
"support": {
"chat": "https://laminas.dev/chat",
"docs": "https://docs.laminas.dev/laminas-feed/",
"forum": "https://discourse.laminas.dev",
"issues": "https://github.com/laminas/laminas-feed/issues",
"rss": "https://github.com/laminas/laminas-feed/releases.atom",
"source": "https://github.com/laminas/laminas-feed"
},
"time": "2019-12-31T16:50:31+00:00"
},
{
"name": "laminas/laminas-hydrator",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-hydrator.git",
"reference": "acab29a3327a70be0a653d88906655b15de15517"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/acab29a3327a70be0a653d88906655b15de15517",
"reference": "acab29a3327a70be0a653d88906655b15de15517",
"shasum": ""
},
"require": {
"laminas/laminas-stdlib": "^2.5.1",
"laminas/laminas-zendframework-bridge": "^1.0",
"php": ">=5.5"
},
"replace": {
"zendframework/zend-hydrator": "self.version"
},
"require-dev": {
"laminas/laminas-eventmanager": "^2.5.1",
"laminas/laminas-filter": "^2.5.1",
"laminas/laminas-inputfilter": "^2.5.1",
"laminas/laminas-serializer": "^2.5.1",
"laminas/laminas-servicemanager": "^2.5.1",
"phpunit/phpunit": "~4.0",
"squizlabs/php_codesniffer": "^2.0@dev"
},
"suggest": {
"laminas/laminas-eventmanager": "^2.5.1, to support aggregate hydrator usage",
"laminas/laminas-filter": "^2.5.1, to support naming strategy hydrator usage",
"laminas/laminas-serializer": "^2.5.1, to use the SerializableStrategy",
"laminas/laminas-servicemanager": "^2.5.1, to support hydrator plugin manager usage"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev",
"dev-develop": "1.1-dev"
}
},
"autoload": {
"psr-4": {
"Laminas\\Hydrator\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"homepage": "https://laminas.dev",
"keywords": [
"hydrator",
"laminas"
],
"support": {
"chat": "https://laminas.dev/chat",
"docs": "https://docs.laminas.dev/laminas-hydrator/",
"forum": "https://discourse.laminas.dev",
"issues": "https://github.com/laminas/laminas-hydrator/issues",
"rss": "https://github.com/laminas/laminas-hydrator/releases.atom",
"source": "https://github.com/laminas/laminas-hydrator"
},
"time": "2019-12-31T17:06:20+00:00"
},
{
"name": "laminas/laminas-stdlib",
"version": "2.7.4",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-stdlib.git",
"reference": "b69e2741673daabdf720bc76668448ceb1e4c537"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/b69e2741673daabdf720bc76668448ceb1e4c537",
"reference": "b69e2741673daabdf720bc76668448ceb1e4c537",
"shasum": ""
},
"require": {
"laminas/laminas-hydrator": "~1.0",
"laminas/laminas-zendframework-bridge": "^1.0",
"php": ">=5.5"
},
"replace": {
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
"athletic/athletic": "~0.1",
"fabpot/php-cs-fixer": "1.7.*",
"laminas/laminas-config": "~2.5",
"laminas/laminas-eventmanager": "~2.5",
"laminas/laminas-filter": "~2.5",
"laminas/laminas-inputfilter": "~2.5",
"laminas/laminas-serializer": "~2.5",
"laminas/laminas-servicemanager": "~2.5",
"phpunit/phpunit": "~4.0"
},
"suggest": {
"laminas/laminas-eventmanager": "To support aggregate hydrator usage",
"laminas/laminas-filter": "To support naming strategy hydrator usage",
"laminas/laminas-serializer": "Laminas\\Serializer component",
"laminas/laminas-servicemanager": "To support hydrator plugin manager usage"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev",
"dev-develop": "2.8-dev"
}
},
"autoload": {
"psr-4": {
"Laminas\\Stdlib\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"homepage": "https://laminas.dev",
"keywords": [
"laminas",
"stdlib"
],
"support": {
"chat": "https://laminas.dev/chat",
"docs": "https://docs.laminas.dev/laminas-stdlib/",
"forum": "https://discourse.laminas.dev",
"issues": "https://github.com/laminas/laminas-stdlib/issues",
"rss": "https://github.com/laminas/laminas-stdlib/releases.atom",
"source": "https://github.com/laminas/laminas-stdlib"
},
"time": "2019-12-31T17:51:00+00:00"
},
{
"name": "laminas/laminas-zendframework-bridge",
"version": "1.8.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-zendframework-bridge.git",
"reference": "eb0d96c708b92177a92bc2239543d3ed523452c6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/eb0d96c708b92177a92bc2239543d3ed523452c6",
"reference": "eb0d96c708b92177a92bc2239543d3ed523452c6",
"shasum": ""
},
"require": {
"php": "~8.1.0 || ~8.2.0 || ~8.3.0"
},
"require-dev": {
"phpunit/phpunit": "^10.4",
"psalm/plugin-phpunit": "^0.18.0",
"squizlabs/php_codesniffer": "^3.7.1",
"vimeo/psalm": "^5.16.0"
},
"type": "library",
"extra": {
"laminas": {
"module": "Laminas\\ZendFrameworkBridge"
}
},
"autoload": {
"files": [
"src/autoload.php"
],
"psr-4": {
"Laminas\\ZendFrameworkBridge\\": "src//"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "Alias legacy ZF class names to Laminas Project equivalents.",
"keywords": [
"ZendFramework",
"autoloading",
"laminas",
"zf"
],
"support": {
"forum": "https://discourse.laminas.dev/",
"issues": "https://github.com/laminas/laminas-zendframework-bridge/issues",
"rss": "https://github.com/laminas/laminas-zendframework-bridge/releases.atom",
"source": "https://github.com/laminas/laminas-zendframework-bridge"
},
"funding": [
{
"url": "https://funding.communitybridge.org/projects/laminas-project",
"type": "community_bridge"
}
],
"abandoned": true,
"time": "2023-11-24T13:56:19+00:00"
},
{
"name": "league/commonmark",
"version": "2.4.1",
@ -333,16 +769,16 @@
},
{
"name": "nette/utils",
"version": "v4.0.2",
"version": "v4.0.3",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
"reference": "cead6637226456b35e1175cc53797dd585d85545"
"reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/utils/zipball/cead6637226456b35e1175cc53797dd585d85545",
"reference": "cead6637226456b35e1175cc53797dd585d85545",
"url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015",
"reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015",
"shasum": ""
},
"require": {
@ -413,9 +849,9 @@
],
"support": {
"issues": "https://github.com/nette/utils/issues",
"source": "https://github.com/nette/utils/tree/v4.0.2"
"source": "https://github.com/nette/utils/tree/v4.0.3"
},
"time": "2023-09-19T11:58:07+00:00"
"time": "2023-10-29T21:02:13+00:00"
},
{
"name": "psr/event-dispatcher",
@ -468,22 +904,100 @@
"time": "2019-01-08T18:20:26+00:00"
},
{
"name": "spatie/yaml-front-matter",
"version": "2.0.7",
"name": "scrivo/highlight.php",
"version": "v9.18.1.10",
"source": {
"type": "git",
"url": "https://github.com/spatie/yaml-front-matter.git",
"reference": "f49f228994de70827ca857efffdd3bd7703aea34"
"url": "https://github.com/scrivo/highlight.php.git",
"reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/f49f228994de70827ca857efffdd3bd7703aea34",
"reference": "f49f228994de70827ca857efffdd3bd7703aea34",
"url": "https://api.github.com/repos/scrivo/highlight.php/zipball/850f4b44697a2552e892ffe71490ba2733c2fc6e",
"reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=5.4"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.7",
"sabberworm/php-css-parser": "^8.3",
"symfony/finder": "^2.8|^3.4|^5.4",
"symfony/var-dumper": "^2.8|^3.4|^5.4"
},
"suggest": {
"ext-mbstring": "Allows highlighting code with unicode characters and supports language with unicode keywords"
},
"type": "library",
"autoload": {
"files": [
"HighlightUtilities/functions.php"
],
"psr-0": {
"Highlight\\": "",
"HighlightUtilities\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Geert Bergman",
"homepage": "http://www.scrivo.org/",
"role": "Project Author"
},
{
"name": "Vladimir Jimenez",
"homepage": "https://allejo.io",
"role": "Maintainer"
},
{
"name": "Martin Folkers",
"homepage": "https://twobrain.io",
"role": "Contributor"
}
],
"description": "Server side syntax highlighter that supports 185 languages. It's a PHP port of highlight.js",
"keywords": [
"code",
"highlight",
"highlight.js",
"highlight.php",
"syntax"
],
"support": {
"issues": "https://github.com/scrivo/highlight.php/issues",
"source": "https://github.com/scrivo/highlight.php"
},
"funding": [
{
"url": "https://github.com/allejo",
"type": "github"
}
],
"time": "2022-12-17T21:53:22+00:00"
},
{
"name": "spatie/yaml-front-matter",
"version": "2.0.8",
"source": {
"type": "git",
"url": "https://github.com/spatie/yaml-front-matter.git",
"reference": "f2f1f749a405fafc9d6337067c92c062d51a581c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/f2f1f749a405fafc9d6337067c92c062d51a581c",
"reference": "f2f1f749a405fafc9d6337067c92c062d51a581c",
"shasum": ""
},
"require": {
"php": "^7.0|^8.0",
"symfony/yaml": "^3.0|^4.0|^5.0|^6.0"
"symfony/yaml": "^3.0|^4.0|^5.0|^6.0|^7.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
@ -515,7 +1029,7 @@
"yaml"
],
"support": {
"source": "https://github.com/spatie/yaml-front-matter/tree/2.0.7"
"source": "https://github.com/spatie/yaml-front-matter/tree/2.0.8"
},
"funding": [
{
@ -527,11 +1041,11 @@
"type": "github"
}
],
"time": "2022-04-06T12:03:55+00:00"
"time": "2023-12-04T10:02:52+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.3.0",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
@ -578,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.3.0"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
},
"funding": [
{
@ -846,28 +1360,27 @@
},
{
"name": "symfony/yaml",
"version": "v6.3.3",
"version": "v7.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "e23292e8c07c85b971b44c1c4b87af52133e2add"
"reference": "0055b230c408428b9b5cde7c55659555be5c0278"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/e23292e8c07c85b971b44c1c4b87af52133e2add",
"reference": "e23292e8c07c85b971b44c1c4b87af52133e2add",
"url": "https://api.github.com/repos/symfony/yaml/zipball/0055b230c408428b9b5cde7c55659555be5c0278",
"reference": "0055b230c408428b9b5cde7c55659555be5c0278",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/deprecation-contracts": "^2.5|^3",
"php": ">=8.2",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"symfony/console": "<5.4"
"symfony/console": "<6.4"
},
"require-dev": {
"symfony/console": "^5.4|^6.0"
"symfony/console": "^6.4|^7.0"
},
"bin": [
"Resources/bin/yaml-lint"
@ -898,7 +1411,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v6.3.3"
"source": "https://github.com/symfony/yaml/tree/v7.0.0"
},
"funding": [
{
@ -914,32 +1427,32 @@
"type": "tidelift"
}
],
"time": "2023-07-31T07:08:24+00:00"
"time": "2023-11-07T10:26:03+00:00"
},
{
"name": "twig/markdown-extra",
"version": "v3.7.1",
"version": "v3.8.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/markdown-extra.git",
"reference": "83dfa86a0379f784ea30bdb9c15a356b8aabf780"
"reference": "b6e4954ab60030233df5d293886b5404558daac8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/83dfa86a0379f784ea30bdb9c15a356b8aabf780",
"reference": "83dfa86a0379f784ea30bdb9c15a356b8aabf780",
"url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/b6e4954ab60030233df5d293886b5404558daac8",
"reference": "b6e4954ab60030233df5d293886b5404558daac8",
"shasum": ""
},
"require": {
"php": ">=7.1.3",
"twig/twig": "^2.7|^3.0"
"php": ">=7.2.5",
"twig/twig": "^3.0"
},
"require-dev": {
"erusev/parsedown": "^1.7",
"league/commonmark": "^1.0|^2.0",
"league/html-to-markdown": "^4.8|^5.0",
"michelf/php-markdown": "^1.8|^2.0",
"symfony/phpunit-bridge": "^5.4|^6.3"
"symfony/phpunit-bridge": "^6.4|^7.0"
},
"type": "library",
"autoload": {
@ -970,7 +1483,7 @@
"twig"
],
"support": {
"source": "https://github.com/twigphp/markdown-extra/tree/v3.7.1"
"source": "https://github.com/twigphp/markdown-extra/tree/v3.8.0"
},
"funding": [
{
@ -982,30 +1495,31 @@
"type": "tidelift"
}
],
"time": "2023-07-29T15:34:56+00:00"
"time": "2023-11-21T14:02:01+00:00"
},
{
"name": "twig/twig",
"version": "v3.7.1",
"version": "v3.8.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554"
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554",
"reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3"
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php80": "^1.22"
},
"require-dev": {
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.3"
"symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0"
},
"type": "library",
"autoload": {
@ -1041,7 +1555,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.7.1"
"source": "https://github.com/twigphp/Twig/tree/v3.8.0"
},
"funding": [
{
@ -1053,7 +1567,7 @@
"type": "tidelift"
}
],
"time": "2023-08-28T11:09:02+00:00"
"time": "2023-11-21T18:54:41+00:00"
}
],
"packages-dev": [],

39
www/dergdown.php Normal file
View file

@ -0,0 +1,39 @@
<?php
use Highlight\Highlighter;
class Dergdown extends ParsedownExtra
{
protected $highlighter;
public function __construct()
{
$this->highlighter = new Highlighter();
}
protected function blockFencedCodeComplete($block)
{
if (! isset($block['element']['text']['attributes'])) {
return $block;
}
$code = $block['element']['text']['text'];
$languageClass = $block['element']['text']['attributes']['class'];
$language = explode('-', $languageClass);
try {
$highlighted = $this->highlighter->highlight($language[1], $code);
$block['element']['text']['attributes']['class'] = vsprintf('%s hljs %s', [
$languageClass,
$highlighted->language,
]);
$block['element']['text']['rawHtml'] = $highlighted->value;
unset($block['element']['text']['text']);
} catch (DomainException $e) {
}
return $block;
}
}

View file

@ -3,6 +3,7 @@
$FONT_AWESOME_ARRAY=[
'markdown' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="20" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M593.8 59.1H46.2C20.7 59.1 0 79.8 0 105.2v301.5c0 25.5 20.7 46.2 46.2 46.2h547.7c25.5 0 46.2-20.7 46.1-46.1V105.2c0-25.4-20.7-46.1-46.2-46.1zM338.5 360.6H277v-120l-61.5 76.9-61.5-76.9v120H92.3V151.4h61.5l61.5 76.9 61.5-76.9h61.5v209.2zm135.3 3.1L381.5 256H443V151.4h61.5V256H566z"/></svg>',
'image' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M0 96C0 60.7 28.7 32 64 32H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6h96 32H424c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>',
'images' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M160 32c-35.3 0-64 28.7-64 64V320c0 35.3 28.7 64 64 64H512c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H160zM396 138.7l96 144c4.9 7.4 5.4 16.8 1.2 24.6S480.9 320 472 320H328 280 200c-9.2 0-17.6-5.3-21.6-13.6s-2.9-18.2 2.9-25.4l64-80c4.6-5.7 11.4-9 18.7-9s14.2 3.3 18.7 9l17.3 21.6 56-84C360.5 132 368 128 376 128s15.5 4 20 10.7zM192 128a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zM48 120c0-13.3-10.7-24-24-24S0 106.7 0 120V344c0 75.1 60.9 136 136 136H456c13.3 0 24-10.7 24-24s-10.7-24-24-24H136c-48.6 0-88-39.4-88-88V120z"/></svg>',
'folder' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z"/></svg>',
'rss' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM96 136c0-13.3 10.7-24 24-24c137 0 248 111 248 248c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-110.5-89.5-200-200-200c-13.3 0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24c83.9 0 152 68.1 152 152c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-57.4-46.6-104-104-104c-13.3 0-24-10.7-24-24zm0 120a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>'
];

View file

@ -3,8 +3,10 @@
class MySQLAdapter {
public $raw;
function __construct() {
$db_params = json_decode(file_get_contents('secrets/db.json'), true);
function __construct($SITE_CONFIG) {
$this->SITE_CONFIG = $SITE_CONFIG;
$db_params = $SITE_CONFIG['db'];
try {
if(false !== getenv('MYSQL_HOST')) {
@ -14,18 +16,16 @@ class MySQLAdapter {
getenv('MYSQL_PORT'));
}
else {
$this->raw = mysqli_connect($db_params['MYSQL_HOST'],
$db_params['MYSQL_USER'], $db_params['MYSQL_PASSWORD'],
$db_params['MYSQL_DATABASE'],
$db_params['MYSQL_PORT']);
$this->raw = 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();
//throw $th;
}
}
@ -69,6 +69,14 @@ class MySQLAdapter {
return $post_data;
}
function _normalize_post_array($post_data) {
$post_data ??= [];
return array_map(function($post) {
return $this->_normalize_post_data($post);
}, $post_data);
}
function bump_post($post_path, $post_metadata = [], $create_dirs = true) {
$post_path = $this->_sanitize_path($post_path);
$path_depth = substr_count($post_path, "/");
@ -79,12 +87,13 @@ class MySQLAdapter {
$qry = "
INSERT INTO posts
(post_path, post_path_depth, post_metadata, post_content)
(host, post_path, post_path_depth, post_metadata, post_content)
VALUES
( ?, ?, ?, ?) AS new
( ?, ?, ?, ?, ?) AS new
ON DUPLICATE KEY UPDATE post_path=new.post_path;";
$this->_exec($qry, "siss",
$this->_exec($qry, "ssiss",
$this->SITE_CONFIG['HTTP_HOST'],
$post_path,
$path_depth,
json_encode($post_metadata),
@ -110,27 +119,28 @@ class MySQLAdapter {
$qry = "INSERT INTO path_access_counts
(access_time,
post_path, agent, referrer,
host, post_path, agent, referrer,
path_access_count,
path_processing_time)
VALUES ( from_unixtime(floor(unix_timestamp(CURRENT_TIMESTAMP) / 300)*300),
?, ?, ?, 1, ?
?, ?, ?, ?, 1, ?
) AS new
ON DUPLICATE KEY
UPDATE path_access_count=path_access_counts.path_access_count+1,
path_processing_time=path_access_counts.path_processing_time+new.path_processing_time;
";
$this->_exec($qry, "sssd", $post_path, $agent, $referrer, $time);
$this->_exec($qry, "ssssd", $this->SITE_CONFIG['HTTP_HOST'], $post_path, $agent, $referrer, $time);
if(preg_match('/^user/', $agent)) {
$this->_exec("UPDATE posts SET post_access_count=post_access_count+1 WHERE post_path=?", "s", $post_path);
$this->_exec("UPDATE posts SET post_access_count=post_access_count+1 WHERE post_path=? AND host=?", "ss",
$post_path, $this->SITE_CONFIG['HTTP_HOST']);
}
}
function get_post_access_counters() {
$qry = "
SELECT post_path, agent, path_access_count, path_processing_time
SELECT host, post_path, agent, path_access_count, path_processing_time
FROM path_access_counts
WHERE path_last_access_time > ( CURRENT_TIMESTAMP - INTERVAL 10 MINUTE );
";
@ -157,7 +167,7 @@ class MySQLAdapter {
function get_post_access_counters_line() {
$qry = "
SELECT access_time, post_path, agent, referrer, path_access_count, path_processing_time
SELECT host, access_time, post_path, agent, referrer, path_access_count, path_processing_time
FROM path_access_counts
WHERE access_time < ( CURRENT_TIMESTAMP - INTERVAL 6 MINUTE )
ORDER BY access_time DESC;
@ -169,7 +179,7 @@ class MySQLAdapter {
try {
$data = $this->_exec($qry, "")->fetch_all(MYSQLI_ASSOC);
$data_prefix="access_metrics,host=" . $_SERVER['SERVER_NAME'];
$data_prefix="access_metrics";
$out_data = "";
@ -180,7 +190,8 @@ class MySQLAdapter {
if($path == '') {
$path = '/';
}
$out_data .= $data_prefix . ",agent=".$post_data['agent'].",path=".$path.",referrer=".$post_data['referrer'];
$out_data .= $data_prefix . ",host=" . $post_data['host'] . ",agent=".$post_data['agent'];
$out_data .= ",path=".$path.",referrer=".$post_data['referrer'];
$out_data .= " access_sum=" . $post_data['path_access_count'] . ",time_sum=" . $post_data['path_processing_time'];
$out_data .= " " . strtotime($post_data['access_time']) . "000000000\n";
@ -205,8 +216,88 @@ class MySQLAdapter {
$this->_exec("
UPDATE posts
SET post_settings_cache=NULL
WHERE post_path LIKE ?;
", "s", $post_path . "%");
WHERE host = ? AND post_path LIKE ?;
", "ss", $this->SITE_CONFIG['HTTP_HOST'], $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];
}
function update_post_search_data($post_path, $post_tags) {
$post_tags []= "path:" . $post_path;
$post_tags []= "host:" . $this->SITE_CONFIG['HTTP_HOST'];
$post_tags = array_unique($post_tags);
$post_tags = array_map(function($val) {
return $this->escape_tag($val);
}, $post_tags);
asort($post_tags);
$post_tags = join(' ', $post_tags);
$qry = "
INSERT INTO posts
( host, post_path, post_tags )
VALUES
( ?, ?, ? ) AS new
ON DUPLICATE KEY
UPDATE post_tags=new.post_tags;
";
$this->_exec($qry, "sss",
$this->SITE_CONFIG['HTTP_HOST'], $post_path, $post_tags);
}
function perform_post_search($taglist, $order = null, $limit = 20, $page = 0) {
$allowed_ordering = [
"post_create_time"
];
$qry = "
SELECT post_path, post_metadata
FROM posts
WHERE MATCH(post_tags) AGAINST (? IN BOOLEAN MODE)
";
if(!is_array($taglist)) {
$taglist = explode(' ', $taglist);
}
$taglist []= '+host:' . $this->SITE_CONFIG['HTTP_HOST'];
$taglist = array_unique($taglist);
$taglist = array_map(function($key) {
return $this->escape_search_tag($key);
}, $taglist);
$taglist = implode(' ', $taglist);
if(isset($order) and in_array($order, $allowed_ordering)) {
$qry = $qry . " ORDER BY " . $order;
}
$qry = $qry . " LIMIT ? OFFSET ?";
$search_results = $this->_exec($qry, "sii", $taglist, $limit, $limit * $page)->fetch_all(MYSQLI_ASSOC);
$search_results = [
"query_string" => $taglist,
"results" => $this->_normalize_post_array($search_results)
];
return $search_results;
}
function update_or_create_post($post_path, $post_metadata, $post_content) {
@ -219,19 +310,22 @@ class MySQLAdapter {
$qry = "
INSERT INTO posts
(post_path, post_path_depth, post_metadata, post_content)
(host, post_path, post_path_depth, post_metadata, post_content)
VALUES
( ?, ?, ?, ?) AS new
( ?, ?, ?, ?, ?) AS new
ON DUPLICATE KEY
UPDATE post_metadata=new.post_metadata,
post_content=new.post_content,
post_update_time=CURRENT_TIMESTAMP;";
$this->_exec($qry, "siss",
$this->_exec($qry, "ssiss",
$this->SITE_CONFIG['HTTP_HOST'],
$post_path,
$path_depth,
json_encode($post_metadata),
$post_content);
$this->update_post_search_data($post_path, $post_metadata['tags'] ?? []);
}
function get_settings_for_path($post_path) {
@ -240,8 +334,8 @@ class MySQLAdapter {
$post_settings = $this->_exec("
SELECT post_path, post_settings_cache
FROM posts
WHERE post_path = ?
", "s", $post_path)->fetch_assoc();
WHERE post_path = ? AND host = ?
", "ss", $post_path, $this->SITE_CONFIG['HTTP_HOST'])->fetch_assoc();
if(!isset($post_settings)) {
return [];
@ -259,8 +353,8 @@ class MySQLAdapter {
$post_metadata = $this->_exec("
SELECT post_path, post_metadata
FROM posts
WHERE post_path = ?
", "s", $post_path)->fetch_assoc();
WHERE post_path = ? AND host = ?
", "ss", $post_path, $this->SITE_CONFIG['HTTP_HOST'])->fetch_assoc();
if(isset($post_metadata['post_metadata'])) {
$post_metadata = json_decode($post_metadata['post_metadata'], true);
@ -282,16 +376,15 @@ class MySQLAdapter {
$with_subposts = false, $with_settings = true) {
$qry = "SELECT *
FROM posts WHERE post_path = ?
FROM posts
WHERE post_path = ? AND host = ?
";
$post_path = $this->_sanitize_path($post_path);
$post_data = $this->_exec($qry, "s", $post_path)->fetch_assoc();
$post_data = $this->_exec($qry, "ss", $post_path, $this->SITE_CONFIG['HTTP_HOST'])->fetch_assoc();
if(!isset($post_data)) {
$post_data = ['found' => false];
}
$post_data ??= ['found' => false];
$post_data['post_path'] = $post_path;
$post_data = $this->_normalize_post_data($post_data);
@ -319,18 +412,17 @@ class MySQLAdapter {
$qry = "SELECT post_path, post_metadata, post_update_time
FROM posts
WHERE (post_path LIKE CONCAT(?,'/%'))
WHERE
host = ?
AND (post_path LIKE CONCAT(?,'/%'))
AND post_path_depth = ?
ORDER BY post_create_time DESC
LIMIT 10";
ORDER BY post_path ASC
LIMIT 50";
$post_data = $this->_exec($qry, "si", $path, $path_depth+1)->fetch_all(MYSQLI_ASSOC);
$post_data = $this->_exec($qry, "ssi", $this->SITE_CONFIG['HTTP_HOST'],
$path, $path_depth+1)->fetch_all(MYSQLI_ASSOC);
$fn = function($data) {
return $this->_normalize_post_data($data);
};
$post_data = array_map($fn, $post_data);
$post_data = $this->_normalize_post_array($post_data);
return $post_data;
}

View file

@ -8,12 +8,57 @@ use Laminas\Feed\Writer\Feed;
class PostHandler extends MySQLAdapter {
public $data_directory;
function __construct() {
parent::__construct();
function __construct($SITE_CONFIG) {
parent::__construct($SITE_CONFIG);
$this->data_directory = 'raw';
$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',
'directory' => 'folder',
'gallery' => 'images',
'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);
@ -24,47 +69,12 @@ class PostHandler extends MySQLAdapter {
$post_data["post_basename"] = basename($post_data["post_path"]);
$post_meta = $post_data['post_metadata'];
$post_meta["title"] ??= basename($post_data["post_path"]);
if($post_meta["title"] == "") {
$post_meta["title"] = "root";
}
if(!isset($post_meta['type'])) {
$type = null;
$ext = pathinfo($post_data['post_basename'], PATHINFO_EXTENSION);
$ext_mapping = [
'' => 'directory',
'md' => 'text/markdown',
'png' => 'image',
'jpg' => 'image',
'jpeg' => 'image'
];
if(isset($ext_mapping[$ext])) {
$post_meta['type'] = $ext_mapping[$ext];
}
else {
$post_meta['type'] = '?';
}
}
if(!isset($post_meta['icon'])) {
$icon_mapping = [
'' => 'question',
'text/markdown' => 'markdown',
'directory' => 'folder',
'image' => 'image'
];
$post_data['post_metadata'] = $this->fill_in_post_meta(
$post_data['post_path'],
$post_meta);
$post_meta['icon'] = $icon_mapping[$post_meta['type']] ?? 'question';
}
$post_data['post_metadata'] = $post_meta;
$post_data["post_file_dir"] = '/' . $this->data_directory . $post_data["post_path"];
$post_data["post_file_dir"] = '/raw' . $post_data["post_path"];
return $post_data;
}
@ -83,7 +93,6 @@ class PostHandler extends MySQLAdapter {
}
function save_file($post_path, $file_path) {
$this->bump_post($post_path);
move_uploaded_file($file_path, $this->data_directory . $post_path);
}
@ -92,8 +101,15 @@ class PostHandler extends MySQLAdapter {
$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'];
if(basename($post_path) == "README.md") {
$readme_metadata = [];
if(isset($post_metadata['settings'])) {
@ -119,9 +135,6 @@ class PostHandler extends MySQLAdapter {
move_uploaded_file($file_path, $this->data_directory . $post_path);
break;
case "mddesc":
$this->save_markdown_post(chop($post_path, '.mddesc'), file_get_contents($file_path));
break;
default:
$this->save_file($post_path, $file_path);
}
@ -130,7 +143,8 @@ class PostHandler extends MySQLAdapter {
function try_get_cached_feed($path, $export_opt) {
$post_cache = $this->_exec("SELECT feed_content, feed_created_on
FROM feed_cache
WHERE search_path=? AND export_type=?", "ss", $path, $export_opt)->fetch_assoc();
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;
@ -145,12 +159,12 @@ class PostHandler extends MySQLAdapter {
$feed = @new Feed;
$feed->setTitle("DergFeed");
$feed->setLink("https://lucidragons.de" . $path);
$feed->setFeedLink("https://lucidragons.de/feeds/atom" . $path, "atom");
$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 <3");
$feed->setDescription("DergenFeed for all your " . $path . " needs");
$feed_posts = $this->_exec("SELECT
post_path,
@ -158,9 +172,9 @@ class PostHandler extends MySQLAdapter {
post_content,
post_metadata
FROM posts
WHERE (post_path = ?) OR (post_path LIKE ?)
WHERE (host = ?) AND ((post_path = ?) OR (post_path LIKE ?))
ORDER BY post_create_time DESC LIMIT 200",
"ss", $path, $path . '/%');
"sss", $this->SITE_CONFIG['HTTP_HOST'], $path, $path . '/%');
while($row = $feed_posts->fetch_array(MYSQLI_ASSOC)) {
$row = $this->_normalize_post_data($row);
@ -173,7 +187,7 @@ class PostHandler extends MySQLAdapter {
$entry = $feed->createEntry();
$entry->setTitle($row['post_path'] . '> ' . $pmeta['title']);
$entry->setLink('https://lucidragons.de' . $row['post_path']);
$entry->setLink($this->SITE_CONFIG['uri_prefix'] . $row['post_path']);
$entry->setDateModified(strtotime($row['post_update_time']));
$entry->setDateCreated(strtotime($row['post_create_time']));
@ -197,12 +211,13 @@ class PostHandler extends MySQLAdapter {
$feed = $this->construct_feed($path);
$this->_exec("INSERT INTO feed_cache
(search_path, export_type, feed_content)
(host, search_path, export_type, feed_content)
VALUES
(?, 'atom', ?),
(?, 'rss', ?)",
"ssss", $path, $feed->export('atom'),
$path, $feed->export('rss'));
(?, ?, '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);
}

View file

@ -6,18 +6,41 @@ require_once 'vendor/autoload.php';
require_once 'post_adapter.php';
require_once 'fontawesome.php';
require_once 'dergdown.php';
$adapter = new PostHandler();
use Symfony\Component\Yaml\Yaml;
$SERVER_HOST = $_SERVER['HTTP_HOST'];
if(!preg_match('/^[\w\.]+$/', $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;
$adapter = new PostHandler($SITE_CONFIG);
$loader = new \Twig\Loader\FilesystemLoader(['./templates', './user_content']);
$twig = new \Twig\Environment($loader,['debug' => true]);
$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;
function dergdown_to_html($text) {
$Parsedown = new Dergdown();
return $Parsedown->text($text);
}
function deduce_user_agent() {
$real_agent=$_SERVER['HTTP_USER_AGENT'];
@ -65,6 +88,7 @@ $twig->addRuntimeLoader(new class implements RuntimeLoaderInterface {
function render_twig($template, $args = []) {
global $twig;
global $FONT_AWESOME_ARRAY;
global $SITE_CONFIG;
$args['fa'] = $FONT_AWESOME_ARRAY;
@ -72,27 +96,33 @@ function render_twig($template, $args = []) {
$settings = $post['settings'] ?? [];
$meta = $post['post_metadata'] ?? [];
$args['banner'] ??= $settings['banners'] ?? [
["src"=> "/static/banner/0.png"],
["src" => "/static/banner/1.png"]
];
$args['banner'] ??= $settings['banners'] ?? $SITE_CONFIG['banners'];
$args['og'] = array_merge([
"title" => $meta['title'] ?? "Dergennibble",
"site_name" => $SITE_CONFIG['opengraph']['site_name'],
"title" => $meta['title'] ?? $SITE_CONFIG['opengraph']['site_name'],
"url" => $_SERVER['REQUEST_URI'],
"type" => "article",
"description" => $meta['description']
?? $settings['description']
?? "The softest spot to find dragons on"
?? $SITE_CONFIG['opengraph']['description']
], $args['og'] ?? []);
if(($meta['type'] ?? '') == 'image') {
$args['og']['image'] ??= "https://lucidragons.de" . $post['post_file_dir'];
$args['og']['image'] ??= $meta['media_file'];
$args['og']['type'] = "image";
}
$args['og']['image'] ??= 'https://lucidragons.de' . $args['banner'][0]["src"];
$args['og']['image'] ??= $args['banner'][0]["src"];
$args['banner'] = json_encode($args['banner']);
$args['site_config'] = $SITE_CONFIG;
$args['age_gate'] = (!isset($_COOKIE['AgeConfirmed'])) && isset($SITE_CONFIG['age_gate']);
$args['content_html'] ??= dergdown_to_html($post['post_content'] ?? '');
echo $twig->render($template, $args);
}
@ -136,6 +166,25 @@ function try_render_post($SURI) {
]);
break;
case 'gallery':
if(preg_match('/^(.*[^\/])((?:#.*)?)$/', $SURI, $match)) {
header('Location: ' . $match[1] . '/' . $match[2]);
die();
}
$search_query = $post['post_metadata']['search_tags'] ??
('+type:image +path:' . $post['post_path'] . '/*');
$search_result = $adapter->perform_post_search($search_query);
echo render_twig('post_types/gallery.html', [
"post" => $post,
"subposts" => $adapter->get_subposts_by_path($SURI),
"gallery_images" => $search_result['results']
]);
break;
case 'image':
echo render_twig('post_types/image.html', [
"post" => $post,
@ -193,6 +242,10 @@ function generate_website($SURI) {
} elseif($SURI == '/api/upload') {
echo $twig->render('upload.html');
} elseif($SURI == '/api/search') {
header('Content-Type: application/json');
echo json_encode($adapter->perform_post_search($_GET['search_query']));
}
} elseif(preg_match('/^\/feed(?:\/(rss|atom)(.*))?$/', $SURI, $match)) {
$feed = $adapter->get_laminas_feed($match[2] ?? '/', $match[1] ?? 'rss');
@ -202,16 +255,14 @@ function generate_website($SURI) {
header('Etag: W/"' . $SURI . '/' . strtotime($feed['feed_ts']) . '"');
echo $feed['feed'];
} elseif(preg_match('/^\s*image/', $_SERVER['HTTP_ACCEPT'])) {
header('Location: /raw' . $SURI);
exit(0);
} elseif(true) {
try_render_post($SURI);
}
}
generate_website($_SERVER['REQUEST_URI']);
$URL_PATH = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
generate_website($URL_PATH);
log_and_die($_SERVER['REQUEST_URI']);
log_and_die($URL_PATH);
?>

View file

@ -0,0 +1,12 @@
function confirmAge() {
let ageGate = document.getElementById("age_gate_block");
ageGate.style.opacity = 0;
setTimeout(() => {
ageGate.parentElement.removeChild(ageGate);
}, 1000);
document.cookie += "AgeConfirmed=true; path=/; max-age=31536000"
}

View file

@ -76,7 +76,7 @@ class BannerHandler {
this.bannerDOM.style.opacity = 0;
}
fadeIn() {
this.bannerDOM.style.opacity = 0.3;
this.bannerDOM.style.opacity = this.currentBannerData.opacity || 0.3;
}
loadNextBanner() {

View file

@ -20,6 +20,7 @@ body {
--highlight_2: #edd29e;
--text_1: #FFFFFF;
--text_border: #A0A0A080;
color: var(--text_1);
background: var(--bg_1);
@ -52,6 +53,45 @@ a:hover {
color: var(--highlight_2);
}
#age_gate_block {
position: fixed;
top: 0;
left: 0;
z-index: 1040;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(20px);
transition: opacity 0.8s linear;
}
#age_gate_block div {
background-color: var(--bg_3);
border-radius: 1em;
padding: 1em;
width: 12em;
margin:0 auto;
display: table;
position: absolute;
left: 0;
right:0;
top: 50%;
-webkit-transform:translateY(-50%);
-moz-transform:translateY(-50%);
-ms-transform:translateY(-50%);
-o-transform:translateY(-50%);
transform:translateY(-50%);
}
#age_gate_block p {
min-height: 5em;
}
#main_header {
overflow: hidden;
position: relative;
@ -110,11 +150,15 @@ a:hover {
margin-right: 2rem;
}
:target {
scroll-margin-top: 6rem;
}
#main_content_wrapper {
--content-width: min(100vw, calc(20rem + 40vw));
--content-total-margin: calc(calc(100vw - var(--content-width)) / 2);
--content-padding: max(0px, min(1rem, var(--content-total-margin)));
--content-padding: max(0.5rem, min(1rem, var(--content-total-margin)));
--content-margin: max(0px, calc(var(--content-total-margin) - 1rem));
padding: 0rem var(--content-padding) 1rem var(--content-padding);
@ -136,6 +180,9 @@ a:hover {
top: 0px;
background: var(--bg_2);
box-shadow: 0px 5px 5px 0px #00000040;
z-index: 5;
}
#post_file_titles {
@ -184,13 +231,6 @@ a:hover {
padding: 0.75rem;
}
#main_content_wrapper article h1 {
text-align: left;
padding-left: 3vmin;
margin-bottom: 0.2rem;
border-bottom: solid 1px darkgrey;
}
#main_content_wrapper article img {
display: block;

View file

@ -0,0 +1,64 @@
.gallery {
display: flex;
flex-direction: row;
flex-flow: row wrap;
justify-content: center;
list-style-type: none;
margin-top: 1rem;
padding: 0px;
}
.gallery li {
margin: 0.2rem;
margin-bottom: 1rem;
border: 1px solid var(--text_border);
float: left;
border-radius: 1rem;
box-shadow: 0px 5px 5px 0px #00000040;
background: var(--bg_2);
height: auto;
transition: 0.3s;
z-index: 1;
}
.gallery li:hover {
border: 1px solid var(--text_1);
transform: scale(1.02);
}
#main_content_wrapper .gallery img {
width: 100%;
height: 15rem;
margin: 0;
border-radius: 1rem 1rem 0 0;
object-fit: cover;
}
@media screen and (max-width: 48rem) {
.gallery li {
width: 45%;
}
#main_content_wrapper .gallery img {
height: 100%;
width: auto;
}
#main_content_wrapper .gallery figure {
height: auto;
}
}
.gallery figcaption {
padding: 0.1rem;
text-align: center;
}

203
www/static/modest.css Normal file
View file

@ -0,0 +1,203 @@
/*
modest.css, licensed under MIT license, Crafted with <3 by John Otander (@4lpine).
Taken from: https://github.com/markdowncss/modest
Modified to fit Dergsite needs
*/
@media print {
*,
*:before,
*:after {
background: transparent !important;
color: #000 !important;
box-shadow: none !important;
text-shadow: none !important;
}
a,
a:visited {
text-decoration: underline;
}
a[href]:after {
content: " (" attr(href) ")";
}
abbr[title]:after {
content: " (" attr(title) ")";
}
a[href^="#"]:after,
a[href^="javascript:"]:after {
content: "";
}
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group;
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
}
pre,
code {
font-family: Menlo, Monaco, "Courier New", monospace;
}
.modest-no-decoration {
text-decoration: none;
}
html {
font-size: 14px;
}
@media screen and (min-width: 32rem) and (max-width: 48rem) {
html {
font-size: 16px;
}
}
@media screen and (min-width: 48rem) {
html {
font-size: 17px;
}
}
article {
line-height: 1.5;
}
article p,
.modest-p {
font-size: 1rem;
margin-bottom: 1.3rem;
}
article h1,
.modest-h1,
article h2,
.modest-h2,
article h3,
.modest-h3,
article h4,
.modest-h4 {
margin: 1.5em 0 .3em;
font-weight: inherit;
line-height: 1.42;
padding-left: 1.5rem;
}
article > :first-child {
margin-top: 0.3rem !important;
}
article h1,
.modest-h1 {
margin-top: 0;
font-size: 1.998rem;
}
article h2,
.modest-h2 {
font-size: 1.427rem;
}
article h3,
.modest-h3 {
font-size: 1.299rem;
}
article h4,
.modest-h4 {
font-size: 1.1rem;
}
article h5,
.modest-h5 {
font-size: 1rem;
}
article h6,
.modest-h6 {
font-size: .88rem;
}
article small,
.modest-small {
font-size: .707rem;
}
/* https://github.com/mrmrs/fluidity */
article h1,
article h2,
article h3 {
border-bottom: 1px solid var(--text_border);
padding-bottom: .3rem;
}
blockquote {
padding-left: 0.8rem;
margin-left: 0.8rem;
margin-right: 4em;
}
blockquote {
border-left: 4px solid var(--text_border);
text-align: justify;
}
pre {
border-radius: 0.5rem;
box-shadow: 2px 5px 5px 0px #00000040;
border-left: 4px solid #206475;
background-color: var(--bg_2);
margin-bottom: 1.3rem;
margin-left: 0.8rem;
margin-right: 4em;
}
pre code {
border-radius: 0.5rem;
}
@media screen and (max-width: 32rem) {
pre, blockquote {
margin-right: 1.5em;
}
}
article ul,
article ol {
padding-left: 2em;
}

View file

@ -0,0 +1,84 @@
/*
Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull <sourdrums@gmail.com>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #002b36;
color: #839496;
}
.hljs-comment,
.hljs-quote {
color: #586e75;
}
/* Solarized Green */
.hljs-keyword,
.hljs-selector-tag,
.hljs-addition {
color: #859900;
}
/* Solarized Cyan */
.hljs-number,
.hljs-string,
.hljs-meta .hljs-meta-string,
.hljs-literal,
.hljs-doctag,
.hljs-regexp {
color: #2aa198;
}
/* Solarized Blue */
.hljs-title,
.hljs-section,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #268bd2;
}
/* Solarized Yellow */
.hljs-attribute,
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-class .hljs-title,
.hljs-type {
color: #b58900;
}
/* Solarized Orange */
.hljs-symbol,
.hljs-bullet,
.hljs-subst,
.hljs-meta,
.hljs-meta .hljs-keyword,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-link {
color: #cb4b16;
}
/* Solarized Red */
.hljs-built_in,
.hljs-deletion {
color: #dc322f;
}
.hljs-formula {
background: #073642;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

View file

@ -3,8 +3,10 @@
{% extends "root.html" %}
{% block feed_links %}
<link rel="alternate" type="application/rss+xml" title="DergSite Global Feed" href="https://lucidragons.de/feed">
<link rel="alternate" type="application/atom+xml" title="DergSite Feed for {{post.post_path}}" href="https://lucidragons.de/feed/atom{{post.post_path}}">
{{ parent() }}
<link rel="alternate" type="application/atom+xml" title="{{og.site_name}} Feed for {{post.post_path}}" href="{{site_config.uri_prefix}}/feed/atom{{post.post_path}}">
{% endblock %}
{% block second_title %}

View file

@ -8,11 +8,11 @@
{%block content_article%}
{% if subposts|length > 0 %}
<h3>Directory contents:</h3>
<table class="directory">
<caption>Directory contents:</caption>
<tr>
<th></th>
<th>Name</th>
<th class="hsmol_hide">Name</th>
<th>Title</th>
<th class="entry_update_time hsmol_hide">Modified</th>
</tr>
@ -21,11 +21,11 @@
<td>
{{ fa[subpost.post_metadata.icon] | raw }}
</td>
<td>
<td class="hsmol_hide">
<a href={{subpost.post_path}}>{{subpost.post_basename}}</a>
</td>
<td class="entry_title">
{{ subpost.post_metadata.title }}
<a href={{subpost.post_path}}>{{ subpost.post_metadata.title }}</a>
</td>
<td class="entry_update_time hsmol_hide">
{{ subpost.post_update_time }}

View file

@ -0,0 +1,59 @@
{% extends "pathed_content.html" %}
{% block extra_head %}
<link rel="stylesheet" href="/static/directorystyle.css">
<link rel="stylesheet" href="/static/gallerystyle.css">
{%endblock%}
{%block content_article%}
{% if subposts|length > 0 %}
<table class="directory">
<h3>Art Subfolders:</h3>
<tr>
<th></th>
<th class="hsmol_hide">Name</th>
<th>Title</th>
<th class="entry_update_time hsmol_hide">Modified</th>
</tr>
{% for subpost in subposts %}
<tr class="entry">
<td>
{{ fa[subpost.post_metadata.icon] | raw }}
</td>
<td class="hsmol_hide">
<a href={{subpost.post_path}}>{{subpost.post_basename}}</a>
</td>
<td class="entry_title">
<a href={{subpost.post_path}}>{{ subpost.post_metadata.title }}</a>
</td>
<td class="entry_update_time hsmol_hide">
{{ subpost.post_update_time }}
</td>
</tr>
{% endfor %}
</table>
{% endif %}
{{ content_html|raw }}
{% if gallery_images|length > 0 %}
<ul class="gallery">
{% for post in gallery_images %}
<li class="entry">
<a href={{post.post_path}}>
<figure>
<img src="{{post.post_metadata.media_file}}">
<figcaption>
{{ post.post_metadata.title }}
</figcaption>
</figure>
</a>
</li>
{% endfor %}
</ul>
{%else%}
<h4>How sad. There are no images yet... What a real shame :c </h4>
{% endif %}
{%endblock%}

View file

@ -10,18 +10,16 @@
{{ parent() }}
<meta property="og:type" content="image" />
<meta name="twitter:card" content="summary_large_image">
<meta name="robots" content="max-image-preview:large">
{%endblock %}
{%block content_article%}
<figure>
<a target="_blank" href="{{post['post_file_dir']}}">
<img id="gallery_image" src="{{post['post_file_dir']}}"></img>
<a target="_blank" href="{{post.post_metadata.media_file}}">
<img id="gallery_image" src="{{post.post_metadata.media_file}}"></img>
</a>
<figcaption>
{{ post.post_content|markdown_to_html }}
{{ content_html|raw }}
</figcaption>
</figure>
{%endblock%}

View file

@ -9,5 +9,5 @@
{%endblock %}
{%block content_article%}
{{ post['post_content']|markdown_to_html }}
{{ content_html|raw }}
{% endblock %}

View file

@ -1,18 +0,0 @@
<html>
<head>
<title>PHP Test</title>
</head>
<body>
<?php
echo '<p>Hello World</p>';
require 'src/templater.php';
$test = new TemplateFillout('test.dergplate');
$test->render();
?>
</body>
</html>

View file

@ -1,31 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title>The Dergsite - {{og.title}}</title>
<title>{{og.site_name}} - {{og.title}}</title>
<link rel="stylesheet" href="/static/dergstyle.css">
<link rel="stylesheet" href="/static/modest.css">
<link rel="stylesheet" href="/static/solarized-dark.css">
<link rel="icon" type="image/x-icon" href="/static/icon.jpeg">
<meta name="viewport" content="width=device-width,initial-scale=1">
{% block feed_links %}
<link rel="alternate" type="application/rss+xml" title="DergSite Global Feed" href="https://lucidragons.de/feed">
<link rel="alternate" type="application/rss+xml" title="{{og.site_name}} Global Feed" href="{{site_config.uri_prefix}}/feed">
{% endblock %}
{% block extra_head %}{% endblock %}
{% block opengraph_tags %}
<meta property="og:site_name" content="The Dergsite">
<meta property="og:site_name" content="{{og.site_name}}">
<meta property="og:url" content="{{og.url}}" />
<meta property="og:title" content="{{ og.title|e }}" />
<meta property="twitter:title" content="{{ og.title|e }}" />
<meta name="twitter:title" content="{{ og.title|e }}" />
<meta property="og:type" content="{{og.type}}" />
<meta property="og:description" content="{{ og.description|e }}" />
<meta property="twitter:description" content="{{ og.description|e }}" />
<meta name="twitter:description" content="{{ og.description|e }}" />
<meta property="og:image" content="{{og.image}}" />
<meta name="twitter:image" content="{{og.image}}" />
<meta name="twitter:card" content="summary_large_image">
<meta name="robots" content="max-image-preview:large">
<meta property="al:android:app_name" content="Medium" />
{% endblock %}
<script type="text/javascript">
@ -33,13 +43,28 @@
</script>
</head>
<body>
{%if age_gate %}
<div id="age_gate_block">
<div>
<p>
This website may contain content meant for an 18+ audience.
</p>
<input type="button" onclick="confirmAge()" value="I understand and am over 18.">
</div>
</div>
<script src="/static/age_gate_check.js"></script>
{% endif %}
<header id="main_header">
<img id="main_banner_img"></img>
<a id="main_banner_img_link" href="/gallery"> full picture</a>
<script src="/static/banner.js"></script>
<h1 id="big_title">{% block big_title %}The dergsite{%endblock%}</h1>
<h1 id="big_title">{% block big_title %}{{og.site_name}}{%endblock%}</h1>
{% block second_title %}{% endblock %}
<div id="title_separator"></div>