Compare commits
21 commits
0dcf36052e
...
de1f1446a3
Author | SHA1 | Date | |
---|---|---|---|
de1f1446a3 | |||
771e9a2ec8 | |||
31150b9b12 | |||
d609265862 | |||
95e3fc0b00 | |||
fc0d38e118 | |||
2fe4d20187 | |||
7c8d0191d2 | |||
02054d418d | |||
c877c8ce31 | |||
cd5b6f2942 | |||
9de4838081 | |||
9a5b9e609e | |||
bf2486caa4 | |||
22c953793c | |||
f9cfb81079 | |||
aabe0c72d5 | |||
626a4bbaf6 | |||
2e012b4fd5 | |||
c9808f90f8 | |||
c607d57221 |
52 changed files with 1806 additions and 639 deletions
|
@ -4,7 +4,7 @@ WORKDIR /app
|
|||
COPY www/composer.* .
|
||||
COPY www/vendor/* vendor/
|
||||
|
||||
FROM php:apache
|
||||
FROM php:8.2-apache
|
||||
WORKDIR /var/www/html
|
||||
|
||||
COPY --from=0 /app/ ./
|
||||
|
@ -15,6 +15,7 @@ RUN a2enmod headers
|
|||
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
|
||||
|
||||
RUN mkdir raw
|
||||
RUN chmod a+wr raw
|
||||
|
||||
COPY www/ .
|
||||
RUN chmod -R a+rw $(ls -I vendor)
|
||||
RUN chmod -R a+rw $(ls -I vendor)
|
|
@ -37,7 +37,10 @@ CREATE TABLE posts (
|
|||
INDEX(host, post_created_at),
|
||||
INDEX(host, post_updated_at),
|
||||
|
||||
FULLTEXT(post_tags)
|
||||
FULLTEXT(post_path),
|
||||
FULLTEXT(post_tags),
|
||||
FULLTEXT(post_title),
|
||||
FULLTEXT(post_brief)
|
||||
);
|
||||
|
||||
CREATE TABLE post_markdown (
|
||||
|
|
|
@ -7,5 +7,11 @@
|
|||
"path": "../dragon_fire_content"
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
"settings": {
|
||||
"conventionalCommits.scopes": [
|
||||
"search",
|
||||
"templates",
|
||||
"css"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -3,8 +3,8 @@ 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 /
|
||||
|
|
181
www/composer.lock
generated
181
www/composer.lock
generated
|
@ -184,33 +184,33 @@
|
|||
},
|
||||
{
|
||||
"name": "laminas/laminas-escaper",
|
||||
"version": "2.13.0",
|
||||
"version": "2.14.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laminas/laminas-escaper.git",
|
||||
"reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba"
|
||||
"reference": "0f7cb975f4443cf22f33408925c231225cfba8cb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/af459883f4018d0f8a0c69c7a209daef3bf973ba",
|
||||
"reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba",
|
||||
"url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/0f7cb975f4443cf22f33408925c231225cfba8cb",
|
||||
"reference": "0f7cb975f4443cf22f33408925c231225cfba8cb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0"
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
|
||||
},
|
||||
"conflict": {
|
||||
"zendframework/zend-escaper": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"infection/infection": "^0.27.0",
|
||||
"laminas/laminas-coding-standard": "~2.5.0",
|
||||
"infection/infection": "^0.27.9",
|
||||
"laminas/laminas-coding-standard": "~3.0.0",
|
||||
"maglnet/composer-require-checker": "^3.8.0",
|
||||
"phpunit/phpunit": "^9.6.7",
|
||||
"psalm/plugin-phpunit": "^0.18.4",
|
||||
"vimeo/psalm": "^5.9"
|
||||
"phpunit/phpunit": "^9.6.16",
|
||||
"psalm/plugin-phpunit": "^0.19.0",
|
||||
"vimeo/psalm": "^5.21.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -242,7 +242,7 @@
|
|||
"type": "community_bridge"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-10T08:35:13+00:00"
|
||||
"time": "2024-10-24T10:12:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laminas/laminas-feed",
|
||||
|
@ -519,16 +519,16 @@
|
|||
},
|
||||
{
|
||||
"name": "league/commonmark",
|
||||
"version": "2.5.1",
|
||||
"version": "2.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/commonmark.git",
|
||||
"reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c"
|
||||
"reference": "b650144166dfa7703e62a22e493b853b58d874b0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/ac815920de0eff6de947eac0a6a94e5ed0fb147c",
|
||||
"reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/b650144166dfa7703e62a22e493b853b58d874b0",
|
||||
"reference": "b650144166dfa7703e62a22e493b853b58d874b0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -541,8 +541,8 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"cebe/markdown": "^1.0",
|
||||
"commonmark/cmark": "0.31.0",
|
||||
"commonmark/commonmark.js": "0.31.0",
|
||||
"commonmark/cmark": "0.31.1",
|
||||
"commonmark/commonmark.js": "0.31.1",
|
||||
"composer/package-versions-deprecated": "^1.8",
|
||||
"embed/embed": "^4.4",
|
||||
"erusev/parsedown": "^1.0",
|
||||
|
@ -621,7 +621,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-24T12:52:09+00:00"
|
||||
"time": "2024-08-16T11:46:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/config",
|
||||
|
@ -707,24 +707,24 @@
|
|||
},
|
||||
{
|
||||
"name": "nette/schema",
|
||||
"version": "v1.3.0",
|
||||
"version": "v1.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/schema.git",
|
||||
"reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188"
|
||||
"reference": "da801d52f0354f70a638673c4a0f04e16529431d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/schema/zipball/a6d3a6d1f545f01ef38e60f375d1cf1f4de98188",
|
||||
"reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188",
|
||||
"url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d",
|
||||
"reference": "da801d52f0354f70a638673c4a0f04e16529431d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nette/utils": "^4.0",
|
||||
"php": "8.1 - 8.3"
|
||||
"php": "8.1 - 8.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.4",
|
||||
"nette/tester": "^2.5.2",
|
||||
"phpstan/phpstan-nette": "^1.0",
|
||||
"tracy/tracy": "^2.8"
|
||||
},
|
||||
|
@ -763,9 +763,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/schema/issues",
|
||||
"source": "https://github.com/nette/schema/tree/v1.3.0"
|
||||
"source": "https://github.com/nette/schema/tree/v1.3.2"
|
||||
},
|
||||
"time": "2023-12-11T11:54:22+00:00"
|
||||
"time": "2024-10-06T23:10:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
|
@ -983,21 +983,21 @@
|
|||
},
|
||||
{
|
||||
"name": "spatie/yaml-front-matter",
|
||||
"version": "2.0.9",
|
||||
"version": "2.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/yaml-front-matter.git",
|
||||
"reference": "cbe67e1cdd0a29a96d74ccab9400fe663e078392"
|
||||
"reference": "5d0009289dd19a23e5f6cbb72c959a9fc1881e32"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/cbe67e1cdd0a29a96d74ccab9400fe663e078392",
|
||||
"reference": "cbe67e1cdd0a29a96d74ccab9400fe663e078392",
|
||||
"url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/5d0009289dd19a23e5f6cbb72c959a9fc1881e32",
|
||||
"reference": "5d0009289dd19a23e5f6cbb72c959a9fc1881e32",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0|^8.0",
|
||||
"symfony/yaml": "^3.0|^4.0|^5.0|^6.0|^7.0"
|
||||
"php": "^8.0",
|
||||
"symfony/yaml": "^6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.0"
|
||||
|
@ -1029,7 +1029,7 @@
|
|||
"yaml"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/spatie/yaml-front-matter/tree/2.0.9"
|
||||
"source": "https://github.com/spatie/yaml-front-matter/tree/2.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1041,20 +1041,20 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-06-13T10:20:51+00:00"
|
||||
"time": "2024-12-02T08:40:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v3.5.0",
|
||||
"version": "v3.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
|
||||
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
|
||||
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
|
||||
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -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.5.0"
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1108,24 +1108,24 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-18T09:32:20+00:00"
|
||||
"time": "2024-09-25T14:20:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.30.0",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "0424dff1c58f028c451efff2045f5d92410bd540"
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
|
||||
"reference": "0424dff1c58f028c451efff2045f5d92410bd540",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
|
@ -1171,7 +1171,7 @@
|
|||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1187,24 +1187,24 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-05-31T15:07:36+00:00"
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.30.0",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
|
||||
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
|
||||
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
||||
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-mbstring": "*"
|
||||
|
@ -1251,7 +1251,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1267,24 +1267,24 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-06-19T12:30:46+00:00"
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.30.0",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
|
||||
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433",
|
||||
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
|
||||
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
@ -1331,7 +1331,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1347,24 +1347,24 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-05-31T15:07:36+00:00"
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
"version": "v1.30.0",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||
"reference": "3fb075789fb91f9ad9af537c4012d523085bd5af"
|
||||
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af",
|
||||
"reference": "3fb075789fb91f9ad9af537c4012d523085bd5af",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
|
||||
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
@ -1407,7 +1407,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0"
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1423,24 +1423,25 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-06-19T12:30:46+00:00"
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v7.1.1",
|
||||
"version": "v7.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "fa34c77015aa6720469db7003567b9f772492bf2"
|
||||
"reference": "099581e99f557e9f16b43c5916c26380b54abb22"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/fa34c77015aa6720469db7003567b9f772492bf2",
|
||||
"reference": "fa34c77015aa6720469db7003567b9f772492bf2",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/099581e99f557e9f16b43c5916c26380b54abb22",
|
||||
"reference": "099581e99f557e9f16b43c5916c26380b54abb22",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3.0",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"conflict": {
|
||||
|
@ -1478,7 +1479,7 @@
|
|||
"description": "Loads and dumps YAML files",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/yaml/tree/v7.1.1"
|
||||
"source": "https://github.com/symfony/yaml/tree/v7.2.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1494,26 +1495,26 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-05-31T14:57:53+00:00"
|
||||
"time": "2024-10-23T06:56:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/markdown-extra",
|
||||
"version": "v3.11.0",
|
||||
"version": "v3.16.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/markdown-extra.git",
|
||||
"reference": "504557d60d80478260ebd2221a2b3332a480865d"
|
||||
"reference": "25f23c02936f8c7157a8413154c06a462c9c20d3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/504557d60d80478260ebd2221a2b3332a480865d",
|
||||
"reference": "504557d60d80478260ebd2221a2b3332a480865d",
|
||||
"url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/25f23c02936f8c7157a8413154c06a462c9c20d3",
|
||||
"reference": "25f23c02936f8c7157a8413154c06a462c9c20d3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"php": ">=8.0.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"twig/twig": "^3.0"
|
||||
"twig/twig": "^3.13|^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"erusev/parsedown": "^1.7",
|
||||
|
@ -1554,7 +1555,7 @@
|
|||
"twig"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/twigphp/markdown-extra/tree/v3.11.0"
|
||||
"source": "https://github.com/twigphp/markdown-extra/tree/v3.16.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1566,31 +1567,31 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-08-07T17:34:09+00:00"
|
||||
"time": "2024-09-03T20:17:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.11.0",
|
||||
"version": "v3.16.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d"
|
||||
"reference": "475ad2dc97d65d8631393e721e7e44fb544f0561"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/e80fb8ebba85c7341a97a9ebf825d7fd4b77708d",
|
||||
"reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/475ad2dc97d65d8631393e721e7e44fb544f0561",
|
||||
"reference": "475ad2dc97d65d8631393e721e7e44fb544f0561",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"php": ">=8.0.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-ctype": "^1.8",
|
||||
"symfony/polyfill-mbstring": "^1.3",
|
||||
"symfony/polyfill-php80": "^1.22",
|
||||
"symfony/polyfill-php81": "^1.29"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"psr/container": "^1.0|^2.0",
|
||||
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
|
||||
},
|
||||
|
@ -1634,7 +1635,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.11.0"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.16.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1646,7 +1647,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-08-08T16:15:16+00:00"
|
||||
"time": "2024-11-29T08:27:05+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<?php
|
||||
|
||||
interface AnalyticsInterface {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<?php
|
||||
|
||||
function sanitize_post_path($post_path) {
|
||||
|
@ -81,6 +80,28 @@ interface PostdataInterface {
|
|||
$order_by = 'path');
|
||||
|
||||
public function get_post_markdown($id);
|
||||
|
||||
|
||||
// Returns an array of PostData information
|
||||
// based on various search parameters.
|
||||
//
|
||||
// search_options can either be:
|
||||
// - An Array
|
||||
// - Or a String
|
||||
//
|
||||
// In case of it being an Array, it may include
|
||||
// the keys:
|
||||
// - "query" (which will be processed similar
|
||||
// to how $search_options will be processed),
|
||||
// - "text", which is searched for in text fields
|
||||
// (title, brief, fulltext),
|
||||
// - "tags", which is matched IN BINARY MODE against
|
||||
// the post tags
|
||||
// - "path", which is used as filter
|
||||
// - "order_by": determines which column to order by. NULL
|
||||
// will order by FULLTEXT match scores
|
||||
// - "limit" and "offset", self-explanatory
|
||||
public function search_posts($search_options);
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,33 +1,16 @@
|
|||
|
||||
<?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;
|
||||
}
|
||||
require_once 'mysql_taglist_handling.php';
|
||||
|
||||
class MySQLHandler
|
||||
implements PostdataInterface {
|
||||
|
||||
CONST SQL_READ_COLUMNS = [
|
||||
'id', 'path', 'created_at', 'updated_at',
|
||||
'title', 'view_count', 'brief'];
|
||||
'title', 'view_count', 'brief', 'search_score'];
|
||||
|
||||
CONST SQL_WRITE_COLUMNS = ['path', 'title', 'brief'];
|
||||
|
||||
|
@ -129,7 +112,7 @@ class MySQLHandler
|
|||
$post_path,
|
||||
substr_count($post_path, "/"),
|
||||
$data['title'],
|
||||
taglist_to_sql_string($post_tags),
|
||||
TagList\create_db_str($post_tags),
|
||||
$data['brief'] ?? null
|
||||
];
|
||||
|
||||
|
@ -293,8 +276,8 @@ class MySQLHandler
|
|||
'path DESC' => true,
|
||||
'created_at' => true,
|
||||
'created_at DESC' => true,
|
||||
'modified_at' => true,
|
||||
'modified_at DESC' => true
|
||||
'updated_at' => true,
|
||||
'updated_at DESC' => true
|
||||
];
|
||||
|
||||
if(!isset($allowed_ordering[$order_by])) {
|
||||
|
@ -343,6 +326,167 @@ class MySQLHandler
|
|||
|
||||
return $data['post_markdown'];
|
||||
}
|
||||
|
||||
public function parse_search_query_string($text) {
|
||||
$element_array = explode(' ', $text);
|
||||
|
||||
$return_text = '';
|
||||
$return_tags = [];
|
||||
$return_options = [];
|
||||
|
||||
foreach($element_array as $element) {
|
||||
if(strlen($element) == 0)
|
||||
continue;
|
||||
|
||||
if(preg_match('/^(\w+):(.+)$/', $element, $match)) {
|
||||
if($match[1] == 'tags') {
|
||||
$return_tags = array_merge($return_tags, explode(',', $match[2]));
|
||||
} else {
|
||||
$return_options[$match[1]] = $match[2];
|
||||
}
|
||||
} else {
|
||||
$return_text .= $element . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'text' => $return_text,
|
||||
'tags' => $return_tags,
|
||||
'options' => $return_options
|
||||
];
|
||||
}
|
||||
|
||||
public function search_posts($options) {
|
||||
// Function to perform an arbitrary search across
|
||||
// the database.
|
||||
//
|
||||
// "options" input is a Hash with the following
|
||||
// possible keys:
|
||||
// - query: This text will be interpreted
|
||||
// as a combination of text to search as well as
|
||||
// tags, order-by requirements, etc.
|
||||
// - text: This text will be used as unmodified
|
||||
// input to the FULLTEXT matching
|
||||
// - tags: This may be either a list or a string of tags
|
||||
// to use for searching
|
||||
// - path: Which path to search within
|
||||
// - order_by: What column (if any) to search by
|
||||
// - limit: Number of results to return, at most
|
||||
// - offset: Number of results to skip before returning
|
||||
|
||||
if(gettype($options) == 'string') {
|
||||
$options = [
|
||||
'query' => $options
|
||||
];
|
||||
}
|
||||
|
||||
// Arrays to construct the query selection later
|
||||
$qry_selects = ['posts.*'];
|
||||
$qry_select_data = [];
|
||||
$qry_select_types = '';
|
||||
|
||||
$qry_wheres = [];
|
||||
$qry_where_data = [];
|
||||
$qry_where_types = '';
|
||||
|
||||
$options['text'] ??= '';
|
||||
|
||||
if(gettype($options['tags'] ?? null) == 'string') {
|
||||
$options['tags'] = TagList\_str_to_raw_taglist($options['tags']);
|
||||
} else {
|
||||
$options['tags'] ??= [];
|
||||
}
|
||||
|
||||
$options['limit'] = min($options['limit'] ?? 100, 100);
|
||||
|
||||
// This code will take a generic user-input string, and will process it
|
||||
// to see if there are any special options to consider.
|
||||
//
|
||||
// These options will always be overridden by the original "options"
|
||||
// array. Text and Tags will be merged. For the limit, the minimum will
|
||||
// be chosen.
|
||||
if(isset($options['query'])) {
|
||||
$search_options = $this->parse_search_query_string($options['query']);
|
||||
|
||||
if(strlen($search_options['text']) > 0) {
|
||||
$options['text'] ??= '';
|
||||
$options['text'] .= ' ' . $search_options['text'];
|
||||
}
|
||||
|
||||
$options['tags'] = array_merge($options['tags'], $search_options['tags']);
|
||||
|
||||
if(isset($search_options['limit'])) {
|
||||
$options['limit'] = min($options['limit'], intval($search_options['limit']));
|
||||
}
|
||||
if(isset($search_options['offset'])) {
|
||||
$options['offset'] = intval($options['offset']);
|
||||
}
|
||||
|
||||
$options = array_merge($options, $search_options['options']);
|
||||
}
|
||||
|
||||
// If we have any tags, construct a tag-matching query
|
||||
if(count($options['tags']) > 0) {
|
||||
$tag_search_string = TagList\create_db_search($options['tags'])['parameter_string'];
|
||||
|
||||
$qry_wheres []= "MATCH(post_tags) AGAINST (? IN BOOLEAN MODE)";
|
||||
$qry_where_data []= $tag_search_string;
|
||||
$qry_where_types .= 's';
|
||||
}
|
||||
|
||||
// If we have any text query strings, we get to construct a rather fun, complex
|
||||
// array of MATCH() AGAINST() text queries.
|
||||
if(strlen($options['text']) > 0) {
|
||||
$text_search_scores = [0];
|
||||
$text_search_wheres = [];
|
||||
foreach([['title', 6], ['brief', 4], ['markdown', 1]] as $arg) {
|
||||
$text_search_scores []= "((MATCH(post_" . $arg[0] . ") AGAINST (?)) * " . $arg[1] . ')';
|
||||
$qry_select_data []= $options['text'];
|
||||
$qry_select_types .= 's';
|
||||
|
||||
$text_search_wheres []= "(MATCH(post_" . $arg[0] . ") AGAINST (?))";
|
||||
$qry_where_data []= $options['text'];
|
||||
$qry_where_types .= 's';
|
||||
}
|
||||
|
||||
$qry_selects []= '(' . implode('+', $text_search_scores) . ') AS post_search_score';
|
||||
$qry_wheres []= '(' . implode(' OR ', $text_search_wheres) . ')';
|
||||
} else {
|
||||
$qry_selects []= '0 AS post_search_score';
|
||||
}
|
||||
|
||||
if(isset($options['path']) && strlen($options['path']) > 0) {
|
||||
$qry_wheres []= "post_path LIKE ?";
|
||||
$qry_where_data []= $options['path'] . '%';
|
||||
$qry_where_types .= 's';
|
||||
}
|
||||
|
||||
if(count($qry_wheres) == 0) {
|
||||
throw new Exception("No search filtering options supplied!");
|
||||
}
|
||||
|
||||
$options['offset'] ??= 0;
|
||||
|
||||
$qry =
|
||||
"SELECT " . implode(', ', $qry_selects) . "
|
||||
FROM posts
|
||||
LEFT JOIN post_markdown ON posts.post_id = post_markdown.post_id
|
||||
WHERE " . implode(' and ', $qry_wheres) . "
|
||||
ORDER BY post_search_score DESC
|
||||
LIMIT " . $options['limit'] . "
|
||||
OFFSET " . $options['offset'];
|
||||
|
||||
$search_results = $this->_exec($qry, $qry_select_types . $qry_where_types,
|
||||
...array_merge($qry_select_data, $qry_where_data))->fetch_all(MYSQLI_ASSOC);
|
||||
|
||||
$outdata = [];
|
||||
foreach($search_results AS $post_element) {
|
||||
$outdata []=
|
||||
$this->process_postdata($post_element);
|
||||
}
|
||||
|
||||
return $outdata;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
81
www/src/db_handler/mysql_taglist_handling.php
Normal file
81
www/src/db_handler/mysql_taglist_handling.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
namespace TagList;
|
||||
|
||||
function escape_entry($tag) {
|
||||
return preg_replace_callback('/[\WZ]/', function($match) {
|
||||
return "Z" . ord($match[0]);
|
||||
}, strtolower($tag));
|
||||
}
|
||||
|
||||
function escape_search_entry($tag) {
|
||||
preg_match("/^([\+\-]?)([^\*]*)(\*?)$/", $tag, $matches);
|
||||
|
||||
if(!isset($matches[1])) {
|
||||
echo "Problem with tag!";
|
||||
var_dump($tag);
|
||||
}
|
||||
|
||||
return $matches[1] . escape_entry($matches[2]) . $matches[3];
|
||||
}
|
||||
|
||||
function _str_to_raw_taglist($taglist) {
|
||||
$split_list = explode(' ', $taglist);
|
||||
|
||||
$split_list = array_filter($split_list, function($entry) {
|
||||
if(strlen($entry) == 0)
|
||||
return false;
|
||||
if(preg_match('/^\s*$/', $entry))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return $split_list;
|
||||
}
|
||||
|
||||
function create_db_str($taglist) {
|
||||
if(gettype($taglist) == 'string') {
|
||||
$taglist = _str_to_raw_taglist($taglist);
|
||||
}
|
||||
|
||||
$taglist = array_unique($taglist);
|
||||
$taglist = array_map(function($val) {
|
||||
return escape_entry($val);
|
||||
}, $taglist);
|
||||
|
||||
asort($taglist);
|
||||
$taglist = join(' ', $taglist);
|
||||
|
||||
return $taglist;
|
||||
}
|
||||
|
||||
function create_db_search($taglist) {
|
||||
if(gettype($taglist) == 'string') {
|
||||
$taglist = _str_to_raw_taglist($taglist);
|
||||
}
|
||||
|
||||
$search_params = [];
|
||||
$search_modifiers = [];
|
||||
|
||||
foreach($taglist as $tag) {
|
||||
if(preg_match('/^(order|limit):(.*)$/i', $tag, $match)) {
|
||||
$search_modifiers[$match[1]] = $match[2];
|
||||
} else {
|
||||
array_push($search_params, $tag);
|
||||
}
|
||||
}
|
||||
|
||||
$search_params = array_map(function($val) {
|
||||
return escape_search_entry($val);
|
||||
}, $search_params);
|
||||
|
||||
asort($search_params);
|
||||
$search_params = join(' ', $search_params);
|
||||
|
||||
return [
|
||||
'modifiers' => $search_modifiers,
|
||||
'parameter_string' => $search_params
|
||||
];
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<?php
|
||||
|
||||
class Post implements ArrayAccess {
|
||||
|
@ -7,8 +6,6 @@ class Post implements ArrayAccess {
|
|||
private $content_html;
|
||||
private $content_markdown;
|
||||
|
||||
private $site_defaults;
|
||||
|
||||
public $data;
|
||||
|
||||
public $html_data;
|
||||
|
@ -30,7 +27,7 @@ class Post implements ArrayAccess {
|
|||
return $post_data;
|
||||
}
|
||||
|
||||
public static function _deduce_type($path) {
|
||||
public static function deduce_type($path) {
|
||||
$ext = pathinfo($path, PATHINFO_EXTENSION);
|
||||
|
||||
if(preg_match("/\.(\w+)\.md$/", $path, $ext_match)) {
|
||||
|
@ -48,10 +45,19 @@ class Post implements ArrayAccess {
|
|||
return $ext_mapping[$ext] ?? '?';
|
||||
}
|
||||
|
||||
public static function _deduce_icon($type) {
|
||||
public static function deduce_media_url($path) {
|
||||
if(preg_match("/^(.*\.\w+)\.md$/", $path, $ext_match)) {
|
||||
return $ext_match[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function deduce_icon($type) {
|
||||
$icon_mapping = [
|
||||
'' => 'question',
|
||||
'text/markdown' => 'markdown',
|
||||
'markdown' => 'markdown',
|
||||
'blog' => 'markdown',
|
||||
'blog_list' => 'rectangle-list',
|
||||
'directory' => 'folder',
|
||||
|
@ -62,19 +68,27 @@ class Post implements ArrayAccess {
|
|||
return $icon_mapping[$type] ?? 'unknown';
|
||||
}
|
||||
|
||||
public static function deduce_template($type) {
|
||||
$template_mapping = [
|
||||
'directory' => 'directory',
|
||||
'gallery' => 'gallery',
|
||||
'image' => 'image'
|
||||
];
|
||||
|
||||
return $template_mapping[$type] ?? 'vanilla';
|
||||
}
|
||||
|
||||
function __construct($post_handler, $post_data, $site_defaults) {
|
||||
$this->handler = $post_handler;
|
||||
|
||||
$this->content_html = null;
|
||||
$this->content_markdown = null;
|
||||
|
||||
$this->site_defaults = $site_defaults;
|
||||
|
||||
if(!isset($post_data) or !isset($post_data['id'])) {
|
||||
$post_data = $this->_generate_404($post_data);
|
||||
}
|
||||
|
||||
$data = $post_data;
|
||||
$data = array_merge($site_defaults, $post_data);
|
||||
|
||||
if($data['path'] == '') {
|
||||
$data['path'] = '/';
|
||||
|
@ -87,23 +101,33 @@ class Post implements ArrayAccess {
|
|||
$data['url'] ??= 'http://' . $post_data['host'] . $post_data['path'];
|
||||
|
||||
$data['basename'] ??= basename($data['path']);
|
||||
|
||||
$data['title'] ??= basename($data['path']);
|
||||
|
||||
$data['tags'] ??= [];
|
||||
|
||||
$data['type'] ??= self::_deduce_type($post_data['path']);
|
||||
$data['type'] ??= self::deduce_type($post_data['path']);
|
||||
|
||||
$data['icon'] ??= self::_deduce_icon($data['type']);
|
||||
$data['icon'] ??= self::deduce_icon($data['type']);
|
||||
$data['template'] ??= self::deduce_template($data['type']);
|
||||
|
||||
if(isset($sql_meta['media_url'])) {
|
||||
$data['thumb_url'] ??= $data['media_url'];
|
||||
}
|
||||
$data['media_url'] ??= self::deduce_media_url($data['path']);
|
||||
$data['media_preview_url'] ??= $data['media_url'];
|
||||
|
||||
$data['preview_image'] ??= $data['banners'][0]['src'] ?? null;
|
||||
// TODO: Try to check for thumb image automatically here
|
||||
$data['preview_image'] ??= $data['media_preview_url'] ??
|
||||
$data['banners'][0]['src'] ?? null;
|
||||
|
||||
$data['brief'] ??= $data['title'];
|
||||
|
||||
if($data['type'] == 'gallery') {
|
||||
$data['search'] ??= [
|
||||
'path' => $data['path'],
|
||||
'tags' => [
|
||||
'+type:image'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
|
@ -128,11 +152,7 @@ class Post implements ArrayAccess {
|
|||
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;
|
||||
return null;
|
||||
}
|
||||
|
||||
public function offsetGet($offset) : mixed {
|
||||
|
@ -142,9 +162,6 @@ class Post implements ArrayAccess {
|
|||
if(isset($this->data[$offset])) {
|
||||
return true;
|
||||
}
|
||||
if(isset($this->site_defaults[$offset])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !is_null($this->offsetGet($offset));
|
||||
}
|
||||
|
@ -189,7 +206,14 @@ class Post implements ArrayAccess {
|
|||
$out_data['html'] = $this->get_html();
|
||||
}
|
||||
if(isset($options['children'])) {
|
||||
die();
|
||||
$children = $this->get_child_posts();
|
||||
$child_arrays = [];
|
||||
|
||||
foreach($children AS $child) {
|
||||
array_push($child_arrays, $child->to_array());
|
||||
}
|
||||
|
||||
$out_data['children'] = $child_arrays;
|
||||
}
|
||||
|
||||
return $out_data;
|
||||
|
|
|
@ -56,6 +56,17 @@ class PostHandler {
|
|||
|
||||
return $out_list;
|
||||
}
|
||||
|
||||
public function search_posts($search_query) {
|
||||
$search_results = $this->db->search_posts($search_query);
|
||||
|
||||
$out_list = [];
|
||||
foreach($search_results as $search_result) {
|
||||
array_push($out_list, new Post($this, $search_result, $this->site_defaults));
|
||||
}
|
||||
|
||||
return $out_list;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -61,6 +61,8 @@ $db_connection->execute_query("DELETE FROM posts;");
|
|||
$sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST);
|
||||
$adapter = new PostHandler($sql_adapter);
|
||||
|
||||
$adapter->site_defaults = [];
|
||||
|
||||
$sql_adapter->debugging = true;
|
||||
|
||||
function test_accounce($title) {
|
||||
|
@ -127,15 +129,23 @@ test_accounce("Setting post markdown...");
|
|||
$sql_adapter->set_postdata([
|
||||
'path' => '/testing/markdowntest',
|
||||
'markdown' => 'Inline markdown test should work...',
|
||||
'title' => "A Markdown Test"
|
||||
'title' => "A Markdown Test",
|
||||
'brief' => "The dragons explore markdown, sort of properly... Maybe.",
|
||||
'tags' => ['one', 'two', 'three', 'sexee']
|
||||
]);
|
||||
$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!
|
||||
|
||||
This is one hell of a cute test!
|
||||
|
||||
> Just checking in...
|
||||
|
||||
{{
|
||||
template: fragments/blog/card.html
|
||||
}}
|
||||
'
|
||||
);
|
||||
var_dump($sql_adapter->get_post_markdown($post['id']));
|
||||
|
|
|
@ -1,24 +1,97 @@
|
|||
|
||||
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Highlight\Highlighter;
|
||||
|
||||
class Dergdown extends ParsedownExtra
|
||||
{
|
||||
protected $highlighter;
|
||||
|
||||
protected $dergInsertRenderer;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->highlighter = new Highlighter();
|
||||
$this->highlighter = null;
|
||||
$this->BlockTypes['{'] []= 'DergInsert';
|
||||
|
||||
$this->dergInsertRenderer = null;
|
||||
}
|
||||
|
||||
public function setDergRenderer($dergRenderer) {
|
||||
$this->dergInsertRenderer = $dergRenderer;
|
||||
}
|
||||
|
||||
protected function blockDergInsert($Line, $currentBlock) {
|
||||
if (preg_match('/^{{\s?(.*)$/', $Line['body'], $match)) {
|
||||
return array(
|
||||
'text' => $match[1] ?? ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function blockDergInsertContinue($Line, $Block) {
|
||||
if(isset($Block['complete'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(preg_match('/(.*)}}/', $Line['body'], $match)) {
|
||||
$Block['text'] .= "\n" . $match[1];
|
||||
$Block['complete'] = true;
|
||||
return $Block;
|
||||
}
|
||||
|
||||
$Block['text'] .= "\n" . $Line['body'];
|
||||
|
||||
return $Block;
|
||||
}
|
||||
|
||||
protected function blockDergInsertComplete($Block) {
|
||||
try {
|
||||
$parsed_data = Yaml::parse($Block['text']);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
return array(
|
||||
'markup' => '
|
||||
<div class="derg-insert-error">
|
||||
<h3> Error in a dergen template! </h3>
|
||||
YAML could not be parsed properly: <br>
|
||||
<code>
|
||||
' . $ex->getMessage() . '</code> </div>'
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if(!isset($this->dergInsertRenderer)) {
|
||||
throw new Exception("No Dergen Renderer was set!");
|
||||
}
|
||||
|
||||
$render_output = $this->dergInsertRenderer->dergRender($parsed_data);
|
||||
} catch (Exception $ex) {
|
||||
return array(
|
||||
'markup' => '
|
||||
<div class="derg-insert-error">
|
||||
<h3> Error in a dergen template! </h3>
|
||||
Rendering engine threw an error: <br>
|
||||
<code>
|
||||
' . $ex->getMessage() . '</code> </div>'
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'markup' => $render_output
|
||||
);
|
||||
}
|
||||
|
||||
protected function blockFencedCodeComplete($block)
|
||||
{
|
||||
{
|
||||
if (! isset($block['element']['text']['attributes'])) {
|
||||
return $block;
|
||||
}
|
||||
|
||||
if(!isset($this->highlighter)) {
|
||||
$this->highlighter = new Highlighter();
|
||||
}
|
||||
|
||||
$code = $block['element']['text']['text'];
|
||||
$languageClass = $block['element']['text']['attributes']['class'];
|
||||
$language = explode('-', $languageClass);
|
||||
|
|
|
@ -4,19 +4,21 @@ $data_time_start = microtime(true);
|
|||
|
||||
require_once '../vendor/autoload.php';
|
||||
|
||||
require_once 'setup_site_config.php';
|
||||
require_once 'setup_db.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';
|
||||
require_once 'setup/twig.php';
|
||||
|
||||
$REQUEST_URI = parse_url($_SERVER['REQUEST_URI']);
|
||||
$REQUEST_PATH = $REQUEST_URI['path'];
|
||||
|
||||
parse_str($REQUEST_URI['query'] ?? '', $REQUEST_QUERY);
|
||||
|
||||
require_once 'setup/permissions.php';
|
||||
|
||||
if(preg_match('/^\/api/', $REQUEST_PATH)) {
|
||||
require_once 'serve/api.php';
|
||||
}
|
||||
|
|
|
@ -17,10 +17,15 @@ if(isset($REQUEST_QUERY['page'])) {
|
|||
$ajax_args['page'] = $adapter->get_post($REQUEST_QUERY['page']);
|
||||
}
|
||||
|
||||
if(isset($REQUEST_QUERY['search'])) {
|
||||
$ajax_args['search_results'] = $post->handler->search_posts($REQUEST_QUERY['search']);
|
||||
}
|
||||
|
||||
|
||||
$ajax_args['fa'] = $FONT_AWESOME_ARRAY;
|
||||
$ajax_args['page'] ??= $SITE_CONFIG['site_defaults'];
|
||||
$ajax_args['post'] ??= $ajax_args['page'];
|
||||
|
||||
echo $twig->render('/ajax/' . $AJAX_REQUEST_TEMPLATE, $ajax_args);
|
||||
echo $twig->render($AJAX_REQUEST_TEMPLATE, $ajax_args);
|
||||
|
||||
?>
|
94
www/src/serve/api.php
Normal file
94
www/src/serve/api.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
use Spatie\YamlFrontMatter\YamlFrontMatter;
|
||||
|
||||
preg_match('/^\/api\/([^\/]*)(.*)/', $REQUEST_PATH, $match);
|
||||
$API_FUNCTION = $match[1];
|
||||
|
||||
switch($API_FUNCTION) {
|
||||
case 'posts':
|
||||
$post = $adapter->get_post($match[2]);
|
||||
|
||||
if(!isset($post)) {
|
||||
echo json_encode([
|
||||
'found' => false,
|
||||
'status' => 404
|
||||
]);
|
||||
} else {
|
||||
echo $post->to_json($REQUEST_QUERY);
|
||||
}
|
||||
break;
|
||||
case 'db_post':
|
||||
echo json_encode($sql_adapter->get_postdata($match[2]));
|
||||
break;
|
||||
|
||||
case 'upload':
|
||||
if(!access_can_upload()) {
|
||||
http_response_code(401);
|
||||
echo json_encode([
|
||||
'status' => '401 Unauthorized'
|
||||
]);
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
if( !isset($_POST['path']) or
|
||||
!isset($_FILES['file'])) {
|
||||
echo json_encode([
|
||||
'status' => 'Missing paramters (must POST path and file!)'
|
||||
]);
|
||||
die();
|
||||
}
|
||||
|
||||
$file_path = sanitize_post_path($_POST['path']);
|
||||
|
||||
$physical_file_path = $SITE_CONFIG['upload']['file_path'] . $file_path;
|
||||
|
||||
$file_dir = dirname($physical_file_path);
|
||||
|
||||
if(!is_dir($file_dir)) {
|
||||
mkdir(dirname($physical_file_path), recursive: true);
|
||||
}
|
||||
|
||||
move_uploaded_file($_FILES['file']['tmp_name'], $physical_file_path);
|
||||
|
||||
$file_ext = pathinfo($file_path, PATHINFO_EXTENSION);
|
||||
|
||||
if($file_ext == 'md') {
|
||||
|
||||
$is_directory = false;
|
||||
$original_file_path = $file_path;
|
||||
|
||||
if(basename($file_path) == 'README.md') {
|
||||
$is_directory = true;
|
||||
$file_path = dirname($file_path);
|
||||
}
|
||||
|
||||
$post_matter = YamlFrontMatter::parse(file_get_contents($physical_file_path));
|
||||
|
||||
$post_data = $post_matter->matter();
|
||||
|
||||
$post_data['path'] = $file_path;
|
||||
$post_data['markdown'] = $post_matter->body();
|
||||
|
||||
// TODO: This should be moved to an appropriately abstracted prep function
|
||||
|
||||
|
||||
if($is_directory) {
|
||||
$post_data['base'] ??= $original_file_path;
|
||||
$post_data['type'] ??= 'directory';
|
||||
}
|
||||
|
||||
$post_data['tags'] ??= [];
|
||||
$post_data['tags'] []= "type:" . ($post_data['type'] ?? Post::deduce_type($file_path));
|
||||
$post_data['tags'] []= "path:" . $file_path;
|
||||
|
||||
$sql_adapter->set_postdata($post_data);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
require_once 'fontawesome.php';
|
||||
|
||||
$post = $adapter->get_post($REQUEST_PATH);
|
||||
|
||||
function render_root_template($template, $args = []) {
|
||||
global $twig;
|
||||
|
@ -13,8 +12,8 @@ function render_root_template($template, $args = []) {
|
|||
$args['page'] ??= $SITE_CONFIG['site_defaults'];
|
||||
|
||||
$page = $args['page'];
|
||||
$page['base'] ??= $page['url'];
|
||||
|
||||
$page['base'] ??= $page['url'] ?? null;
|
||||
|
||||
$args['opengraph'] = [
|
||||
"site_name" => $page['site_name'] ?? 'Nameless Site',
|
||||
"title" => $page['title'] ?? 'Titleless',
|
||||
|
@ -23,7 +22,7 @@ function render_root_template($template, $args = []) {
|
|||
];
|
||||
|
||||
$args['banners'] = json_encode($page['banners'] ?? []);
|
||||
|
||||
|
||||
$args['age_gate'] = (!isset($_COOKIE['AgeConfirmed']))
|
||||
&& isset($SITE_CONFIG['age_gate']);
|
||||
|
||||
|
@ -34,9 +33,79 @@ function render_pathed_content_template($template, $args = []) {
|
|||
render_root_template($template, $args);
|
||||
}
|
||||
|
||||
render_pathed_content_template('post_types/markdown.html', [
|
||||
'page' => $post
|
||||
]);
|
||||
function render_post($post, $args = []) {
|
||||
$template = $post['template']
|
||||
?? ($post['markdown'] == '' ? 'directory' : 'vanilla');
|
||||
|
||||
|
||||
if(isset($post['search_tags'])) {
|
||||
$post['search'] = $post['search_tags'];
|
||||
}
|
||||
|
||||
if(isset($post['search'])) {
|
||||
$args['search_results'] = $post->handler->search_posts($post['search']);
|
||||
}
|
||||
|
||||
$args['page'] = $post;
|
||||
|
||||
render_pathed_content_template(
|
||||
'render_templates/' . $template . '.html',
|
||||
$args);
|
||||
}
|
||||
|
||||
if($REQUEST_PATH == '/upload') {
|
||||
render_root_template('upload.html');
|
||||
die();
|
||||
}
|
||||
if($REQUEST_PATH == '/search') {
|
||||
$search_results = [];
|
||||
$display_type = 'none';
|
||||
|
||||
$search_query = [
|
||||
'user_input' => $_GET['query'] ?? null,
|
||||
'user_type' => $_GET['search_type'] ?? 'all',
|
||||
'types' => [['All', 'all'], ['Images', 'image'], ['Blog Posts', 'blog']]
|
||||
];
|
||||
|
||||
if(isset($_GET['query']) && strlen($_GET['query']) > 0) {
|
||||
$search_request['query'] = $_GET['query'];
|
||||
|
||||
if(isset($_GET['search_type']) && $_GET['search_type'] != 'all') {
|
||||
$search_request['tags'] []= 'type:' . $_GET['search_type'];
|
||||
}
|
||||
|
||||
$search_results = $adapter->search_posts($search_request);
|
||||
|
||||
$type_count = [];
|
||||
if(count($search_results) > 0) {
|
||||
foreach($search_results as $result) {
|
||||
$type_count[$result['type']] ??= 0;
|
||||
$type_count[$result['type']] += 1;
|
||||
}
|
||||
|
||||
$display_type = array_search(max($type_count), $type_count);
|
||||
}
|
||||
} else {
|
||||
$display_type = 'no_search';
|
||||
}
|
||||
|
||||
$search_page = $SITE_CONFIG['site_defaults'];
|
||||
$search_page['path'] = '/search';
|
||||
$search_page['title'] = 'Search';
|
||||
$search_page['basename'] = 'search';
|
||||
|
||||
render_root_template('search.html', [
|
||||
'search_results' => $search_results,
|
||||
'page' => $search_page,
|
||||
'display_type' => $display_type,
|
||||
'search_query' => $search_query
|
||||
]);
|
||||
die();
|
||||
}
|
||||
|
||||
$post = $adapter->get_post($REQUEST_PATH);
|
||||
render_post($post);
|
||||
|
||||
die();
|
||||
|
||||
if(!isset($post)) {
|
||||
|
|
|
@ -29,14 +29,27 @@ $sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST);
|
|||
$adapter = new PostHandler($sql_adapter);
|
||||
|
||||
require_once 'dergdown.php';
|
||||
require_once 'setup/derg_insert.php';
|
||||
|
||||
function dergdown_to_html($text) {
|
||||
function dergdown_to_html($post) {
|
||||
$DergInsert = new DergInsertRenderer($post);
|
||||
$Parsedown = new Dergdown();
|
||||
$Parsedown->setDergRenderer($DergInsert);
|
||||
|
||||
return $Parsedown->text($text);
|
||||
$markdown = $post->markdown;
|
||||
|
||||
if($markdown == '') {
|
||||
$markdown = '
|
||||
{{
|
||||
template: fragments/directory/inline.html
|
||||
}}
|
||||
';
|
||||
}
|
||||
|
||||
return $Parsedown->text($markdown);
|
||||
}
|
||||
function post_to_html($post) {
|
||||
return dergdown_to_html($post->markdown);
|
||||
return dergdown_to_html($post);
|
||||
}
|
||||
$adapter->markdown_engine = "post_to_html";
|
||||
|
43
www/src/setup/derg_insert.php
Normal file
43
www/src/setup/derg_insert.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
class DergInsertRenderer {
|
||||
protected $twig;
|
||||
protected $post;
|
||||
protected $postAdapter;
|
||||
|
||||
public function __construct($post) {
|
||||
global $twig;
|
||||
global $adapter;
|
||||
|
||||
$this->twig = $twig;
|
||||
$this->post = $post;
|
||||
$this->postAdapter = $adapter;
|
||||
}
|
||||
|
||||
public function dergRender($renderConfig) {
|
||||
global $FONT_AWESOME_ARRAY;
|
||||
|
||||
if(!isset($renderConfig['template'])) {
|
||||
throw new Exception("No template type given!");
|
||||
}
|
||||
|
||||
$template = $renderConfig['template'];
|
||||
|
||||
$args = [
|
||||
'post' => $this->post,
|
||||
'page' => $this->post,
|
||||
'fa' => $FONT_AWESOME_ARRAY
|
||||
];
|
||||
|
||||
if(isset($renderConfig['post'])) {
|
||||
$args['post'] = $this->postAdapter->get_post($renderConfig['post']);
|
||||
}
|
||||
if(isset($renderConfig['page'])) {
|
||||
$args['page'] = $this->postAdapter->get_post($renderConfig['page']);
|
||||
}
|
||||
|
||||
return $this->twig->render($template, $args);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
26
www/src/setup/permissions.php
Normal file
26
www/src/setup/permissions.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
$ACCESS_PERMISSIONS = [
|
||||
"read" => true,
|
||||
"upload" => false
|
||||
];
|
||||
|
||||
$ACCESS_KEY = $REQUEST_QUERY['ACCESS_KEY']
|
||||
?? $_POST['ACCESS_KEY']
|
||||
?? $_COOKIE['ACCESS_KEY']
|
||||
?? '';
|
||||
|
||||
if($ACCESS_KEY == $SITE_CONFIG['ACCESS_KEY']) {
|
||||
$ACCESS_PERMISSIONS = [
|
||||
"read" => true,
|
||||
"upload" => true
|
||||
];
|
||||
}
|
||||
|
||||
function access_can_upload() {
|
||||
global $ACCESS_PERMISSIONS;
|
||||
|
||||
return $ACCESS_PERMISSIONS['upload'];
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
const BANNER_TIME = 600 * 1000.0
|
||||
const BANNER_ANIMATION = "opacity 0.8s linear, transform 0.1s linear"
|
||||
const BANNER_ANIMATION = "opacity 0.8s linear, transform 1s linear"
|
||||
// const BANNER_ANIMATION = "opacity 0.8s linear"
|
||||
|
||||
|
||||
class BannerHandler {
|
||||
constructor(banner_container, banner_image, banner_link) {
|
||||
|
@ -32,7 +34,7 @@ class BannerHandler {
|
|||
|
||||
console.log("Starting tick")
|
||||
|
||||
this.bannerUpdateTimer = setInterval(() => { this.updateTick() }, 100);
|
||||
this.bannerUpdateTimer = setInterval(() => { this.updateTick() }, 1000);
|
||||
}
|
||||
stopUpdateTick() {
|
||||
if(this.bannerUpdateTimer === null) {
|
||||
|
@ -71,7 +73,7 @@ class BannerHandler {
|
|||
const bannerPercentage = (bannerPercentageFrom + (bannerPercentageTo - bannerPercentageFrom) * this.currentPhase)
|
||||
|
||||
const banner_top = (1-bannerPercentage) * bannerTranslateMax
|
||||
this.bannerDOM.style.transform = "translateY(" + banner_top + 'px' + ")"
|
||||
this.bannerDOM.style.transform = "translateZ(0.1px) translateY(" + banner_top + 'px)'
|
||||
}
|
||||
|
||||
fadeOut() {
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
@import "styles/age_gate.css";
|
||||
@import "styles/post_navbar.css";
|
||||
@import "styles/search.css";
|
||||
@import "styles/toc.css";
|
||||
|
||||
/********************
|
||||
* GENERAL SETTINGS *
|
||||
********************
|
||||
*/
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
|
@ -25,6 +34,12 @@ body {
|
|||
--text_1: #FFFFFF;
|
||||
--text_border: #A0A0A080;
|
||||
|
||||
--content-width: min(100vw, calc(20rem + 40vw));
|
||||
--content-total-margin: calc(calc(100vw - var(--content-width)) / 2);
|
||||
|
||||
--content-padding: max(0.5rem, min(1rem, var(--content-total-margin)));
|
||||
--content-margin: max(0px, calc(var(--content-total-margin) - 1rem));
|
||||
|
||||
color: var(--text_1);
|
||||
background: var(--bg_1);
|
||||
margin: 0px;
|
||||
|
@ -42,6 +57,17 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px) {
|
||||
#toc_container {
|
||||
display: none !important;
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
#main_content_flexbox:before {
|
||||
flex: 0.5 0 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
:link {
|
||||
color: var(--highlight_1);
|
||||
font-style: italic;
|
||||
|
@ -56,45 +82,6 @@ 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;
|
||||
|
@ -157,19 +144,31 @@ a:hover {
|
|||
scroll-margin-top: 6rem;
|
||||
}
|
||||
|
||||
#main_content_flexbox {
|
||||
width: 100vw;
|
||||
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
align-items: flex-start;
|
||||
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
flex: 0.2 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
#main_content_wrapper {
|
||||
--content-width: min(100vw, calc(20rem + 40vw));
|
||||
--content-total-margin: calc(calc(100vw - var(--content-width)) / 2);
|
||||
/*padding: 0rem var(--content-padding) 1rem var(--content-padding);*/
|
||||
|
||||
/*width: var(--content-width);*/
|
||||
flex: 0 1 var(--content-width);
|
||||
|
||||
--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);
|
||||
width: auto;
|
||||
|
||||
|
||||
margin-left: var(--content-margin);
|
||||
margin-right: var(--content-margin);
|
||||
/*margin-left: calc(var(--content-margin));
|
||||
//margin-right: var(--content-margin);*/
|
||||
|
||||
margin-top: 0px;
|
||||
|
||||
min-height: 100%;
|
||||
|
@ -206,137 +205,42 @@ body.htmx-request::before {
|
|||
transition-delay: 0.5s;
|
||||
}
|
||||
|
||||
#post_file_bar {
|
||||
position: sticky;
|
||||
.folder_listing {
|
||||
& input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
top: 0px;
|
||||
& input ~ ul {
|
||||
display: none;
|
||||
}
|
||||
& input:checked ~ ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
background: var(--bg_2);
|
||||
|
||||
box-shadow: 0px 5px 5px 0px #00000040;
|
||||
z-index: 5;
|
||||
label > :nth-child(2) {
|
||||
display: none;
|
||||
}
|
||||
input:checked ~ label > :nth-child(1) {
|
||||
display: none;
|
||||
}
|
||||
input:checked ~ label > :nth-child(2) {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#post_file_titles {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: left;
|
||||
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#post_file_titles > li {
|
||||
padding: 0.2rem 0.8rem;
|
||||
|
||||
font-style: bold;
|
||||
font-size: 1.5rem;
|
||||
|
||||
background: var(--highlight_1);
|
||||
}
|
||||
|
||||
#post_file_path {
|
||||
width: 100%;
|
||||
font-style: italic;
|
||||
padding-left: 0.5rem;
|
||||
|
||||
background: var(--highlight_1);
|
||||
|
||||
font-size: 1.1rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
list-style-type: none;
|
||||
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
|
||||
white-space: nowrap;
|
||||
}
|
||||
#post_file_path a {
|
||||
color: var(--text_1);
|
||||
padding-right: 0.2rem;
|
||||
}
|
||||
|
||||
.navbar-expand {
|
||||
background-color: var(--bg_3);
|
||||
|
||||
border: 0.15rem solid var(--highlight_1);
|
||||
border-top: none;
|
||||
|
||||
border-radius: 0 0 0.2rem 0.2rem;
|
||||
overflow: clip;
|
||||
|
||||
padding-bottom: 0.2rem;
|
||||
padding-left: 0.3rem;
|
||||
|
||||
max-height: 80vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.navbar-full-path {
|
||||
width: 100%;
|
||||
font-style: italic;
|
||||
padding-left: 0.2rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
list-style-type: none;
|
||||
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
|
||||
white-space: nowrap;
|
||||
}
|
||||
.navbar-full-path li {
|
||||
margin-left: 0.3rem;
|
||||
}
|
||||
|
||||
.navbar-folder-list span, .navbar-folder-list label {
|
||||
width: 1rem;
|
||||
display: inline-block;
|
||||
label.expandable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.navbar-folder-list ul, .navbar-folder-list {
|
||||
list-style: none;
|
||||
padding-left: 0.4rem;
|
||||
margin-left: 0.4rem;
|
||||
|
||||
border-left: 1px solid var(--text_border);
|
||||
}
|
||||
.navbar-folder-list a:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.folder-listing input {
|
||||
input.expandable {
|
||||
display: none;
|
||||
}
|
||||
.folder-listing input ~ ul {
|
||||
input.expandable + .expandable {
|
||||
display: none;
|
||||
}
|
||||
.folder-listing input:checked ~ ul {
|
||||
input.expandable:checked + .expandable {
|
||||
display: block;
|
||||
}
|
||||
.folder-listing label > :nth-child(2) {
|
||||
display: none;
|
||||
}
|
||||
.folder-listing input:checked ~ label > :nth-child(1) {
|
||||
display: none;
|
||||
}
|
||||
.folder-listing input:checked ~ label > :nth-child(2) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#navbar-expand-label {
|
||||
cursor: pointer;
|
||||
}
|
||||
#navbar-expander, .navbar-expand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#navbar-expander:checked + .navbar-expand {
|
||||
display: block;
|
||||
}
|
||||
|
||||
article {
|
||||
background: var(--bg_3);
|
||||
|
@ -345,17 +249,17 @@ article {
|
|||
box-shadow: 3px 7px 7px 0px #00000040;
|
||||
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
article img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
|
||||
max-height: 70vh;
|
||||
|
||||
margin: 1vmin;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
& img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
|
||||
max-height: 70vh;
|
||||
|
||||
margin: 1vmin;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
#content_footer {
|
||||
|
@ -380,9 +284,8 @@ article img {
|
|||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#main_footer span {
|
||||
align-self: flex-end;
|
||||
width: 100%;
|
||||
& span {
|
||||
align-self: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,111 +93,111 @@ html {
|
|||
|
||||
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;
|
||||
& p,
|
||||
.modest-p {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
article ul,
|
||||
article ol {
|
||||
padding-left: 2em;
|
||||
& h1,
|
||||
.modest-h1,
|
||||
& h2,
|
||||
.modest-h2,
|
||||
& h3,
|
||||
.modest-h3,
|
||||
& h4,
|
||||
.modest-h4 {
|
||||
margin: 1.5em 0 .3em;
|
||||
font-weight: inherit;
|
||||
line-height: 1.42;
|
||||
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
& > :first-child {
|
||||
margin-top: 0.3rem !important;
|
||||
}
|
||||
|
||||
& h1,
|
||||
.modest-h1 {
|
||||
margin-top: 0;
|
||||
font-size: 1.998rem;
|
||||
}
|
||||
|
||||
& h2,
|
||||
.modest-h2 {
|
||||
font-size: 1.427rem;
|
||||
}
|
||||
|
||||
& h3,
|
||||
.modest-h3 {
|
||||
font-size: 1.299rem;
|
||||
}
|
||||
|
||||
& h4,
|
||||
.modest-h4 {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
& h5,
|
||||
.modest-h5 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
& h6,
|
||||
.modest-h6 {
|
||||
font-size: .88rem;
|
||||
}
|
||||
|
||||
& small,
|
||||
.modest-small {
|
||||
font-size: .707rem;
|
||||
}
|
||||
|
||||
/* https://github.com/mrmrs/fluidity */
|
||||
|
||||
& h1,
|
||||
& h2,
|
||||
& 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;
|
||||
}
|
||||
}
|
||||
|
||||
& ul,
|
||||
& ol {
|
||||
padding-left: 2em;
|
||||
}
|
||||
}
|
42
www/static/styles/age_gate.css
Normal file
42
www/static/styles/age_gate.css
Normal file
|
@ -0,0 +1,42 @@
|
|||
/****************
|
||||
* AGE GATE CSS *
|
||||
****************/
|
||||
|
||||
#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;
|
||||
|
||||
& 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%);
|
||||
}
|
||||
|
||||
& p {
|
||||
min-height: 5em;
|
||||
}
|
||||
}
|
122
www/static/styles/post_navbar.css
Normal file
122
www/static/styles/post_navbar.css
Normal file
|
@ -0,0 +1,122 @@
|
|||
|
||||
|
||||
.navbar {
|
||||
position: sticky;
|
||||
|
||||
top: 0px;
|
||||
|
||||
background: var(--bg_2);
|
||||
|
||||
box-shadow: 0px 5px 5px 0px #00000040;
|
||||
z-index: 5;
|
||||
|
||||
& > ._titles {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: left;
|
||||
|
||||
height: 2.3rem;
|
||||
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
|
||||
& li {
|
||||
padding: 0.2rem 0.8rem;
|
||||
|
||||
font-style: bold;
|
||||
font-size: 1.5rem;
|
||||
|
||||
background: var(--highlight_1);
|
||||
}
|
||||
}
|
||||
|
||||
& > ._path {
|
||||
width: 100%;
|
||||
height: 1.5rem;
|
||||
|
||||
font-style: italic;
|
||||
padding-left: 0.5rem;
|
||||
|
||||
background: var(--highlight_1);
|
||||
|
||||
font-size: 1.1rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
list-style-type: none;
|
||||
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
& a {
|
||||
color: var(--text_1);
|
||||
padding-right: 0.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
& > ._expand_label {
|
||||
cursor: pointer;
|
||||
}
|
||||
& > ._details {
|
||||
background-color: var(--bg_3);
|
||||
|
||||
border: 0.15rem solid var(--highlight_1);
|
||||
border-top: none;
|
||||
|
||||
border-radius: 0 0 0.2rem 0.2rem;
|
||||
overflow: clip;
|
||||
|
||||
padding-bottom: 0.2rem;
|
||||
padding-left: 0.3rem;
|
||||
|
||||
max-height: 80vh;
|
||||
overflow-y: scroll;
|
||||
|
||||
& > ._full_path {
|
||||
width: 100%;
|
||||
font-style: italic;
|
||||
padding-left: 0.2rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
list-style-type: none;
|
||||
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
& li {
|
||||
margin-left: 0.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
& > ._folder_list {
|
||||
list-style: none;
|
||||
padding-left: 0.4rem;
|
||||
margin-left: 0.4rem;
|
||||
|
||||
border-left: 1px solid var(--text_border);
|
||||
|
||||
& ul {
|
||||
list-style: none;
|
||||
padding-left: 0.4rem;
|
||||
margin-left: 0.4rem;
|
||||
|
||||
border-left: 1px solid var(--text_border);
|
||||
}
|
||||
|
||||
& a:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
& span, & label {
|
||||
width: 1rem;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
113
www/static/styles/search.css
Normal file
113
www/static/styles/search.css
Normal file
|
@ -0,0 +1,113 @@
|
|||
|
||||
|
||||
.dergen_search_form {
|
||||
text-align: center;
|
||||
|
||||
& h1 {
|
||||
padding-left: 0px;
|
||||
|
||||
border: none;
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
|
||||
& input[type='text'] {
|
||||
color: var(--text_1);
|
||||
|
||||
height: 2.5rem;
|
||||
min-width: min(80%, 30em);
|
||||
|
||||
font-size: 1.2em;
|
||||
|
||||
background-color: #616161;
|
||||
|
||||
border: none;
|
||||
border-radius: 1.25rem;
|
||||
|
||||
padding-left: 1.5rem;
|
||||
|
||||
margin-bottom: 0.8em;
|
||||
|
||||
box-shadow: 3px 3px 3px #00000033;
|
||||
}
|
||||
|
||||
& button {
|
||||
color: var(--text_1);
|
||||
background-color: #515151;
|
||||
border: none;
|
||||
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
width: 15em;
|
||||
height: 2.5em;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-bottom: 1em;
|
||||
|
||||
box-shadow: 3px 3px 2px #00000033;
|
||||
}
|
||||
|
||||
button, input[type='text'] {
|
||||
transition: background-color 0.3s;
|
||||
|
||||
&:hover {
|
||||
background-color: #777777;
|
||||
}
|
||||
}
|
||||
|
||||
& ._search_type {
|
||||
text-align: left;
|
||||
|
||||
margin-top: 1em;
|
||||
|
||||
& input {
|
||||
display: none;
|
||||
}
|
||||
& label {
|
||||
min-width: 5em;
|
||||
text-align: center;
|
||||
|
||||
padding-left: 0.7em;
|
||||
padding-right: 0.7em;
|
||||
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
|
||||
border-bottom: 2px solid #FFFFFF50;
|
||||
}
|
||||
& input:checked + label {
|
||||
border-bottom: 2px solid #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.dergen_search_result_listing {
|
||||
list-style: none;
|
||||
|
||||
margin-left: 0em;
|
||||
padding-left: 0;
|
||||
|
||||
& > li {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
& ._details {
|
||||
list-style: none;
|
||||
padding-left: 1.3em;
|
||||
|
||||
& ._path_details {
|
||||
color: #AAAAAA;
|
||||
|
||||
& span {
|
||||
font-size: 0.8rem;
|
||||
min-width: 5rem;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
& ._title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
60
www/static/styles/toc.css
Normal file
60
www/static/styles/toc.css
Normal file
|
@ -0,0 +1,60 @@
|
|||
|
||||
.table_of_contents {
|
||||
position: sticky;
|
||||
float: left;
|
||||
|
||||
top: 0px;
|
||||
|
||||
flex: 0.1 0 15em;
|
||||
|
||||
padding-right: 0.3em;
|
||||
|
||||
padding-bottom: 0.5em;
|
||||
border-radius: 0 0em 0em 0.5em;
|
||||
|
||||
box-shadow: -0.2em 0.2em 0.2em black;
|
||||
|
||||
--toc-fg: #cecece;
|
||||
--toc-bg: #EE901544;
|
||||
|
||||
background-color: var(--bg_2);
|
||||
|
||||
& a {
|
||||
display: inline-block;
|
||||
|
||||
transition: all 0.5s !important;
|
||||
|
||||
line-height: 1em;
|
||||
text-align: justify;
|
||||
font-size: 0.9rem;
|
||||
|
||||
padding-left: 0.2em;
|
||||
padding-right: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
|
||||
|
||||
|
||||
color: var(--toc-fg) !important;
|
||||
}
|
||||
|
||||
& .active {
|
||||
background-color: var(--toc-bg);
|
||||
border-bottom: 1px solid var(--highlight_1);
|
||||
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
& ol {
|
||||
padding-left: 0.5em;
|
||||
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
& > li {
|
||||
border-radius: 0.2em;
|
||||
border-bottom: 1px solid transparent;
|
||||
|
||||
margin-bottom: 0.2em;
|
||||
transition: all 1s;
|
||||
}
|
||||
}
|
265
www/static/toc.js
Normal file
265
www/static/toc.js
Normal file
|
@ -0,0 +1,265 @@
|
|||
// Code By Webdevtrick ( https://webdevtrick.com )
|
||||
// https://webdevtrick.com/dynamic-table-of-contents/
|
||||
//
|
||||
// Modified by dergens
|
||||
|
||||
let tocId = "toc";
|
||||
|
||||
const TOC_IDENTIFIER = "toc";
|
||||
const TOC_MAIN_CONTENT_ID = "#content_article"
|
||||
const TOC_TOP_PIXEL_MARGIN = 300;
|
||||
|
||||
class TocTracker {
|
||||
known_heading_elements = [];
|
||||
|
||||
constructor() {
|
||||
this.intersection_observer = null;
|
||||
|
||||
this.tracking_update_callbacks = [];
|
||||
this.tracking_rebuild_callbacks = [];
|
||||
}
|
||||
|
||||
_disconnectIntersectionObserver() {
|
||||
if(this.intersection_observer) {
|
||||
this.intersection_observer.disconnect();
|
||||
this.intersection_observer = null;
|
||||
}
|
||||
}
|
||||
|
||||
_setupIntersectionObserver() {
|
||||
let options = {
|
||||
root: null,
|
||||
rootMargin: `-${TOC_TOP_PIXEL_MARGIN}px 0px 0px 0px`,
|
||||
threshold: [1]
|
||||
};
|
||||
|
||||
this.intersection_observer = new IntersectionObserver(
|
||||
(entries) => this._findActiveHeading(),
|
||||
options
|
||||
);
|
||||
|
||||
this.known_heading_elements.forEach((element) => {
|
||||
this.intersection_observer.observe(element.dom);
|
||||
});
|
||||
}
|
||||
|
||||
_searchForHeadings() {
|
||||
const main = document.querySelector(TOC_MAIN_CONTENT_ID);
|
||||
if (!main) {
|
||||
throw Error("A `main` tag section is required to query headings from.");
|
||||
}
|
||||
|
||||
let headings = main.querySelectorAll("h1, h2, h3, h4, h5, h6");
|
||||
headings.forEach((heading, index) => {
|
||||
const heading_level = parseInt(heading.tagName.slice(-1));
|
||||
|
||||
this.known_heading_elements.push({
|
||||
level: heading_level,
|
||||
name: heading.innerHTML,
|
||||
dom: heading,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_sortHeadings() {
|
||||
this.known_heading_elements.sort((a, b) => {
|
||||
return a.dom.getBoundingClientRect().y
|
||||
- b.dom.getBoundingClientRect().y;
|
||||
});
|
||||
}
|
||||
|
||||
_generateHeadingPaths() {
|
||||
let current_path = [];
|
||||
|
||||
this.known_heading_elements.forEach(heading => {
|
||||
while((current_path.length != 0) && (current_path[current_path.length-1].level >= heading.level))
|
||||
current_path.pop();
|
||||
|
||||
current_path.push(heading);
|
||||
|
||||
// The zero does nothing. But according to stackoverflow, it makes it faster.
|
||||
// Why? No idea.
|
||||
// Just leave it in I suppose.
|
||||
heading.path = current_path.slice(0);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
_fillHeadingIDs() {
|
||||
this.known_heading_elements.forEach(heading => {
|
||||
heading.id = heading.dom.id;
|
||||
if(heading.id)
|
||||
return;
|
||||
|
||||
heading.dom.id = heading.path.map(i =>
|
||||
i.name.replace(/\W+/g, '-').trim()
|
||||
).join('--').toLowerCase();
|
||||
|
||||
heading.id = heading.dom.id;
|
||||
});
|
||||
}
|
||||
|
||||
_findActiveHeading() {
|
||||
const arr = this.known_heading_elements;
|
||||
|
||||
if(arr.length == 0)
|
||||
return;
|
||||
|
||||
// Start should always be higher
|
||||
// End always lower
|
||||
let start = 0, end = arr.length - 1;
|
||||
let mid = Math.floor((start + end) / 2);
|
||||
|
||||
// Special case handling, if all items are above the scroll margin
|
||||
if(arr[end].dom.getBoundingClientRect().y < TOC_TOP_PIXEL_MARGIN) {
|
||||
start = end;
|
||||
mid = end;
|
||||
}
|
||||
|
||||
// Iterate until we find the boundary
|
||||
while (mid > start) {
|
||||
|
||||
// Check if the mid is above our boundary ((lower Y))
|
||||
if (arr[mid].dom.getBoundingClientRect().y < TOC_TOP_PIXEL_MARGIN)
|
||||
start = mid;
|
||||
else
|
||||
end = mid;
|
||||
|
||||
// Find the mid index
|
||||
mid = Math.floor((start + end) / 2);
|
||||
}
|
||||
|
||||
this.tracking_update_callbacks.forEach(callback => callback(arr[start]));
|
||||
|
||||
return arr[start];
|
||||
}
|
||||
|
||||
reloadHeadings() {
|
||||
this.known_heading_elements = [];
|
||||
|
||||
this._disconnectIntersectionObserver();
|
||||
this._searchForHeadings();
|
||||
this._sortHeadings();
|
||||
this._generateHeadingPaths();
|
||||
this._fillHeadingIDs();
|
||||
|
||||
this.tracking_rebuild_callbacks.forEach(item => item());
|
||||
|
||||
this._setupIntersectionObserver();
|
||||
}
|
||||
|
||||
onTrackingUpdate(callback) {
|
||||
this.tracking_update_callbacks.push(callback);
|
||||
|
||||
return callback;
|
||||
}
|
||||
|
||||
onTrackingRebuild(callback) {
|
||||
this.tracking_rebuild_callbacks.push(callback);
|
||||
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
|
||||
class TocNavBarUpdater {
|
||||
constructor(toc_tracker) {
|
||||
this.toc_tracker = toc_tracker;
|
||||
this.navbar_dom = null;
|
||||
|
||||
this.added_navbar_elements = [];
|
||||
|
||||
this.toc_tracker.onTrackingRebuild(() =>
|
||||
this.trackingRebuildCallback() );
|
||||
|
||||
this.toc_tracker.onTrackingUpdate((element) => this.trackingUpdateCallback(element) );
|
||||
}
|
||||
|
||||
_removeNavbarElements() {
|
||||
this.added_navbar_elements.forEach(dom => dom.remove());
|
||||
this.added_navbar_elements = [];
|
||||
}
|
||||
|
||||
trackingRebuildCallback() {
|
||||
this._removeNavbarElements();
|
||||
|
||||
this.navbar_dom = document.querySelector('#main_navbar_path');
|
||||
}
|
||||
|
||||
trackingUpdateCallback(element) {
|
||||
this._removeNavbarElements();
|
||||
|
||||
const navbar_prev_node = this.navbar_dom.children[this.navbar_dom.children.length -1];
|
||||
|
||||
element.path.forEach(pathItem => {
|
||||
let newNode = document.createElement('li');
|
||||
|
||||
const pathURL = location.pathname + '#' + pathItem.id + location.search;
|
||||
newNode.innerHTML = "<a href=" + pathURL + "> #<sub>" + pathItem.level + '</sub>' + pathItem.name + '</a>'
|
||||
|
||||
this.navbar_dom.insertBefore(newNode, navbar_prev_node);
|
||||
this.added_navbar_elements.push(newNode);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class TocSidemenu {
|
||||
constructor(toc_tracker) {
|
||||
this.toc_tracker = toc_tracker;
|
||||
|
||||
this.sidebar_dom = null;
|
||||
|
||||
this.sidebar_elements = {};
|
||||
this.currently_active_entry = null;
|
||||
|
||||
toc_tracker.onTrackingRebuild(() => this.trackingRebuildCallback());
|
||||
toc_tracker.onTrackingUpdate((element) => this.trackingUpdateCallback(element));
|
||||
}
|
||||
|
||||
_clearSidebar() {
|
||||
if(this.sidebar_dom) {
|
||||
this.sidebar_dom.remove();
|
||||
}
|
||||
|
||||
this.sidebar_elements = {};
|
||||
this.currently_active_entry = null;
|
||||
}
|
||||
|
||||
_generateSidebar() {
|
||||
this.toc_tracker.known_heading_elements.forEach(element => {
|
||||
let new_element = document.createElement('li');
|
||||
|
||||
const pathURL = location.pathname + '#' + element.id + location.search;
|
||||
|
||||
new_element.style = "padding-left: " + element.level * 0.8 + "em";
|
||||
new_element.innerHTML = "<a href=" + pathURL + ">" + element.name + "</a>";
|
||||
|
||||
this.sidebar_elements[element.id] = new_element;
|
||||
|
||||
this.sidebar_dom.appendChild(new_element);
|
||||
});
|
||||
}
|
||||
|
||||
trackingRebuildCallback() {
|
||||
this._clearSidebar();
|
||||
|
||||
this.sidebar_dom = document.createElement('ol');
|
||||
document.querySelector('#toc').appendChild(this.sidebar_dom);
|
||||
|
||||
this._generateSidebar();
|
||||
}
|
||||
|
||||
trackingUpdateCallback(entry) {
|
||||
if(this.currently_active_entry) {
|
||||
this.currently_active_entry.classList.remove('active');
|
||||
}
|
||||
|
||||
let active_entry = this.sidebar_elements[entry.dom.id];
|
||||
active_entry.classList.add('active');
|
||||
this.currently_active_entry = active_entry;
|
||||
}
|
||||
}
|
||||
|
||||
let tracker = new TocTracker();
|
||||
let navbar_updater = new TocNavBarUpdater(tracker);
|
||||
let sidebar_updater = new TocSidemenu(tracker);
|
|
@ -1,4 +0,0 @@
|
|||
|
||||
{% for post in page.child_posts %}
|
||||
{{ include('ajax/compact_filelist/entry.html') }}
|
||||
{% endfor %}
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
|
||||
<a href="{{post.url}}">
|
||||
<div class="article_blop">
|
||||
<div class="article_blop_bg" style="background-image:url({{post.preview_image}});"></div>
|
||||
|
@ -16,7 +15,7 @@
|
|||
</ul>
|
||||
|
||||
<span class="article_blop_excerpt">
|
||||
{{post.excerpt}}
|
||||
{{ post.brief }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
0
www/templates/fragments/blog/listing.html
Normal file
0
www/templates/fragments/blog/listing.html
Normal file
10
www/templates/fragments/decoration/blog_header.html
Normal file
10
www/templates/fragments/decoration/blog_header.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
<div>
|
||||
<h1>
|
||||
{{ post.title }}
|
||||
</h1>
|
||||
|
||||
{% if post.authors %}
|
||||
Written by {{ post.authors }}
|
||||
{% endif %}
|
||||
</div>
|
|
@ -1,10 +1,10 @@
|
|||
<div id="post_file_bar">
|
||||
<menu id="post_file_titles">
|
||||
<div class="navbar">
|
||||
<menu class="_titles">
|
||||
<li>
|
||||
{{ page.basename }}
|
||||
</li>
|
||||
</menu>
|
||||
<menu id="post_file_path">
|
||||
<menu class="_path" id="main_navbar_path">
|
||||
{% set split_post = page.path |split('/') %}
|
||||
{% for i in range(0, split_post|length - 1) %}
|
||||
<li>
|
||||
|
@ -20,7 +20,7 @@
|
|||
|
||||
|
||||
<li style="margin-left: auto;">
|
||||
<label for="navbar-expander" id="navbar-expand-label"> {{ fa['bars'] | raw }} </label>
|
||||
<label for="navbar-expander" class="expandable"> {{ fa['bars'] | raw }} </label>
|
||||
<a rel="alternate" type="application/rss+xml" target="_blank"
|
||||
style="padding-left: 0.3rem;" href="/feed/rss{{page.path}}">
|
||||
{{ fa['rss']|raw }}
|
||||
|
@ -28,10 +28,10 @@
|
|||
</li>
|
||||
</menu>
|
||||
|
||||
<input id="navbar-expander" class="navbar-expand" type="checkbox" hx-preserve>
|
||||
<div class="navbar-expand">
|
||||
<input id="navbar-expander" class="expandable" type="checkbox" hx-preserve>
|
||||
<div class="_details expandable">
|
||||
Full path:
|
||||
<ul class="navbar-full-path">
|
||||
<ul class="_full_path">
|
||||
{% set split_post = page.path |split('/') %}
|
||||
{% for i in range(0, split_post|length - 1) %}
|
||||
<li>
|
||||
|
@ -52,8 +52,8 @@
|
|||
|
||||
<h4>Folder browser: </h4>
|
||||
|
||||
<ul class="navbar-folder-list">
|
||||
<li class="folder-listing">
|
||||
<ul class="_folder_list">
|
||||
<li>
|
||||
<span>
|
||||
{{ fa['turn-up'] | raw}}
|
||||
</span>
|
||||
|
@ -61,7 +61,7 @@
|
|||
<a href={{page.parent.path}}>..</a>
|
||||
</li>
|
||||
|
||||
{{ include('ajax/compact_filelist/listing.html') }}
|
||||
{{ include('fragments/directory/compact/listing.html') }}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
|
@ -1,12 +1,12 @@
|
|||
|
||||
|
||||
<li class="folder-listing">
|
||||
<li class="folder_listing">
|
||||
{% set folder_key = random() %}
|
||||
|
||||
<input type="checkbox" id="folder-open-{{folder_key}}" name="folder-open-{{folder_key}}">
|
||||
<label for="folder-open-{{folder_key}}"
|
||||
hx-trigger="click once queue:last, mouseenter once queue:all, intersect once queue:all"
|
||||
hx-get="/ajax/compact_filelist/listing.html?page={{ post.path }}"
|
||||
hx-get="/ajax/fragments/directory/compact/listing.html?page={{ post.path }}"
|
||||
hx-target="next ul">
|
||||
|
||||
{{ fa['folder'] | raw }}
|
||||
|
@ -17,7 +17,7 @@
|
|||
{{ post.basename }} - {{ post.title }}
|
||||
</a>
|
||||
|
||||
<ul class="folder-listing">
|
||||
<ul class="folder_listing">
|
||||
<li>
|
||||
<span>
|
||||
Loading...
|
4
www/templates/fragments/directory/compact/listing.html
Normal file
4
www/templates/fragments/directory/compact/listing.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
{% for post in page.child_posts %}
|
||||
{{ include('fragments/directory/compact/entry.html') }}
|
||||
{% endfor %}
|
39
www/templates/fragments/directory/inline.html
Normal file
39
www/templates/fragments/directory/inline.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
<h3>Directory contents for {{page.basename}}:</h3>
|
||||
<table class="directory">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="hsmol_hide">Name</th>
|
||||
<th>Title</th>
|
||||
<th class="entry_update_time hsmol_hide">Modified</th>
|
||||
</tr>
|
||||
|
||||
<tr class="entry">
|
||||
<td>
|
||||
{{ fa['turn-up'] | raw }}
|
||||
</td>
|
||||
<td class="hsmol_hide">
|
||||
<a href={{page.parent.path}}> .. </a>
|
||||
</td>
|
||||
<td class="entry_title">
|
||||
<a href={{page.parent.path}}> .. </a>
|
||||
</td>
|
||||
<td class="entry_update_time hsmol_hide">
|
||||
</td>
|
||||
</tr>
|
||||
{% for post in page.child_posts %}
|
||||
<tr class="entry">
|
||||
<td>
|
||||
{{ fa[post.icon] | raw }}
|
||||
</td>
|
||||
<td class="hsmol_hide">
|
||||
<a href={{post.path}}>{{post.basename}}</a>
|
||||
</td>
|
||||
<td class="entry_title">
|
||||
<a href={{post.path}}>{{ post.title }}</a>
|
||||
</td>
|
||||
<td class="entry_update_time hsmol_hide">
|
||||
{{ post.updated_at }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
|
@ -1,28 +0,0 @@
|
|||
|
||||
{% extends "root.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="/static/gallerystyle.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block second_title %}
|
||||
<h2> Gallery </h2>
|
||||
{% endblock %}
|
||||
|
||||
{%block main_content%}
|
||||
<figure>
|
||||
|
||||
<a href="{{ image_url }}" target="_blank">
|
||||
<img id="gallery_image" src="{{ image_url }}"> </img>
|
||||
</a>
|
||||
|
||||
<article>
|
||||
<figcaption id="gallery_title"> {{ image_title }} </figcaption>
|
||||
<span> {{ image_desc }} </span>
|
||||
<br>
|
||||
<a href="{{ artist_src_link }}"> Artist: {{ artist_name }} </a>
|
||||
<br>
|
||||
<a href="{{ image_src_link }}"> Source link </a>
|
||||
</article>
|
||||
</figure>
|
||||
{%endblock%}
|
|
@ -1,18 +0,0 @@
|
|||
|
||||
{% extends "root.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="/static/gallerystyle.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block second_title %}
|
||||
<h2> Gallery </h2>
|
||||
{% endblock %}
|
||||
|
||||
{%block main_content%}
|
||||
<div id="gallery_root_grid">
|
||||
{% for key,value in array_path %}
|
||||
<div class=""
|
||||
{% endfor %}
|
||||
</div>
|
||||
{%endblock%}
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
{%block main_content%}
|
||||
|
||||
{{ include('fragments/filepath_bar.html') }}
|
||||
{{ include('fragments/decoration/navbar.html') }}
|
||||
|
||||
<article id="content_article">
|
||||
{%block content_article %}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
|
||||
|
||||
{% extends "pathed_content.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="/static/directorystyle.css">
|
||||
{%endblock%}
|
||||
|
||||
{%block content_article%}
|
||||
{% if subposts|length > 0 %}
|
||||
<h3>Directory contents:</h3>
|
||||
<table class="directory">
|
||||
<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>
|
||||
{%else%}
|
||||
<h4>This directory appears to be empty...</h4>
|
||||
<span>This shouldn't happen, sorry!</span>
|
||||
{% endif %}
|
||||
|
||||
{{ content_html|raw }}
|
||||
|
||||
{%endblock%}
|
|
@ -1,59 +0,0 @@
|
|||
|
||||
|
||||
{% 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%}
|
13
www/templates/render_templates/directory.html
Normal file
13
www/templates/render_templates/directory.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
|
||||
{% extends "pathed_content.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="/static/directorystyle.css">
|
||||
{%endblock%}
|
||||
|
||||
{%block content_article%}
|
||||
{{ include('fragments/directory/inline.html') }}
|
||||
|
||||
{{ page.html|raw }}
|
||||
{%endblock%}
|
27
www/templates/render_templates/gallery.html
Normal file
27
www/templates/render_templates/gallery.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
|
||||
{% extends "pathed_content.html" %}
|
||||
|
||||
{%block content_article%}
|
||||
{{ include('fragments/directory/inline.html') }}
|
||||
|
||||
{{ content_html|raw }}
|
||||
|
||||
{% if search_results|length > 0 %}
|
||||
<ul class="gallery">
|
||||
{% for post in search_results %}
|
||||
<li class="entry">
|
||||
<a href={{post.path}}>
|
||||
<figure>
|
||||
<img src="{{post.media_preview_url}}">
|
||||
<figcaption>
|
||||
{{ post.title }}
|
||||
</figcaption>
|
||||
</figure>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{%else%}
|
||||
<h4>How sad. There are no images yet... What a real shame :c </h4>
|
||||
{% endif %}
|
||||
{%endblock%}
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
|
||||
{% extends "pathed_content.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
|
@ -14,12 +13,12 @@
|
|||
|
||||
{%block content_article%}
|
||||
<figure>
|
||||
<a target="_blank" href="{{post.post_metadata.media_file}}">
|
||||
<img id="gallery_image" src="{{post.post_metadata.media_file}}"></img>
|
||||
<a target="_blank" href="{{page.media_url}}">
|
||||
<img id="gallery_image" src="{{page.media_url}}"></img>
|
||||
</a>
|
||||
|
||||
<figcaption>
|
||||
{{ content_html|raw }}
|
||||
{{ page.html|raw }}
|
||||
</figcaption>
|
||||
</figure>
|
||||
{%endblock%}
|
|
@ -9,7 +9,7 @@
|
|||
{%endblock %}
|
||||
|
||||
{%block content_article%}
|
||||
{% if page.title %}
|
||||
{% if page.title and (page.title != page.basename) %}
|
||||
<h1 class="content-title"> {{ page.title }} </h1>
|
||||
{% endif %}
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
<script src="/static/htmx.min.js"></script>
|
||||
<script id="main_banner_script" src="/static/banner.js"></script>
|
||||
<script src="/static/toc.js"></script>
|
||||
|
||||
{% if page.base %}
|
||||
<base href="{{ page.base }}">
|
||||
|
@ -90,20 +91,34 @@
|
|||
</menu>
|
||||
</header>
|
||||
|
||||
<main id="main_content_wrapper">
|
||||
{% block main_content %}
|
||||
<h3>This here should have been replaced by content.
|
||||
</h3>
|
||||
<div id="main_content_flexbox">
|
||||
<nav id="toc_container" class="table_of_contents navbar">
|
||||
<div class="_titles">
|
||||
</div>
|
||||
<div class="_path">
|
||||
toc
|
||||
</div>
|
||||
|
||||
If you can see this, complain to your nearest dragon.
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
<div id="toc">
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main id="main_content_wrapper">
|
||||
{% 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>
|
||||
</div>
|
||||
|
||||
<footer id="main_footer">
|
||||
{% block main_footer %}
|
||||
<span> test? </span>
|
||||
{% endblock %}
|
||||
</footer>
|
||||
|
||||
<script> tracker.reloadHeadings(); </script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
80
www/templates/search.html
Normal file
80
www/templates/search.html
Normal file
|
@ -0,0 +1,80 @@
|
|||
|
||||
|
||||
{% extends "pathed_content.html" %}
|
||||
|
||||
{% block second_title %}
|
||||
<h2> Dergen Search </h2>
|
||||
{% endblock %}
|
||||
|
||||
{%block content_article %}
|
||||
<form class="dergen_search_form" method="get" enctype="multipart/form-data" action="/search">
|
||||
<h1>
|
||||
Dergle
|
||||
</h1>
|
||||
|
||||
<input type="text" id="query" autocomplete="on" name="query" placeholder="Search for..."
|
||||
{% if search_query.user_type %}
|
||||
value="{{search_query.user_input}}"
|
||||
{% endif %}
|
||||
/> <br>
|
||||
|
||||
<button type="submit">Search!</button>
|
||||
<button>I'm feeling cuddly</button>
|
||||
|
||||
<div class="_search_type">
|
||||
{% for type in search_query.types %}
|
||||
<input type="radio" name="search_type"
|
||||
id="type_{{type[1]}}" value="{{type[1]}}"
|
||||
onchange="this.form.submit();"
|
||||
{% if type[1] == search_query.user_type %}
|
||||
checked
|
||||
{% endif %}
|
||||
>
|
||||
<label for="type_{{type[1]}}">
|
||||
{{type[0]}}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if display_type == 'no_search' %}
|
||||
<h3>Search for something :D</h3>
|
||||
<ul>
|
||||
<li>Type normal words to search for post title, brief descriptions and main content</li>
|
||||
<li>Use the syntax <code>tags:tag_a,tag_b,...</code> to search for specific tags</li>
|
||||
<li>Use the type selector to more precisely look for images, blogs, etc.</li>
|
||||
</ul>
|
||||
|
||||
{% elseif display_type == 'image' %}
|
||||
<ul class="gallery">
|
||||
{% for post in search_results %}
|
||||
<li class="entry">
|
||||
<a href={{post.path}}>
|
||||
<figure>
|
||||
<img src="{{post.media_preview_url}}">
|
||||
<figcaption>
|
||||
{{ post.title }} ({{ post.search_score }})
|
||||
</figcaption>
|
||||
</figure>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{%elseif display_type == 'no results' %}
|
||||
<h4>How sad. There are no images yet... What a real shame :c </h4>
|
||||
{%else%}
|
||||
<ul class="dergen_search_result_listing">
|
||||
{% for post in search_results %}
|
||||
<li>
|
||||
<ul class="_details">
|
||||
<li class="_path_details"> <span>Score: {{post.search_score|round(2)}}</span> <span>Path: {{post.path}}</span> </li>
|
||||
<li class="_title"> <a href="{{post.url}}" target="_blank">
|
||||
{{post.title}} </a>
|
||||
</li>
|
||||
<li class="_brief"> {{post.brief}} </li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{%endblock%}
|
|
@ -8,10 +8,10 @@
|
|||
|
||||
{%block main_content%}
|
||||
<article>
|
||||
<form method="post" enctype="multipart/form-data" action="/api/admin/upload">
|
||||
<input type="text" id="post_path" name="post_path"/>
|
||||
<input type="password" id="api_key" name="api_key"/>
|
||||
<input type="file" id="post_data" name="post_data"/>
|
||||
<form method="post" enctype="multipart/form-data" action="/api/upload">
|
||||
<input type="text" id="path" name="path"/>
|
||||
<input type="password" id="ACCESS_KEY" name="ACCESS_KEY"/>
|
||||
<input type="file" id="file" name="file"/>
|
||||
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue