diff --git a/.forgejo/workflows/phplint.yaml b/.forgejo/workflows/lint.yaml similarity index 100% rename from .forgejo/workflows/phplint.yaml rename to .forgejo/workflows/lint.yaml diff --git a/.gitignore b/.gitignore index 57872d0..0f521b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /vendor/ + +sftp.json \ No newline at end of file diff --git a/.htaccess b/.htaccess deleted file mode 100644 index 094dd6c..0000000 --- a/.htaccess +++ /dev/null @@ -1,6 +0,0 @@ - -RewriteEngine On -RewriteBase / - -RewriteCond %{REQUEST_URI} !^/?static/.* -RewriteRule (.*) router.php diff --git a/composer.json b/composer.json deleted file mode 100644 index 6130261..0000000 --- a/composer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "require": { - "twig/twig": "^3.0", - "twig/markdown-extra": "^3.6", - "league/commonmark": "^2.4" - } -} diff --git a/docker_dev/Dockerfile b/docker_dev/Dockerfile index 60a6a66..6440854 100644 --- a/docker_dev/Dockerfile +++ b/docker_dev/Dockerfile @@ -1,7 +1,7 @@ FROM composer WORKDIR /app -COPY composer.* . +COPY www/composer.* . RUN composer install FROM php:apache @@ -12,6 +12,9 @@ RUN chmod -R a+r ./vendor RUN a2enmod rewrite RUN a2enmod headers +RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli -COPY . . -RUN chmod -R a+r $(ls -I vendor) +RUN mkdir raw + +COPY www/ . +RUN chmod -R a+rw $(ls -I vendor) diff --git a/docker_dev/MysqlDockerfile b/docker_dev/MysqlDockerfile new file mode 100644 index 0000000..be3c855 --- /dev/null +++ b/docker_dev/MysqlDockerfile @@ -0,0 +1,4 @@ +FROM mysql:8.0-debian + +WORKDIR /docker-entrypoint-initdb.d +COPY mysql_schema.sql ./ \ No newline at end of file diff --git a/docker_dev/compose.yaml b/docker_dev/compose.yaml index 5e8c99b..1b9493d 100644 --- a/docker_dev/compose.yaml +++ b/docker_dev/compose.yaml @@ -5,22 +5,44 @@ services: dockerfile: docker_dev/Dockerfile ports: - 8081:80 + environment: + MYSQL_USER: root + MYSQL_PASSWORD: example + MYSQL_DATABASE: dragon_fire + MYSQL_HOST: mysql + MYSQL_PORT: 3306 develop: watch: - path: ./ action: rebuild - - path: ../. + - path: ../www/composer.* + action: rebuild + - path: ../www/ action: sync - target: /usr/local/apache2/htdocs/ + target: /var/www/html + ignore: + - ../.git + - mysql_schema.sql + volumes: + - website_datavolume:/var/www/html/raw + mysql: - image: mysql:8.0-debian + build: + dockerfile: MysqlDockerfile # NOTE: use of "mysql_native_password" is not recommended: https://dev.mysql.com/doc/refman/8.0/en/upgrading-from-previous-series.html#upgrade-caching-sha2-password # (this is just an example, not intended to be a production configuration) command: --default-authentication-plugin=mysql_native_password restart: always environment: MYSQL_ROOT_PASSWORD: example + ports: + - 3306:3306 + develop: + watch: + - path: mysql_schema.sql + action: rebuild volumes: - sqlvolume:/var/lib/mysql volumes: sqlvolume: {} + website_datavolume: {} diff --git a/docker_dev/mysql_schema.sql b/docker_dev/mysql_schema.sql new file mode 100644 index 0000000..baf0a58 --- /dev/null +++ b/docker_dev/mysql_schema.sql @@ -0,0 +1,148 @@ + +CREATE DATABASE dragon_fire; + +USE dragon_fire; + +CREATE TABLE posts ( + post_id INTEGER AUTO_INCREMENT, + + host VARCHAR(64) NOT NULL, + post_path VARCHAR(255) NOT NULL, + post_path_depth INTEGER NOT NULL DEFAULT 0, + + post_create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + post_update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + + post_access_count INTEGER DEFAULT 0, + + post_metadata JSON NOT NULL, + post_settings_cache JSON DEFAULT NULL, + + post_content MEDIUMTEXT, + + PRIMARY KEY(post_id), + CONSTRAINT unique_post UNIQUE (host, post_path), + + INDEX(host, post_path), + INDEX(post_path_depth, post_path), + INDEX(post_create_time), + INDEX(post_update_time) +); + +CREATE TABLE path_access_counts ( + access_time DATETIME NOT NULL, + host VARCHAR(64) NOT NULL, + post_path VARCHAR(255), + agent VARCHAR(255), + referrer VARCHAR(255), + + path_access_count INTEGER DEFAULT 0, + path_processing_time DOUBLE PRECISION DEFAULT 0, + + PRIMARY KEY(access_time, host, post_path, agent, referrer) +); + +CREATE TABLE feed_cache ( + host VARCHAR(64) NOT NULL, + search_path VARCHAR(255), + export_type VARCHAR(255), + + feed_created_on DATETIME DEFAULT CURRENT_TIMESTAMP, + + feed_content MEDIUMTEXT, + + PRIMARY KEY(host, search_path, export_type) +); + +INSERT INTO posts (post_path, post_path_depth, post_metadata, post_content) +VALUES ( + '/about', + 0, +' +{ + "tags": ["test", "test2", "hellorld"], + "brief": "This is a simple test indeed", + "type": "text/markdown", + "title": "About the dergen" +} +', +' +# About the dergs indeed + +This is just a simple test. Might be nice, though! +' +), ( + '/about/neira', + 1, +' +{ + "tags": ["test", "test2", "hellorld", "neira"], + "brief": "This is a soft grab of Neira", + "type": "text/markdown", + "title": "About her" +} +', +' +# Nothing here yet! + +Sorry for this. She is working hard :> +' +), ( + '/about/xasin', + 1, +' +{ + "tags": ["test", "test2", "hellorld", "xasin"], + "brief": "This is a soft grab of Xasin", + "type": "text/markdown", + "title": "About her" +} +', +' +# Nothing here yet! + +Sorry for this. He is working hard :> +' +), ( + '/about/mesh', + 1, +' +{ + "tags": ["test", "test2", "hellorld", "mesh"], + "brief": "This is a soft grab of Mesh", + "type": "text/markdown", + "title": "About her" +} +', +' +# Nothing here yet! + +Sorry for this. Shi is working hard :> +' +), ( + '/about/alviere', + 1, +' +{ + "tags": ["test", "test2", "hellorld", "mesh"], + "brief": "SHE GRABS", + "type": "text/markdown", + "title": "SHE GRABS" +} +', +' +# Nothing here yet! + +Sorry for this. She GRABS A LOT + +---- + +## And now, for the lorem: + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Eleifend mi in nulla posuere sollicitudin aliquam ultrices sagittis orci. Risus commodo viverra maecenas accumsan lacus vel facilisis. Sed viverra tellus in hac habitasse. Nulla malesuada pellentesque elit eget gravida cum. Posuere sollicitudin aliquam ultrices sagittis orci a. Libero nunc consequat interdum varius sit amet. Bibendum arcu vitae elementum curabitur vitae nunc sed velit. Amet mauris commodo quis imperdiet massa tincidunt nunc pulvinar. Sed adipiscing diam donec adipiscing. Laoreet id donec ultrices tincidunt arcu non sodales. Id semper risus in hendrerit gravida rutrum quisque non. Ut venenatis tellus in metus vulputate eu. + +Risus sed vulputate odio ut enim blandit volutpat. Placerat in egestas erat imperdiet. Non curabitur gravida arcu ac tortor dignissim convallis aenean. Neque aliquam vestibulum morbi blandit cursus risus at. Elementum integer enim neque volutpat ac tincidunt vitae semper. Eu ultrices vitae auctor eu augue ut. In mollis nunc sed id semper risus in hendrerit gravida. Lectus arcu bibendum at varius vel pharetra vel turpis nunc. In pellentesque massa placerat duis. Non quam lacus suspendisse faucibus. Vitae aliquet nec ullamcorper sit amet risus nullam. Accumsan lacus vel facilisis volutpat est velit egestas dui. + +Risus feugiat in ante metus dictum at tempor commodo. Duis ut diam quam nulla. Nunc aliquet bibendum enim facilisis gravida neque convallis. Tincidunt augue interdum velit euismod in pellentesque. Praesent semper feugiat nibh sed pulvinar proin gravida hendrerit lectus. Non odio euismod lacinia at quis risus sed vulputate odio. Nunc sed blandit libero volutpat sed cras ornare arcu. Adipiscing enim eu turpis egestas pretium aenean pharetra magna. Ut tristique et egestas quis ipsum suspendisse. Blandit cursus risus at ultrices mi tempus imperdiet nulla malesuada. +' +); \ No newline at end of file diff --git a/router.php b/router.php deleted file mode 100644 index 9e2c99d..0000000 --- a/router.php +++ /dev/null @@ -1,43 +0,0 @@ - true]); -$twig->addExtension(new Twig\Extra\Markdown\MarkdownExtension()); - -use Twig\Extra\Markdown\DefaultMarkdown; -use Twig\Extra\Markdown\MarkdownRuntime; -use Twig\RuntimeLoader\RuntimeLoaderInterface; - -$twig->addRuntimeLoader(new class implements RuntimeLoaderInterface { - public function load($class) { - if (MarkdownRuntime::class === $class) { - return new MarkdownRuntime(new DefaultMarkdown()); - } - } -}); - -if($_SERVER['REQUEST_URI'] == '/') { - echo $twig->render('root.html'); -} elseif(preg_match('/^\/about(.html)?$/', $_SERVER['REQUEST_URI'])) { - echo $twig->render('about.html'); -} elseif(preg_match('/^\/gallery\/([^\?]+)/', $_SERVER['REQUEST_URI'])) { - echo $twig->render('/gallery/gallery_entry.html', [ - 'image_url' => '/static/banner/0.png', - 'image_title' => 'Test!', - 'image_desc' => 'A soft piece made by a dear friend', - 'artist_name' => 'Doggonaut', - 'artist_src_link' => 'https://twitter.com/doggonaut' - ]); -} else { - echo $twig->render('rrror.html',[ - "error_code" => '404 Hoard not found!', - "error_description" => "Well, we searched - far and wide for `" . $_SERVER['REQUEST_URI'] . "` but - somehow it must have gotten lost... Sorry!" - ]); -} - -?> diff --git a/scripts/upload_file.sh b/scripts/upload_file.sh new file mode 100755 index 0000000..587ceb9 --- /dev/null +++ b/scripts/upload_file.sh @@ -0,0 +1,15 @@ + +CURL_URL=${DERG_UPLOAD_URL:-https://lucidragons.de/api/admin/upload} +CURL_KEY=${DERG_UPLOAD_KEY:-SoftDragonKeys} + +for FILE_PATH in "$@" +do + if [ -f "${FILE_PATH}" ]; then + POST_PATH="/${FILE_PATH#"./"}" + + echo "Uploading ${FILE_PATH} to ${POST_PATH}" + + curl -i -X POST -H "Content-Type: multipart/form-data" \ + -F "api_key=${CURL_KEY}" -F "post_path=${POST_PATH}" -F "post_data=@${FILE_PATH}" "${CURL_URL}" + fi +done \ No newline at end of file diff --git a/static/banner.js b/static/banner.js deleted file mode 100644 index 256d683..0000000 --- a/static/banner.js +++ /dev/null @@ -1,69 +0,0 @@ - -const banner_show_time = 600 * 1000.0 -const banner_animated_style = "opacity 0.8s linear, transform 0.1s linear" - -var banner_current_src = localStorage.getItem('main_banner_img') - -function getBannerTime() { - return (new Date()).getTime() / banner_show_time -} -function getBannerSrc() { - return "/static/banner/" + Math.floor(getBannerTime() + 1000/banner_show_time) % 2 + ".png" -} -function update_banner_top(banner, banner_container) { - const banner_top_max = 0 - const banner_top_min = -banner.clientHeight + banner_container.clientHeight - - const banner_top = (1-(getBannerTime()%1)) * banner_top_min - banner.style.transform = "translateY(" + banner_top + 'px' + ")" -} - -let banner_update_src = banner_current_src -function update_banner(banner, banner_container) { - - image_select = getBannerSrc() - - update_banner_top(banner, banner_container) - - if(image_select != banner_update_src) { - banner.style.opacity = 0 - - setTimeout(() => { - banner.src = image_select - }, 1000) - - banner_update_src = image_select - localStorage.setItem('main_banner_img', image_select) - - document.getElementById("main_banner_img_link").href = "/gallery/test" - } -} - -const banner_container = document.getElementById("main_header") -const banner = document.getElementById("main_banner_img") - - -banner.addEventListener('load', () => { - update_banner_top(banner, banner_container) - - const next_banner_src = getBannerSrc() - - if(banner_current_src != next_banner_src) { - banner.style.transition = banner_animated_style - setTimeout(() => banner.style.opacity = 0.3, 1000) - } - else { - banner.style.opacity = 0.3 - setTimeout(() => banner.style.transition = banner_animated_style, 0) - } - - banner_current_src = next_banner_src -}) - -document.addEventListener("DOMContentLoaded", function () { - banner.src = getBannerSrc() - document.getElementById("main_banner_img_link").href = "/gallery/test" -}) - -setInterval(() => update_banner(banner, banner_container), 100) -addEventListener("resize", () => update_banner(banner, banner_container)); diff --git a/static/dergstyle.css b/static/dergstyle.css deleted file mode 100644 index 9bf4f29..0000000 --- a/static/dergstyle.css +++ /dev/null @@ -1,134 +0,0 @@ - -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - color: #B0B0B0; - background: #201c2a; - margin: 0px; - - position: relative; - - min-height: 100vh; - padding-bottom: 4em; -} - -:link { - color: cyan; - font-style: italic; - text-decoration: none; - - transition: color 0.2s; -} -a:visited { - color: cyan; -} -a:hover { - color: lightblue; -} - -#main_header { - overflow: hidden; - position: relative; - - padding-bottom: 0.7em; -} -#main_banner_img { - position: absolute; - left: 0px; - right: 0px; - - width: 100vw; - - z-index: -1; - - opacity: 0; - top: 0px; -} -#main_banner_img_link { - position: absolute; - right: 2vw; - bottom: 0.5em; - - font-size: 0.8em; -} - -#nav_bar { - display: flex; - flex-direction: row; - justify-content: center; - - list-style-type: none; - margin-top: 1em; - padding: 0px; -} -#nav_bar li { - padding: 0em 0.3em 0em 0.3em; -} - -#big_title { - text-align: center; - font-size: 2.5em; - margin-bottom: 0.2em; -} -#main_header h2 { - text-align: center; - font-size: 2em; - margin-bottom: 0.2em; -} - -#title_separator { - height: 1.5px; - background-color: #ddd; - opacity: 0.5; - margin-left: 2em; - margin-right: 2em; -} - -#main_content_wrapper { - padding: 3vmin 1em 1em 1em; - width: auto; - margin-left: max(0.75em, min(max(20vmin, 50vw - 30rem), 50vw - 25rem)); - margin-right: max(0.75em, min(max(20vmin, 50vw - 30rem), 50vw - 25rem)); - - min-height: 100%; - - background: #3e355479; -} - -#main_content_wrapper article { - background: #3e3554; - color: #c6c3c3; - border-radius: 0.3em; - box-shadow: 3px 7px 7px 0px #00000040; - - padding: 0.75em; -} -#main_content_wrapper article h1 { - text-align: left; - padding-left: 3vmin; - margin-bottom: 0.2em; - - border-bottom: solid 1px darkgrey; -} - -#main_footer { - display: flex; - - height: 2.5em; - text-align: center; - background-color: #3a3a3a; - margin: 0px; - - position: absolute; - bottom: 0px; - width: 100%; -} - -#main_footer span { - align-self: flex-end; - width: 100%; -} diff --git a/static/rrrorstyle.css b/static/rrrorstyle.css deleted file mode 100644 index c9f4de8..0000000 --- a/static/rrrorstyle.css +++ /dev/null @@ -1,13 +0,0 @@ - - -#rrr_header { - font-size: 2em; - - border-bottom: 1px solid grey; - padding-bottom: 0.2em; - margin-bottom: 0.3em; -} - -#rrr_code { - font-size: 1.5em; -} \ No newline at end of file diff --git a/templates/about.html b/templates/about.html deleted file mode 100644 index 16352aa..0000000 --- a/templates/about.html +++ /dev/null @@ -1,13 +0,0 @@ - - -{% extends "root.html" %} - -{% block second_title %} -

Abouts

-{% endblock %} - -{%block main_content%} -
- {{ include('about.md')|markdown_to_html }} -
-{%endblock%} diff --git a/templates/index.html b/templates/index.html deleted file mode 100644 index 50f9c93..0000000 --- a/templates/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - Lucidragons' Fire - - - -

The dergsite

-
- - - -
- {% block content %}

Soon there shall be content!

{% endblock %} -
- - - - - diff --git a/templates/root.dergplate b/templates/root.dergplate deleted file mode 100644 index fa4e62b..0000000 --- a/templates/root.dergplate +++ /dev/null @@ -1,18 +0,0 @@ - - - PHP Test - - - Hello World

'; - - require 'src/templater.php'; - - $test = new TemplateFillout('test.dergplate'); - - $test->render(); - - ?> - - diff --git a/templates/root.html b/templates/root.html deleted file mode 100644 index 0bb44f5..0000000 --- a/templates/root.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - Lucidragons' Fire - - - - - - {% block extra_head %}{% endblock %} - - - - -
- - full picture - -

{% block big_title %}The dergsite{%endblock%}

- {% block second_title %}{% endblock %} -
- - -
  • About
  • -
  • Blog
  • -
  • Projects
  • -
  • Artworks
  • -
    -
    - -
    - {% block main_content %}

    Soon there shall be content!

    {% endblock %} -
    - - - - - diff --git a/templates/rrror.html b/templates/rrror.html deleted file mode 100644 index f9f3ff9..0000000 --- a/templates/rrror.html +++ /dev/null @@ -1,21 +0,0 @@ - -{% extends "root.html" %} - -{% block extra_head %} - -{% endblock %} - -{% block second_title %} -

    (Broken)

    -{% endblock %} - -{% block main_content %} -
    -

    The Dergs are confused:

    -

    {{ error_code }}

    - -
    - {{ error_description|markdown_to_html }} -
    -
    -{% endblock %} \ No newline at end of file diff --git a/test_entries/README.md b/test_entries/README.md new file mode 100644 index 0000000..01c17d1 --- /dev/null +++ b/test_entries/README.md @@ -0,0 +1,8 @@ +--- +settings: + colourscheme: fun + post_style: generic + banners: + - src: /banner/0.png + - src: /banner/1.png +--- \ No newline at end of file diff --git a/test_entries/about/README.md b/test_entries/about/README.md new file mode 100644 index 0000000..8fd8ceb --- /dev/null +++ b/test_entries/about/README.md @@ -0,0 +1,11 @@ +--- +tags: [what] +directory_data: + type: text/markdown +--- + +# The dergens + +The + +yes \ No newline at end of file diff --git a/test_entries/about/neira.md b/test_entries/about/neira.md new file mode 100644 index 0000000..aedd371 --- /dev/null +++ b/test_entries/about/neira.md @@ -0,0 +1,8 @@ +--- +--- + +# About her + + +This is some test content, to simulate a little markdown page for... Well, our website. +I wouldn't worry too much about it. Just know it's here :) \ No newline at end of file diff --git a/static/banner/1.png b/test_entries/about/neira/1.png similarity index 100% rename from static/banner/1.png rename to test_entries/about/neira/1.png diff --git a/test_entries/about/neira/1.png.mddesc b/test_entries/about/neira/1.png.mddesc new file mode 100644 index 0000000..b04743e --- /dev/null +++ b/test_entries/about/neira/1.png.mddesc @@ -0,0 +1,8 @@ +--- +title: A cuddly image <3 +author: Shaky // Doggonaut +--- + +# Cuddly dragons + +A dear picture made by a dear friend. Shaky - we hope you are OK. \ No newline at end of file diff --git a/test_entries/about/neira/Neira_Queen.png b/test_entries/about/neira/Neira_Queen.png new file mode 100644 index 0000000..078fba6 Binary files /dev/null and b/test_entries/about/neira/Neira_Queen.png differ diff --git a/test_entries/about/neira/README.md b/test_entries/about/neira/README.md new file mode 100644 index 0000000..9faac34 --- /dev/null +++ b/test_entries/about/neira/README.md @@ -0,0 +1,10 @@ +--- +directory: + settings: + banners: + - src: /about/neira/Neira_Queen.png + from: 0.5 + to: 0.95 +--- + +# She is soft~<3 \ No newline at end of file diff --git a/test_entries/about/neira/really_long_pathname_oh_god/README.md b/test_entries/about/neira/really_long_pathname_oh_god/README.md new file mode 100644 index 0000000..0571644 --- /dev/null +++ b/test_entries/about/neira/really_long_pathname_oh_god/README.md @@ -0,0 +1,5 @@ +--- +title: Oh, the pain +--- + +# AAA \ No newline at end of file diff --git a/test_entries/about/neira/subtest.md b/test_entries/about/neira/subtest.md new file mode 100644 index 0000000..51f4c64 --- /dev/null +++ b/test_entries/about/neira/subtest.md @@ -0,0 +1,9 @@ +--- +tags: + - neira + - comfy + - testing +title: Subtest, it's nice :> +--- + +## Subtests, because it's nice \ No newline at end of file diff --git a/test_entries/about/neira/test_me_readme.md b/test_entries/about/neira/test_me_readme.md new file mode 100644 index 0000000..7b9fd95 --- /dev/null +++ b/test_entries/about/neira/test_me_readme.md @@ -0,0 +1,12 @@ +--- +title: A little image test idea +--- + +# README concept + +This file is just to show the README concept - it's its own file but will be +rendered under a directory listing :) + +There's also a test for an image! Let's hope that works: + +![A very cute image :>](1.png) \ No newline at end of file diff --git a/static/banner/0.png b/test_entries/banner/0.png similarity index 100% rename from static/banner/0.png rename to test_entries/banner/0.png diff --git a/static/banner/banner0.jpeg b/test_entries/banner/banner0.jpeg similarity index 100% rename from static/banner/banner0.jpeg rename to test_entries/banner/banner0.jpeg diff --git a/test_entries/test/the/images/1.png b/test_entries/test/the/images/1.png new file mode 100644 index 0000000..6e5069f Binary files /dev/null and b/test_entries/test/the/images/1.png differ diff --git a/test_entries/test/the/images/1.png.mddesc b/test_entries/test/the/images/1.png.mddesc new file mode 100644 index 0000000..b04743e --- /dev/null +++ b/test_entries/test/the/images/1.png.mddesc @@ -0,0 +1,8 @@ +--- +title: A cuddly image <3 +author: Shaky // Doggonaut +--- + +# Cuddly dragons + +A dear picture made by a dear friend. Shaky - we hope you are OK. \ No newline at end of file diff --git a/test_entries/test/the/images/README.md b/test_entries/test/the/images/README.md new file mode 100644 index 0000000..c92c2dd --- /dev/null +++ b/test_entries/test/the/images/README.md @@ -0,0 +1,17 @@ +--- +title: A little image test idea +directory: + settings: + colourscheme: spicy + banners: + - src: /about/neira/Neira_Queen.png +--- + +# README concept + +This file is just to show the README concept - it's its own file but will be +rendered under a directory listing :) + +There's also a test for an image! Let's hope that works: + +![A very cute image :>](1.png) \ No newline at end of file diff --git a/user_content/about.md b/user_content/about.md deleted file mode 100644 index b83b2e5..0000000 --- a/user_content/about.md +++ /dev/null @@ -1,15 +0,0 @@ -# About the dergens - -### Who we are -Just little things, but it does feel good. - - -No spacing, huh... - -### What we like to do - -nom Nom Nom test? -Though I don't understand why this is not quite functional... - - -Markdown truly is a delight to work with! \ No newline at end of file diff --git a/www/.gitignore b/www/.gitignore new file mode 100644 index 0000000..a725465 --- /dev/null +++ b/www/.gitignore @@ -0,0 +1 @@ +vendor/ \ No newline at end of file diff --git a/www/.htaccess b/www/.htaccess index 605d2f4..eb86f63 100644 --- a/www/.htaccess +++ b/www/.htaccess @@ -1 +1,34 @@ + +AddType text/plain .md +AddType text/plain .atom +AddType text/plain .rss + +# php_value upload_max_filesize 40M +# php_value post_max_size 42M + +RewriteEngine On +RewriteBase / + +RewriteCond %{REQUEST_URI} !^/(raw|static)/ +RewriteRule ^.*\.(flv|gif|ico|jpg|jpeg|mp4|mpeg|png|svg|swf|webp)$ raw/%{HTTP_HOST}%{REQUEST_URI} [L,END] + +RewriteRule ^/?raw/(.*)$ raw/%{HTTP_HOST}/$1 [L,END] + +RewriteEngine On +RewriteCond %{HTTPS} !on +RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,END] + +RewriteCond %{REQUEST_URI} !^/?(static|raw|robots\.txt).* +RewriteRule (.*) router.php + Allow from all + +Options +Indexes + + + Header set Cache-Control "max-age=315360, public" + + + + Header set Cache-Control "max-age=315360, public" + \ No newline at end of file diff --git a/www/composer.json b/www/composer.json new file mode 100644 index 0000000..4edcc55 --- /dev/null +++ b/www/composer.json @@ -0,0 +1,12 @@ +{ + "require": { + "twig/twig": "^3.0", + "twig/markdown-extra": "^3.6", + "league/commonmark": "^2.4", + "spatie/yaml-front-matter": "^2.0", + "laminas/laminas-feed": "^2.6", + "erusev/parsedown": "^1.7", + "erusev/parsedown-extra": "^0.8.1", + "scrivo/highlight.php": "v9.18.1.10" + } +} diff --git a/composer.lock b/www/composer.lock similarity index 51% rename from composer.lock rename to www/composer.lock index b8538ec..c157e3a 100644 --- a/composer.lock +++ b/www/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "785c1dec442cd0287ada9cb48a31d45c", + "content-hash": "34ef4bdab7508bbe6f60a1a56e33d08c", "packages": [ { "name": "dflydev/dot-access-data", @@ -82,17 +82,453 @@ "time": "2022-10-27T11:44:00+00:00" }, { - "name": "league/commonmark", - "version": "2.4.0", + "name": "erusev/parsedown", + "version": "1.7.4", "source": { "type": "git", - "url": "https://github.com/thephpleague/commonmark.git", - "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048" + "url": "https://github.com/erusev/parsedown.git", + "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d44a24690f16b8c1808bf13b1bd54ae4c63ea048", - "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35" + }, + "type": "library", + "autoload": { + "psr-0": { + "Parsedown": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", + "keywords": [ + "markdown", + "parser" + ], + "support": { + "issues": "https://github.com/erusev/parsedown/issues", + "source": "https://github.com/erusev/parsedown/tree/1.7.x" + }, + "time": "2019-12-30T22:54:17+00:00" + }, + { + "name": "erusev/parsedown-extra", + "version": "0.8.1", + "source": { + "type": "git", + "url": "https://github.com/erusev/parsedown-extra.git", + "reference": "91ac3ff98f0cea243bdccc688df43810f044dcef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/91ac3ff98f0cea243bdccc688df43810f044dcef", + "reference": "91ac3ff98f0cea243bdccc688df43810f044dcef", + "shasum": "" + }, + "require": { + "erusev/parsedown": "^1.7.4" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35" + }, + "type": "library", + "autoload": { + "psr-0": { + "ParsedownExtra": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "An extension of Parsedown that adds support for Markdown Extra.", + "homepage": "https://github.com/erusev/parsedown-extra", + "keywords": [ + "markdown", + "markdown extra", + "parsedown", + "parser" + ], + "support": { + "issues": "https://github.com/erusev/parsedown-extra/issues", + "source": "https://github.com/erusev/parsedown-extra/tree/0.8.x" + }, + "time": "2019-12-30T23:20:37+00:00" + }, + { + "name": "laminas/laminas-escaper", + "version": "2.13.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-escaper.git", + "reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/af459883f4018d0f8a0c69c7a209daef3bf973ba", + "reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-mbstring": "*", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + }, + "conflict": { + "zendframework/zend-escaper": "*" + }, + "require-dev": { + "infection/infection": "^0.27.0", + "laminas/laminas-coding-standard": "~2.5.0", + "maglnet/composer-require-checker": "^3.8.0", + "phpunit/phpunit": "^9.6.7", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Escaper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", + "homepage": "https://laminas.dev", + "keywords": [ + "escaper", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-escaper/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-escaper/issues", + "rss": "https://github.com/laminas/laminas-escaper/releases.atom", + "source": "https://github.com/laminas/laminas-escaper" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2023-10-10T08:35:13+00:00" + }, + { + "name": "laminas/laminas-feed", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-feed.git", + "reference": "c1594cb32b117d3b409d4beee12c724cb26daa71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/c1594cb32b117d3b409d4beee12c724cb26daa71", + "reference": "c1594cb32b117d3b409d4beee12c724cb26daa71", + "shasum": "" + }, + "require": { + "laminas/laminas-escaper": "~2.5", + "laminas/laminas-stdlib": "~2.5", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": ">=5.5" + }, + "replace": { + "zendframework/zend-feed": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-cache": "~2.5", + "laminas/laminas-db": "~2.5", + "laminas/laminas-http": "~2.5", + "laminas/laminas-validator": "~2.5", + "phpunit/phpunit": "~4.0", + "psr/http-message": "^1.0" + }, + "suggest": { + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating feeds and Atom entries in the Writer subcomponent", + "psr/http-message": "PSR-7 ^1.0, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Feed\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides functionality for consuming RSS and Atom feeds", + "homepage": "https://laminas.dev", + "keywords": [ + "feed", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-feed/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-feed/issues", + "rss": "https://github.com/laminas/laminas-feed/releases.atom", + "source": "https://github.com/laminas/laminas-feed" + }, + "time": "2019-12-31T16:50:31+00:00" + }, + { + "name": "laminas/laminas-hydrator", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-hydrator.git", + "reference": "acab29a3327a70be0a653d88906655b15de15517" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/acab29a3327a70be0a653d88906655b15de15517", + "reference": "acab29a3327a70be0a653d88906655b15de15517", + "shasum": "" + }, + "require": { + "laminas/laminas-stdlib": "^2.5.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": ">=5.5" + }, + "replace": { + "zendframework/zend-hydrator": "self.version" + }, + "require-dev": { + "laminas/laminas-eventmanager": "^2.5.1", + "laminas/laminas-filter": "^2.5.1", + "laminas/laminas-inputfilter": "^2.5.1", + "laminas/laminas-serializer": "^2.5.1", + "laminas/laminas-servicemanager": "^2.5.1", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "^2.0@dev" + }, + "suggest": { + "laminas/laminas-eventmanager": "^2.5.1, to support aggregate hydrator usage", + "laminas/laminas-filter": "^2.5.1, to support naming strategy hydrator usage", + "laminas/laminas-serializer": "^2.5.1, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^2.5.1, to support hydrator plugin manager usage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev", + "dev-develop": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Hydrator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://laminas.dev", + "keywords": [ + "hydrator", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-hydrator/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-hydrator/issues", + "rss": "https://github.com/laminas/laminas-hydrator/releases.atom", + "source": "https://github.com/laminas/laminas-hydrator" + }, + "time": "2019-12-31T17:06:20+00:00" + }, + { + "name": "laminas/laminas-stdlib", + "version": "2.7.4", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-stdlib.git", + "reference": "b69e2741673daabdf720bc76668448ceb1e4c537" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/b69e2741673daabdf720bc76668448ceb1e4c537", + "reference": "b69e2741673daabdf720bc76668448ceb1e4c537", + "shasum": "" + }, + "require": { + "laminas/laminas-hydrator": "~1.0", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": ">=5.5" + }, + "replace": { + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "athletic/athletic": "~0.1", + "fabpot/php-cs-fixer": "1.7.*", + "laminas/laminas-config": "~2.5", + "laminas/laminas-eventmanager": "~2.5", + "laminas/laminas-filter": "~2.5", + "laminas/laminas-inputfilter": "~2.5", + "laminas/laminas-serializer": "~2.5", + "laminas/laminas-servicemanager": "~2.5", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "laminas/laminas-eventmanager": "To support aggregate hydrator usage", + "laminas/laminas-filter": "To support naming strategy hydrator usage", + "laminas/laminas-serializer": "Laminas\\Serializer component", + "laminas/laminas-servicemanager": "To support hydrator plugin manager usage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "stdlib" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-stdlib/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-stdlib/issues", + "rss": "https://github.com/laminas/laminas-stdlib/releases.atom", + "source": "https://github.com/laminas/laminas-stdlib" + }, + "time": "2019-12-31T17:51:00+00:00" + }, + { + "name": "laminas/laminas-zendframework-bridge", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-zendframework-bridge.git", + "reference": "eb0d96c708b92177a92bc2239543d3ed523452c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/eb0d96c708b92177a92bc2239543d3ed523452c6", + "reference": "eb0d96c708b92177a92bc2239543d3ed523452c6", + "shasum": "" + }, + "require": { + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.4", + "psalm/plugin-phpunit": "^0.18.0", + "squizlabs/php_codesniffer": "^3.7.1", + "vimeo/psalm": "^5.16.0" + }, + "type": "library", + "extra": { + "laminas": { + "module": "Laminas\\ZendFrameworkBridge" + } + }, + "autoload": { + "files": [ + "src/autoload.php" + ], + "psr-4": { + "Laminas\\ZendFrameworkBridge\\": "src//" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Alias legacy ZF class names to Laminas Project equivalents.", + "keywords": [ + "ZendFramework", + "autoloading", + "laminas", + "zf" + ], + "support": { + "forum": "https://discourse.laminas.dev/", + "issues": "https://github.com/laminas/laminas-zendframework-bridge/issues", + "rss": "https://github.com/laminas/laminas-zendframework-bridge/releases.atom", + "source": "https://github.com/laminas/laminas-zendframework-bridge" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "abandoned": true, + "time": "2023-11-24T13:56:19+00:00" + }, + { + "name": "league/commonmark", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/3669d6d5f7a47a93c08ddff335e6d945481a1dd5", + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5", "shasum": "" }, "require": { @@ -185,7 +621,7 @@ "type": "tidelift" } ], - "time": "2023-03-24T15:16:10+00:00" + "time": "2023-08-30T16:55:00+00:00" }, { "name": "league/config", @@ -271,21 +707,21 @@ }, { "name": "nette/schema", - "version": "v1.2.3", + "version": "v1.2.5", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f" + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", + "url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a", + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a", "shasum": "" }, "require": { "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", - "php": ">=7.1 <8.3" + "php": "7.1 - 8.3" }, "require-dev": { "nette/tester": "^2.3 || ^2.4", @@ -327,35 +763,36 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.3" + "source": "https://github.com/nette/schema/tree/v1.2.5" }, - "time": "2022-10-13T01:24:26+00:00" + "time": "2023-10-05T20:37:59+00:00" }, { "name": "nette/utils", - "version": "v3.2.9", + "version": "v4.0.3", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "c91bac3470c34b2ecd5400f6e6fdf0b64a836a5c" + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/c91bac3470c34b2ecd5400f6e6fdf0b64a836a5c", - "reference": "c91bac3470c34b2ecd5400f6e6fdf0b64a836a5c", + "url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015", + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015", "shasum": "" }, "require": { - "php": ">=7.2 <8.3" + "php": ">=8.0 <8.4" }, "conflict": { - "nette/di": "<3.0.6" + "nette/finder": "<3", + "nette/schema": "<1.2.2" }, "require-dev": { "jetbrains/phpstorm-attributes": "dev-master", - "nette/tester": "~2.0", + "nette/tester": "^2.5", "phpstan/phpstan": "^1.0", - "tracy/tracy": "^2.3" + "tracy/tracy": "^2.9" }, "suggest": { "ext-gd": "to use Image", @@ -363,13 +800,12 @@ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", "ext-json": "to use Nette\\Utils\\Json", "ext-mbstring": "to use Strings::lower() etc...", - "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", - "ext-xml": "to use Strings::length() etc. when mbstring is not available" + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -413,9 +849,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v3.2.9" + "source": "https://github.com/nette/utils/tree/v4.0.3" }, - "time": "2023-01-18T03:26:20+00:00" + "time": "2023-10-29T21:02:13+00:00" }, { "name": "psr/event-dispatcher", @@ -468,26 +904,166 @@ "time": "2019-01-08T18:20:26+00:00" }, { - "name": "symfony/deprecation-contracts", - "version": "v2.5.2", + "name": "scrivo/highlight.php", + "version": "v9.18.1.10", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + "url": "https://github.com/scrivo/highlight.php.git", + "reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/850f4b44697a2552e892ffe71490ba2733c2fc6e", + "reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-json": "*", + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "^4.8|^5.7", + "sabberworm/php-css-parser": "^8.3", + "symfony/finder": "^2.8|^3.4|^5.4", + "symfony/var-dumper": "^2.8|^3.4|^5.4" + }, + "suggest": { + "ext-mbstring": "Allows highlighting code with unicode characters and supports language with unicode keywords" + }, + "type": "library", + "autoload": { + "files": [ + "HighlightUtilities/functions.php" + ], + "psr-0": { + "Highlight\\": "", + "HighlightUtilities\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Geert Bergman", + "homepage": "http://www.scrivo.org/", + "role": "Project Author" + }, + { + "name": "Vladimir Jimenez", + "homepage": "https://allejo.io", + "role": "Maintainer" + }, + { + "name": "Martin Folkers", + "homepage": "https://twobrain.io", + "role": "Contributor" + } + ], + "description": "Server side syntax highlighter that supports 185 languages. It's a PHP port of highlight.js", + "keywords": [ + "code", + "highlight", + "highlight.js", + "highlight.php", + "syntax" + ], + "support": { + "issues": "https://github.com/scrivo/highlight.php/issues", + "source": "https://github.com/scrivo/highlight.php" + }, + "funding": [ + { + "url": "https://github.com/allejo", + "type": "github" + } + ], + "time": "2022-12-17T21:53:22+00:00" + }, + { + "name": "spatie/yaml-front-matter", + "version": "2.0.8", + "source": { + "type": "git", + "url": "https://github.com/spatie/yaml-front-matter.git", + "reference": "f2f1f749a405fafc9d6337067c92c062d51a581c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/f2f1f749a405fafc9d6337067c92c062d51a581c", + "reference": "f2f1f749a405fafc9d6337067c92c062d51a581c", + "shasum": "" + }, + "require": { + "php": "^7.0|^8.0", + "symfony/yaml": "^3.0|^4.0|^5.0|^6.0|^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\YamlFrontMatter\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian De Deyne", + "email": "sebastian@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A to the point yaml front matter parser", + "homepage": "https://github.com/sebastiandedeyne/yaml-front-matter", + "keywords": [ + "front matter", + "jekyll", + "spatie", + "yaml" + ], + "support": { + "source": "https://github.com/spatie/yaml-front-matter/tree/2.0.8" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-12-04T10:02:52+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -516,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/v2.5.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" }, "funding": [ { @@ -532,20 +1108,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -560,7 +1136,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -598,7 +1174,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" }, "funding": [ { @@ -614,20 +1190,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -642,7 +1218,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -681,7 +1257,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -697,20 +1273,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", "shasum": "" }, "require": { @@ -719,7 +1295,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -764,7 +1340,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" }, "funding": [ { @@ -780,32 +1356,103 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { - "name": "twig/markdown-extra", - "version": "v3.6.0", + "name": "symfony/yaml", + "version": "v7.0.0", "source": { "type": "git", - "url": "https://github.com/twigphp/markdown-extra.git", - "reference": "8f1179e279cea6ef14066a4560b859df58acd5d8" + "url": "https://github.com/symfony/yaml.git", + "reference": "0055b230c408428b9b5cde7c55659555be5c0278" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/8f1179e279cea6ef14066a4560b859df58acd5d8", - "reference": "8f1179e279cea6ef14066a4560b859df58acd5d8", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0055b230c408428b9b5cde7c55659555be5c0278", + "reference": "0055b230c408428b9b5cde7c55659555be5c0278", "shasum": "" }, "require": { - "php": ">=7.1.3", - "twig/twig": "^2.7|^3.0" + "php": ">=8.2", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.0.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-07T10:26:03+00:00" + }, + { + "name": "twig/markdown-extra", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/markdown-extra.git", + "reference": "b6e4954ab60030233df5d293886b5404558daac8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/b6e4954ab60030233df5d293886b5404558daac8", + "reference": "b6e4954ab60030233df5d293886b5404558daac8", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "twig/twig": "^3.0" }, "require-dev": { "erusev/parsedown": "^1.7", "league/commonmark": "^1.0|^2.0", "league/html-to-markdown": "^4.8|^5.0", "michelf/php-markdown": "^1.8|^2.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "symfony/phpunit-bridge": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -836,7 +1483,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/markdown-extra/tree/v3.6.0" + "source": "https://github.com/twigphp/markdown-extra/tree/v3.8.0" }, "funding": [ { @@ -848,30 +1495,31 @@ "type": "tidelift" } ], - "time": "2023-02-09T06:45:16+00:00" + "time": "2023-11-21T14:02:01+00:00" }, { "name": "twig/twig", - "version": "v3.6.0", + "version": "v3.8.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "106c170d08e8415d78be2d16c3d057d0d108262b" + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/106c170d08e8415d78be2d16c3d057d0d108262b", - "reference": "106c170d08e8415d78be2d16c3d057d0d108262b", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" }, "require-dev": { "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" }, "type": "library", "autoload": { @@ -907,7 +1555,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.6.0" + "source": "https://github.com/twigphp/Twig/tree/v3.8.0" }, "funding": [ { @@ -919,7 +1567,7 @@ "type": "tidelift" } ], - "time": "2023-05-03T19:06:57+00:00" + "time": "2023-11-21T18:54:41+00:00" } ], "packages-dev": [], diff --git a/www/dergdown.php b/www/dergdown.php new file mode 100644 index 0000000..8380be7 --- /dev/null +++ b/www/dergdown.php @@ -0,0 +1,39 @@ + + +highlighter = new Highlighter(); + } + + protected function blockFencedCodeComplete($block) + { + if (! isset($block['element']['text']['attributes'])) { + return $block; + } + + $code = $block['element']['text']['text']; + $languageClass = $block['element']['text']['attributes']['class']; + $language = explode('-', $languageClass); + + try { + $highlighted = $this->highlighter->highlight($language[1], $code); + $block['element']['text']['attributes']['class'] = vsprintf('%s hljs %s', [ + $languageClass, + $highlighted->language, + ]); + $block['element']['text']['rawHtml'] = $highlighted->value; + unset($block['element']['text']['text']); + } catch (DomainException $e) { + } + + return $block; + } +} \ No newline at end of file diff --git a/www/dergstyle.css b/www/dergstyle.css deleted file mode 100644 index 1c31397..0000000 --- a/www/dergstyle.css +++ /dev/null @@ -1,42 +0,0 @@ - -body { - color: #B0B0B0; - background: #302A3F; -} - -:link { - color: cyan; - font-style: italic; - text-decoration: none; - - transition: color 0.2s; -} -:link:hover { - color: lightblue; -} - -ul { - display: flex; - flex-direction: row; - justify-content: center; - - list-style-type: none; - margin-top: 1em; - padding: 0px; -} -ul li { - padding: 0em 0.3em 0em 0.3em; -} - -#big_title { - text-align: center; - font-size: 2.5em; - margin-bottom: 0.2em; -} -#title_separator { - height: 1.5px; - background-color: #ddd; - opacity: 0.5; - margin-left: 2em; - margin-right: 2em; -} diff --git a/www/fontawesome.php b/www/fontawesome.php new file mode 100644 index 0000000..91b82d5 --- /dev/null +++ b/www/fontawesome.php @@ -0,0 +1,11 @@ + '', + 'image' => '', + 'images' => '', + 'folder' => '', + 'rss' => '' +]; + +?> \ No newline at end of file diff --git a/www/index.php b/www/index.php deleted file mode 100644 index 6084dcf..0000000 --- a/www/index.php +++ /dev/null @@ -1,11 +0,0 @@ -render('index.html',[ - "a_variable" => "is very spicy. In a good way." -]); -?> diff --git a/www/mysql_adapter.php b/www/mysql_adapter.php new file mode 100644 index 0000000..3d845b6 --- /dev/null +++ b/www/mysql_adapter.php @@ -0,0 +1,431 @@ +SITE_CONFIG = $SITE_CONFIG; + + $db_params = $SITE_CONFIG['db']; + + try { + if(false !== getenv('MYSQL_HOST')) { + $this->raw = mysqli_connect(getenv('MYSQL_HOST'), + getenv('MYSQL_USER'), getenv('MYSQL_PASSWORD'), + getenv('MYSQL_DATABASE'), + getenv('MYSQL_PORT')); + } + else { + $this->raw = mysqli_connect($db_params['host'], + $db_params['user'], $db_params['password'], + $db_params['database'], + $db_params['port']); + } + } catch (\Throwable $th) { + echo 'Connection failed
    '; + echo 'Error number: ' . mysqli_connect_errno() . '
    '; + echo 'Error message: ' . mysqli_connect_error() . '
    '; + die(); + } + } + + function _sanitize_path($post_path) { + $post_path = chop($post_path, '/'); + + if($post_path == "") { + return ""; + } + + if(!preg_match('/^(?:\/[\w-]+)+(?:\.[\w-]+)*$/', $post_path)) { + echo "Post path match against " . $post_path . " failed!"; + die(); + } + + return $post_path; + } + + function _exec($qery, $argtypes = '', ...$args) { + $stmt = $this->raw->prepare($qery); + if($argtypes != ""){ + $stmt->bind_param($argtypes, ...$args); + } + $stmt->execute(); + + return $stmt->get_result(); + } + + function _normalize_post_data($post_data) { + $post_data ??= ['found' => null]; + + if(isset($post_data['found']) && $post_data['found'] == false) { + return $post_data; + } + + $post_data["found"] = true; + + $post_data['post_metadata'] = json_decode($post_data["post_metadata"], true) ?? []; + $post_data["post_content"] ??= ''; + + return $post_data; + } + + function _normalize_post_array($post_data) { + $post_data ??= []; + + return array_map(function($post) { + return $this->_normalize_post_data($post); + }, $post_data); + } + + function bump_post($post_path, $post_metadata = [], $create_dirs = true) { + $post_path = $this->_sanitize_path($post_path); + $path_depth = substr_count($post_path, "/"); + + if($create_dirs) { + $this->make_post_directory(dirname($post_path)); + } + + $qry = " + INSERT INTO posts + (host, post_path, post_path_depth, post_metadata, post_content) + VALUES + ( ?, ?, ?, ?, ?) AS new + ON DUPLICATE KEY UPDATE post_path=new.post_path;"; + + $this->_exec($qry, "ssiss", + $this->SITE_CONFIG['HTTP_HOST'], + $post_path, + $path_depth, + json_encode($post_metadata), + ''); + } + + function make_post_directory($directory) { + $json_metadata = ["type" => 'directory']; + + while(strlen($directory) > 1) { + try { + $this->bump_post($directory, $json_metadata, false); + } + catch(Exception $e) { + } + + $directory = dirname($directory); + } + } + + function log_post_access($post_path, $agent, $referrer, $time) { + $post_path = $this->_sanitize_path($post_path); + + $qry = "INSERT INTO path_access_counts + (access_time, + host, post_path, agent, referrer, + path_access_count, + path_processing_time) + VALUES ( from_unixtime(floor(unix_timestamp(CURRENT_TIMESTAMP) / 300)*300), + ?, ?, ?, ?, 1, ? + ) AS new + ON DUPLICATE KEY + UPDATE path_access_count=path_access_counts.path_access_count+1, + path_processing_time=path_access_counts.path_processing_time+new.path_processing_time; + "; + + $this->_exec($qry, "ssssd", $this->SITE_CONFIG['HTTP_HOST'], $post_path, $agent, $referrer, $time); + + if(preg_match('/^user/', $agent)) { + $this->_exec("UPDATE posts SET post_access_count=post_access_count+1 WHERE post_path=? AND host=?", "ss", + $post_path, $this->SITE_CONFIG['HTTP_HOST']); + } + } + + function get_post_access_counters() { + $qry = " + SELECT host, post_path, agent, path_access_count, path_processing_time + FROM path_access_counts + WHERE path_last_access_time > ( CURRENT_TIMESTAMP - INTERVAL 10 MINUTE ); + "; + + $data = $this->_exec($qry, "")->fetch_all(MYSQLI_ASSOC); + + $out_data = []; + + foreach($data AS $post_data) { + $path = $post_data['post_path']; + + $agent_data = ($out_data[$path] ?? []); + + $agent_data[$post_data['agent']] = [ + 'count' => $post_data['path_access_count'], + 'time' => round($post_data['path_processing_time'], 6) + ]; + + $out_data[$path] = $agent_data; + } + + return $out_data; + } + + function get_post_access_counters_line() { + $qry = " + SELECT host, access_time, post_path, agent, referrer, path_access_count, path_processing_time + FROM path_access_counts + WHERE access_time < ( CURRENT_TIMESTAMP - INTERVAL 6 MINUTE ) + ORDER BY access_time DESC; + "; + + $this->raw->begin_transaction(); + $top_access_time = null; + + try { + $data = $this->_exec($qry, "")->fetch_all(MYSQLI_ASSOC); + + $data_prefix="access_metrics"; + + $out_data = ""; + + foreach($data AS $post_data) { + $top_access_time ??= $post_data['access_time']; + + $path = $post_data['post_path']; + if($path == '') { + $path = '/'; + } + $out_data .= $data_prefix . ",host=" . $post_data['host'] . ",agent=".$post_data['agent']; + $out_data .= ",path=".$path.",referrer=".$post_data['referrer']; + + $out_data .= " access_sum=" . $post_data['path_access_count'] . ",time_sum=" . $post_data['path_processing_time']; + $out_data .= " " . strtotime($post_data['access_time']) . "000000000\n"; + } + + + $this->_exec("DELETE FROM path_access_counts WHERE access_time <= ?", "s", $top_access_time); + + $this->raw->commit(); + return $out_data; + + } catch (\Throwable $th) { + $this->raw->rollback(); + + throw $th; + } + } + + function reset_post_settings_cache($post_path) { + $post_path = $this->_sanitize_path($post_path); + + $this->_exec(" + UPDATE posts + SET post_settings_cache=NULL + WHERE host = ? AND post_path LIKE ?; + ", "ss", $this->SITE_CONFIG['HTTP_HOST'], $post_path . "%"); + } + + function escape_tag($tag) { + return preg_replace_callback('/[\WZ]/', function($match) { + return "Z" . ord($match[0]); + }, strtolower($tag)); + } + + function escape_search_tag($tag) { + preg_match("/^([\+\-]?)(.*?)(\*?)$/", $tag, $matches); + + if(!isset($matches[1])) { + echo "Problem with tag!"; + var_dump($tag); + } + + return $matches[1] . $this->escape_tag($matches[2]) . $matches[3]; + } + + function update_post_search_data($post_path, $post_tags) { + $post_tags []= "path:" . $post_path; + $post_tags []= "host:" . $this->SITE_CONFIG['HTTP_HOST']; + + $post_tags = array_unique($post_tags); + $post_tags = array_map(function($val) { + return $this->escape_tag($val); + }, $post_tags); + + asort($post_tags); + $post_tags = join(' ', $post_tags); + + $qry = " + INSERT INTO posts + ( host, post_path, post_tags ) + VALUES + ( ?, ?, ? ) AS new + ON DUPLICATE KEY + UPDATE post_tags=new.post_tags; + "; + + $this->_exec($qry, "sss", + $this->SITE_CONFIG['HTTP_HOST'], $post_path, $post_tags); + } + + function perform_post_search($taglist, $order = null, $limit = 20, $page = 0) { + $allowed_ordering = [ + "post_create_time" + ]; + + $qry = " + SELECT post_path, post_metadata + FROM posts + WHERE MATCH(post_tags) AGAINST (? IN BOOLEAN MODE) + "; + + if(!is_array($taglist)) { + $taglist = explode(' ', $taglist); + } + + $taglist []= '+host:' . $this->SITE_CONFIG['HTTP_HOST']; + $taglist = array_unique($taglist); + $taglist = array_map(function($key) { + return $this->escape_search_tag($key); + }, $taglist); + + $taglist = implode(' ', $taglist); + + if(isset($order) and in_array($order, $allowed_ordering)) { + $qry = $qry . " ORDER BY " . $order; + } + + $qry = $qry . " LIMIT ? OFFSET ?"; + + $search_results = $this->_exec($qry, "sii", $taglist, $limit, $limit * $page)->fetch_all(MYSQLI_ASSOC); + $search_results = [ + "query_string" => $taglist, + "results" => $this->_normalize_post_array($search_results) + ]; + + return $search_results; + } + + function update_or_create_post($post_path, $post_metadata, $post_content) { + $post_path = $this->_sanitize_path($post_path); + $path_depth = substr_count($post_path, "/"); + + $this->make_post_directory(dirname($post_path)); + + $this->reset_post_settings_cache($post_path); + + $qry = " + INSERT INTO posts + (host, post_path, post_path_depth, post_metadata, post_content) + VALUES + ( ?, ?, ?, ?, ?) AS new + ON DUPLICATE KEY + UPDATE post_metadata=new.post_metadata, + post_content=new.post_content, + post_update_time=CURRENT_TIMESTAMP;"; + + $this->_exec($qry, "ssiss", + $this->SITE_CONFIG['HTTP_HOST'], + $post_path, + $path_depth, + json_encode($post_metadata), + $post_content); + + $this->update_post_search_data($post_path, $post_metadata['tags'] ?? []); + } + + function get_settings_for_path($post_path) { + $post_path = $this->_sanitize_path($post_path); + + $post_settings = $this->_exec(" + SELECT post_path, post_settings_cache + FROM posts + WHERE post_path = ? AND host = ? + ", "ss", $post_path, $this->SITE_CONFIG['HTTP_HOST'])->fetch_assoc(); + + if(!isset($post_settings)) { + return []; + } + if(isset($post_settings['post_settings_cache'])) { + return json_decode($post_settings['post_settings_cache'], true); + } + + $parent_settings = []; + if($post_path != "") { + $parent_settings = $this->get_settings_for_path(dirname($post_path)); + } + + $post_settings = []; + $post_metadata = $this->_exec(" + SELECT post_path, post_metadata + FROM posts + WHERE post_path = ? AND host = ? + ", "ss", $post_path, $this->SITE_CONFIG['HTTP_HOST'])->fetch_assoc(); + + if(isset($post_metadata['post_metadata'])) { + $post_metadata = json_decode($post_metadata['post_metadata'], true); + + if(isset($post_metadata['settings'])) { + $post_settings = $post_metadata['settings']; + } + } + + $post_settings = array_merge($parent_settings, $post_settings); + + $this->_exec("UPDATE posts SET post_settings_cache=? WHERE post_path=?", "ss", + json_encode($post_settings), $post_path); + + return $post_settings; + } + + function get_post_by_path($post_path, + $with_subposts = false, $with_settings = true) { + + $qry = "SELECT * + FROM posts + WHERE post_path = ? AND host = ? + "; + + $post_path = $this->_sanitize_path($post_path); + + $post_data = $this->_exec($qry, "ss", $post_path, $this->SITE_CONFIG['HTTP_HOST'])->fetch_assoc(); + + $post_data ??= ['found' => false]; + $post_data['post_path'] = $post_path; + + $post_data = $this->_normalize_post_data($post_data); + + if(!$post_data['found']) { + return $post_data; + } + + if($with_subposts) { + $post_data['subposts'] = $this->get_subposts_by_path($post_path); + } + if($with_settings) { + $post_data['settings'] = $this->get_settings_for_path($post_path); + } + + return $post_data; + } + + function get_subposts_by_path($path) { + global $sql; + + $path = $this->_sanitize_path($path); + + $path_depth = substr_count($path, "/"); + + $qry = "SELECT post_path, post_metadata, post_update_time + FROM posts + WHERE + host = ? + AND (post_path LIKE CONCAT(?,'/%')) + AND post_path_depth = ? + ORDER BY post_path ASC + LIMIT 50"; + + $post_data = $this->_exec($qry, "ssi", $this->SITE_CONFIG['HTTP_HOST'], + $path, $path_depth+1)->fetch_all(MYSQLI_ASSOC); + + $post_data = $this->_normalize_post_array($post_data); + + return $post_data; + } +} + +?> diff --git a/www/post_adapter.php b/www/post_adapter.php new file mode 100644 index 0000000..65b4ced --- /dev/null +++ b/www/post_adapter.php @@ -0,0 +1,226 @@ +data_directory = 'raw/' . $this->SITE_CONFIG['HTTP_HOST']; + } + + function deduce_post_type($post_path) { + $ext = pathinfo($post_path, PATHINFO_EXTENSION); + + if(preg_match("/\.(\w+)\.md$/", $post_path, $ext_match)) { + $ext = $ext_match[1]; + } + + $ext_mapping = [ + '' => 'directory', + 'md' => 'text/markdown', + 'png' => 'image', + 'jpg' => 'image', + 'jpeg' => 'image' + ]; + + return $ext_mapping[$ext] ?? '?'; + } + + function fill_in_post_meta($post_path, $meta) { + $icon_mapping = [ + '' => 'question', + 'text/markdown' => 'markdown', + 'directory' => 'folder', + 'gallery' => 'images', + 'image' => 'image' + ]; + + $meta["title"] ??= basename($post_path); + + if($meta["title"] == "") { + $meta["title"] = "root"; + } + + if(!isset($meta['media_file']) and preg_match("/\.(\w+)\.md$/", $post_path)) { + $meta['media_file'] = "https://" . $this->SITE_CONFIG['HTTP_HOST'] . chop($post_path, ".md"); + } + + $meta['tags'] ??= []; + $meta['type'] ??= $this->deduce_post_type($post_path); + + $meta['icon'] ??= $icon_mapping[$meta['type']] ?? 'question'; + + return $meta; + } + + function _normalize_post_data($post_data) { + $post_data = parent::_normalize_post_data($post_data); + + if(!$post_data['found']) { + return $post_data; + } + + $post_data["post_basename"] = basename($post_data["post_path"]); + + $post_meta = $post_data['post_metadata']; + + $post_data['post_metadata'] = $this->fill_in_post_meta( + $post_data['post_path'], + $post_meta); + + $post_data["post_file_dir"] = '/raw' . $post_data["post_path"]; + + return $post_data; + } + + function make_post_directory($directory) { + $data_directory = $this->data_directory . $directory; + + is_dir($data_directory) || mkdir($data_directory, 0777, true); + + parent::make_post_directory($directory); + } + + function update_or_create_post(...$args) { + $this->_exec("TRUNCATE feed_cache"); + parent::update_or_create_post(...$args); + } + + function save_file($post_path, $file_path) { + move_uploaded_file($file_path, $this->data_directory . $post_path); + } + + function save_markdown_post($post_path, $post_data) { + $frontmatter_post = YamlFrontMatter::parse($post_data); + $post_path = $this->_sanitize_path($post_path); + + $post_content = $frontmatter_post->body(); + + $post_metadata = $frontmatter_post->matter(); + + $post_metadata = $this->fill_in_post_meta( + $post_path, + $post_metadata); + + $post_metadata['tags'][]= 'type:' . $post_metadata['type']; + + if(basename($post_path) == "README.md") { + $readme_metadata = []; + if(isset($post_metadata['settings'])) { + $readme_metadata['settings'] = $post_metadata['settings']; + } + if(isset($post_metadata['directory'])) { + $readme_metadata = $post_metadata['directory']; + } + + $this->update_or_create_post(dirname($post_path), + $readme_metadata, $post_content); + } + + $this->update_or_create_post($post_path, $post_metadata, $post_content); + } + + function handle_upload($post_path, $file_path) { + $ext = pathinfo($post_path, PATHINFO_EXTENSION); + + switch($ext) { + case "md": + $this->save_markdown_post($post_path, file_get_contents($file_path)); + + move_uploaded_file($file_path, $this->data_directory . $post_path); + break; + default: + $this->save_file($post_path, $file_path); + } + } + + function try_get_cached_feed($path, $export_opt) { + $post_cache = $this->_exec("SELECT feed_content, feed_created_on + FROM feed_cache + WHERE host=? AND search_path=? AND export_type=?", + "sss", $this->SITE_CONFIG['HTTP_HOST'], $path, $export_opt)->fetch_assoc(); + + if(!isset($post_cache)) { + return null; + } + + return ['feed' => $post_cache['feed_content'], 'feed_ts' => $post_cache['feed_created_on']]; + } + + function construct_feed($path) { + $path = $this->_sanitize_path($path); + + $feed = @new Feed; + + $feed->setTitle("DergFeed"); + $feed->setLink($this->SITE_CONFIG['uri_prefix'] . $path); + $feed->setFeedLink($this->SITE_CONFIG['uri_prefix'] . "/feeds/atom" . $path, "atom"); + + $feed->setDateModified(time()); + + $feed->setDescription("DergenFeed for all your " . $path . " needs"); + + $feed_posts = $this->_exec("SELECT + post_path, + post_create_time, post_update_time, + post_content, + post_metadata + FROM posts + WHERE (host = ?) AND ((post_path = ?) OR (post_path LIKE ?)) + ORDER BY post_create_time DESC LIMIT 200", + "sss", $this->SITE_CONFIG['HTTP_HOST'], $path, $path . '/%'); + + while($row = $feed_posts->fetch_array(MYSQLI_ASSOC)) { + $row = $this->_normalize_post_data($row); + $pmeta = $row['post_metadata']; + + if($pmeta['type'] == 'directory') { + continue; + } + + $entry = $feed->createEntry(); + + $entry->setTitle($row['post_path'] . '> ' . $pmeta['title']); + $entry->setLink($this->SITE_CONFIG['uri_prefix'] . $row['post_path']); + $entry->setDateModified(strtotime($row['post_update_time'])); + $entry->setDateCreated(strtotime($row['post_create_time'])); + + $entry->setDescription($pmeta['brief'] ?? $pmeta['title']); + + $feed->addEntry($entry); + } + + return $feed; + } + + function get_laminas_feed($path, $export_opt) { + $path = $this->_sanitize_path($path); + + $feed_cache = $this->try_get_cached_feed($path, $export_opt); + + if(isset($feed_cache)) { + return $feed_cache; + } + + $feed = $this->construct_feed($path); + + $this->_exec("INSERT INTO feed_cache + (host, search_path, export_type, feed_content) + VALUES + (?, ?, 'atom', ?), + (?, ?, 'rss', ?)", + "ssssss", + $this->SITE_CONFIG['HTTP_HOST'], $path, $feed->export('atom'), + $this->SITE_CONFIG['HTTP_HOST'], $path, $feed->export('rss')); + + return $this->try_get_cached_feed($path, $export_opt); + } +} + +?> \ No newline at end of file diff --git a/www/robots.txt b/www/robots.txt new file mode 100644 index 0000000..14267e9 --- /dev/null +++ b/www/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / \ No newline at end of file diff --git a/www/router.php b/www/router.php new file mode 100644 index 0000000..18c4641 --- /dev/null +++ b/www/router.php @@ -0,0 +1,268 @@ + true, + 'cache' => 'twig_cache' +]); +$twig->addExtension(new Twig\Extra\Markdown\MarkdownExtension()); + +use Twig\Extra\Markdown\DefaultMarkdown; +use Twig\Extra\Markdown\MarkdownRuntime; +use Twig\RuntimeLoader\RuntimeLoaderInterface; + +function dergdown_to_html($text) { + $Parsedown = new Dergdown(); + + return $Parsedown->text($text); +} + +function deduce_user_agent() { + $real_agent=$_SERVER['HTTP_USER_AGENT']; + + if(preg_match('/(Googlebot|\w*Google\w*)/', $real_agent, $match)) { + return "bot/google/" . $match[1]; + } + elseif(preg_match('/(Mozilla|Chrome|Chromium)/', $real_agent, $match)) { + return "user/" . $match[1]; + } + else { + return "unidentified"; + } +} + +function log_and_die($path, $die_code = 0, $referrer = null) { + global $data_time_start; + global $adapter; + + $data_time_end = microtime(true); + + if(!isset($referrer)) { + $referrer = 'magic'; + + if(isset($_SERVER['HTTP_REFERER'])) { + $referrer = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST); + } + } + + $adapter->log_post_access($path, + deduce_user_agent(), + $referrer, + $data_time_end - $data_time_start); + + die($die_code); +} + +$twig->addRuntimeLoader(new class implements RuntimeLoaderInterface { + public function load($class) { + if (MarkdownRuntime::class === $class) { + return new MarkdownRuntime(new DefaultMarkdown()); + } + } +}); + +function render_twig($template, $args = []) { + global $twig; + global $FONT_AWESOME_ARRAY; + global $SITE_CONFIG; + + $args['fa'] = $FONT_AWESOME_ARRAY; + + $post = $args['post'] ?? []; + $settings = $post['settings'] ?? []; + $meta = $post['post_metadata'] ?? []; + + $args['banner'] ??= $settings['banners'] ?? $SITE_CONFIG['banners']; + + $args['og'] = array_merge([ + "site_name" => $SITE_CONFIG['opengraph']['site_name'], + "title" => $meta['title'] ?? $SITE_CONFIG['opengraph']['site_name'], + "url" => $_SERVER['REQUEST_URI'], + "type" => "article", + "description" => $meta['description'] + ?? $settings['description'] + ?? $SITE_CONFIG['opengraph']['description'] + ], $args['og'] ?? []); + + if(($meta['type'] ?? '') == 'image') { + $args['og']['image'] ??= $meta['media_file']; + $args['og']['type'] = "image"; + } + + $args['og']['image'] ??= $args['banner'][0]["src"]; + + $args['banner'] = json_encode($args['banner']); + + $args['site_config'] = $SITE_CONFIG; + + $args['age_gate'] = (!isset($_COOKIE['AgeConfirmed'])) && isset($SITE_CONFIG['age_gate']); + + $args['content_html'] ??= dergdown_to_html($post['post_content'] ?? ''); + + echo $twig->render($template, $args); +} + +function try_render_post($SURI) { + global $adapter; + + $post = $adapter->get_post_by_path($SURI); + + if(!$post['found']) { + echo render_twig('post_types/rrror.html',[ + "error_code" => '404 Hoard not found!', + "error_description" => "Well, we searched + far and wide for `" . $SURI . "` but + somehow it must have gotten lost... Sorry!", + "post" => array_merge($post, [ + "post_metadata" => ["title" => "404 ???"] + ]) + ]); + + log_and_die('/404', referrer: ($_SERVER['HTTP_REFERER'] ?? 'magic')); + } + + switch($post['post_metadata']['type']) { + case 'directory': + if(preg_match('/^(.*[^\/])((?:#.*)?)$/', $SURI, $match)) { + header('Location: ' . $match[1] . '/' . $match[2]); + + die(); + } + + echo render_twig('post_types/directory.html', [ + "post" => $post, + "subposts" => $adapter->get_subposts_by_path($SURI) + ]); + + break; + + case 'text/markdown': + echo render_twig('post_types/markdown.html', [ + "post" => $post + ]); + break; + + case 'gallery': + if(preg_match('/^(.*[^\/])((?:#.*)?)$/', $SURI, $match)) { + header('Location: ' . $match[1] . '/' . $match[2]); + + die(); + } + + $search_query = $post['post_metadata']['search_tags'] ?? + ('+type:image +path:' . $post['post_path'] . '/*'); + + $search_result = $adapter->perform_post_search($search_query); + + echo render_twig('post_types/gallery.html', [ + "post" => $post, + "subposts" => $adapter->get_subposts_by_path($SURI), + "gallery_images" => $search_result['results'] + ]); + break; + + case 'image': + echo render_twig('post_types/image.html', [ + "post" => $post, + ]); + break; + } +} + +function generate_website($SURI) { + global $adapter; + global $FONT_AWESOME_ARRAY; + + if(preg_match('/^\/api\/admin/', $SURI)) { + header('Content-Type: application/json'); + + $user_api_key = ''; + if(isset($_GET['api_key'])) { + $user_api_key = $_GET['api_key']; + } + if(isset($_POST['api_key'])) { + $user_api_key = $_POST['api_key']; + } + + if($user_api_key != file_get_contents('secrets/api_admin_key')) { + http_response_code(401); + + echo json_encode([ + "authorized" => false + ]); + + log_and_die('/api/401'); + } + + if($SURI = '/api/admin/upload') { + $adapter->handle_upload($_POST['post_path'], $_FILES['post_data']['tmp_name']); + + echo json_encode(["ok" => true]); + } + } elseif(preg_match('/^\/api/', $SURI)) { + if($SURI == '/api/post_counters') { + header('Content-Type: application/json'); + echo json_encode($adapter->get_post_access_counters()); + } elseif($SURI == '/api/metrics') { + header('Content-Type: application/line'); + echo $adapter->get_post_access_counters_line(); + } elseif(preg_match('/^\/api\/posts(.*)$/', $SURI, $match)) { + + header('Content-Type: application/json'); + echo json_encode($adapter->get_post_by_path($match[1])); + + } elseif(preg_match('/^\/api\/subposts(.*)$/', $SURI, $match)) { + + header('Content-Type: application/json'); + echo json_encode(get_subposts($match[1])); + } elseif($SURI == '/api/upload') { + + echo $twig->render('upload.html'); + } elseif($SURI == '/api/search') { + + header('Content-Type: application/json'); + echo json_encode($adapter->perform_post_search($_GET['search_query'])); + } + } elseif(preg_match('/^\/feed(?:\/(rss|atom)(.*))?$/', $SURI, $match)) { + $feed = $adapter->get_laminas_feed($match[2] ?? '/', $match[1] ?? 'rss'); + + header('Content-Type: application/xml'); + header('Cache-Control: max-age=1800'); + header('Etag: W/"' . $SURI . '/' . strtotime($feed['feed_ts']) . '"'); + + echo $feed['feed']; + } elseif(true) { + try_render_post($SURI); + } +} + +$URL_PATH = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); +generate_website($URL_PATH); + +log_and_die($URL_PATH); + +?> diff --git a/www/secrets/.gitignore b/www/secrets/.gitignore new file mode 100644 index 0000000..f573c46 --- /dev/null +++ b/www/secrets/.gitignore @@ -0,0 +1,2 @@ +*.json +api_admin_key \ No newline at end of file diff --git a/www/secrets/.htaccess b/www/secrets/.htaccess new file mode 100644 index 0000000..6c5ba68 --- /dev/null +++ b/www/secrets/.htaccess @@ -0,0 +1,2 @@ + +Deny from all \ No newline at end of file diff --git a/static/.htaccess b/www/static/.htaccess similarity index 87% rename from static/.htaccess rename to www/static/.htaccess index 5997760..1c117e2 100644 --- a/static/.htaccess +++ b/www/static/.htaccess @@ -1,5 +1,7 @@ Allow from all +Options +Indexes + Header set Cache-Control "max-age=315360, public" diff --git a/www/static/age_gate_check.js b/www/static/age_gate_check.js new file mode 100644 index 0000000..716ba3c --- /dev/null +++ b/www/static/age_gate_check.js @@ -0,0 +1,12 @@ + + +function confirmAge() { + let ageGate = document.getElementById("age_gate_block"); + ageGate.style.opacity = 0; + + setTimeout(() => { + ageGate.parentElement.removeChild(ageGate); + }, 1000); + + document.cookie += "AgeConfirmed=true; path=/; max-age=31536000" +} \ No newline at end of file diff --git a/www/static/banner.js b/www/static/banner.js new file mode 100644 index 0000000..1dc78c1 --- /dev/null +++ b/www/static/banner.js @@ -0,0 +1,160 @@ + +const BANNER_TIME = 600 * 1000.0 +const BANNER_ANIMATION = "opacity 0.8s linear, transform 0.1s linear" + +class BannerHandler { + constructor(banner_container, banner_image, banner_link) { + this.bannerContainerDOM = banner_container + this.bannerDOM = banner_image + this.bannerLinkDOM = banner_link + + this.bannerUpdateTimer = null + this.currentPhase = 0 + + this.currentBannerData = null + try { + this.currentBannerData = JSON.parse(localStorage.getItem('main_banner_img')) + } catch(e) {} + + this.currentBannerData ||= {} + + this.bannerDOM.onload=() => { this.onBannerLoaded() } + this.bannerDOM.onerror=() => { + this.fadeOut(); + setTimeout(() => this.loadNextBanner(), 1000); + } + } + + startUpdateTick() { + if(this.bannerUpdateTimer !== null) { + return + } + + console.log("Starting tick") + + this.bannerUpdateTimer = setInterval(() => { this.updateTick() }, 100); + } + stopUpdateTick() { + if(this.bannerUpdateTimer === null) { + return + } + + console.log("Stopping tick!") + + clearInterval(this.bannerUpdateTimer); + this.bannerUpdateTimer = null + } + + getPhase() { + return (new Date()).getTime() / BANNER_TIME; + } + + getTargetBanner() { + if(window.dergBannerOptions == null) { + return {} + } + + var banner_index = Math.floor(this.getPhase()) % window.dergBannerOptions.length + var banner_choice = window.dergBannerOptions[banner_index] + + return banner_choice + } + + updateTranslation() { + const bannerTranslateMax = -this.bannerDOM.clientHeight + this.bannerContainerDOM.clientHeight + + const bannerPercentageFrom = this.currentBannerData.from || 0; + const bannerPercentageTo = this.currentBannerData.to || 1; + + const bannerPercentage = (bannerPercentageFrom + (bannerPercentageTo - bannerPercentageFrom) * this.currentPhase) + + const banner_top = (1-bannerPercentage) * bannerTranslateMax + this.bannerDOM.style.transform = "translateY(" + banner_top + 'px' + ")" + } + + fadeOut() { + this.bannerDOM.style.opacity = 0; + } + fadeIn() { + this.bannerDOM.style.opacity = this.currentBannerData.opacity || 0.3; + } + + loadNextBanner() { + this.currentBannerData = this.getTargetBanner() + this.currentBannerData.bannerTime = new Date() + + this.loadBanner() + } + + loadBanner() { + console.log("Target banner:"); + console.log(this.currentBannerData); + + localStorage.setItem("main_banner_img", JSON.stringify(this.currentBannerData)) + + this.currentPhase = 0 + + if((this.currentBannerData === null) + || (this.currentBannerData.src === undefined)) { + + this.onBannerLoaded() + return + } + + this.bannerDOM.src = this.currentBannerData.src + this.bannerLinkDOM.href = this.currentBannerData.href || this.currentBannerData.src + } + onBannerLoaded() { + console.log("Loaded?"); + + this.currentPhase = this.getPhase() % 1 + + this.updateTranslation() + this.fadeIn() + + setTimeout(() => { + this.animateOn() + + this.startUpdateTick() + }, 10) + } + + updateTick() { + console.log("tick") + + const nextPhase = this.getPhase() % 1; + + if((nextPhase > this.currentPhase) + && (this.currentBannerData.src == this.getTargetBanner().src)) { + + this.currentPhase = nextPhase; + this.updateTranslation(); + } else { + this.fadeOut() + setTimeout(() => { + this.loadNextBanner() + }, 1000); + + this.stopUpdateTick() + } + } + + animateOn() { + this.bannerDOM.style.transition = BANNER_ANIMATION + } + + start() { + this.fadeIn() + + this.loadBanner() + } +} + +var bannerHandler = new BannerHandler( + document.getElementById("main_header"), + document.getElementById("main_banner_img"), + document.getElementById("main_banner_img_link")) + +bannerHandler.start() + +// addEventListener("resize", () => update_banner(banner, banner_container)); diff --git a/www/static/dergstyle.css b/www/static/dergstyle.css new file mode 100644 index 0000000..ccde5fa --- /dev/null +++ b/www/static/dergstyle.css @@ -0,0 +1,273 @@ + + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +svg { + fill: var(--text_1); + padding-top: 0.1rem; +} + +body { + --bg_1: #0e0a2a; + --bg_2: #2c2943; + --bg_3: #3f4148; + + --highlight_1: #ee9015; + --highlight_2: #edd29e; + + --text_1: #FFFFFF; + --text_border: #A0A0A080; + + color: var(--text_1); + background: var(--bg_1); + margin: 0px; + + position: relative; + + min-height: 100vh; + padding-bottom: 4rem; +} + +@media only screen and (max-width: 600px) { + .hsmol_hide { + display: none !important; + visibility: hidden !important; + } +} + +:link { + color: var(--highlight_1); + font-style: italic; + text-decoration: none; + + transition: color 0.2s; +} +a:visited { + color: var(--highlight_1); +} +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; + + padding-bottom: 0.7rem; +} +#main_banner_img { + position: absolute; + left: 0px; + right: 0px; + + width: 100vw; + + z-index: -1; + + opacity: 0; + top: 0px; +} +#main_banner_img_link { + position: absolute; + right: 2vw; + bottom: 0.5rem; + + font-size: 0.8rem; +} + +#nav_bar { + display: flex; + flex-direction: row; + justify-content: center; + + list-style-type: none; + margin-top: 1rem; + padding: 0px; +} +#nav_bar li { + padding: 0rem 0.3rem 0rem 0.3rem; +} + +#big_title { + text-align: center; + font-size: 2.5rem; + margin-bottom: 0.2rem; +} +#main_header h2 { + text-align: center; + font-size: 2rem; + margin-bottom: 0.2rem; +} + +#title_separator { + height: 1.5px; + background-color: #ddd; + opacity: 0.5; + margin-left: 2rem; + margin-right: 2rem; +} + +:target { + scroll-margin-top: 6rem; +} + +#main_content_wrapper { + --content-width: min(100vw, calc(20rem + 40vw)); + --content-total-margin: calc(calc(100vw - var(--content-width)) / 2); + + --content-padding: max(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-top: 0px; + + min-height: 100%; + + background: var(--bg_2); +} + +#post_file_bar { + position: sticky; + + top: 0px; + + background: var(--bg_2); + + box-shadow: 0px 5px 5px 0px #00000040; + z-index: 5; +} + +#post_file_titles { + display: flex; + flex-direction: row; + justify-content: left; + + list-style-type: none; + padding: 0px; +} + +#post_file_titles * { + padding: 0.5rem; + font-style: bold; + font-size: 1.3rem; + + background: var(--highlight_1); +} + +#post_file_path { + width: 100%; + font-style: italic; + padding-left: 0.5rem; + + background: var(--highlight_1); + + 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; +} + +#main_content_wrapper article { + background: var(--bg_3); + + border-radius: 0rem 0rem 0.8rem 0.8rem; + box-shadow: 3px 7px 7px 0px #00000040; + + padding: 0.75rem; +} + +#main_content_wrapper article img { + display: block; + max-width: 100%; + + max-height: 70vh; + + margin: 1vmin; + margin-right: auto; + margin-left: auto; +} + +#content_footer { + display: block; + max-width: 100%; + + margin-top: 0.5em; + + font-size: 0.8em; + border-top: solid 1px darkgrey; + opacity: 0.7; +} + +#main_footer { + display: flex; + + height: 2.5rem; + text-align: center; + background-color: #3a3a3a; + margin: 0px; + + position: absolute; + bottom: 0px; + width: 100%; +} + +#main_footer span { + align-self: flex-end; + width: 100%; +} diff --git a/www/static/directorystyle.css b/www/static/directorystyle.css new file mode 100644 index 0000000..d762c6f --- /dev/null +++ b/www/static/directorystyle.css @@ -0,0 +1,36 @@ + + +table.directory { + width: 100%; +} + +table.directory caption { + text-align: left; + font-size: 1.5rem; + + padding-left: 3vmin; +} + +table.directory td { + padding: 0.2rem; + text-align: left; +} +table.directory th { + padding: 0.2rem; + text-align: left; + + padding-bottom: 0.05rem; +} + +table.directory tr.entry:hover { + background: rgba(255, 255, 255, 0.1); +} + +table.directory tr.entry .entry_title { + width: 100%; +} + +table.directory .entry_update_time { + display: block; + width: 12rem; +} \ No newline at end of file diff --git a/www/static/gallerystyle.css b/www/static/gallerystyle.css new file mode 100644 index 0000000..c73ab1a --- /dev/null +++ b/www/static/gallerystyle.css @@ -0,0 +1,64 @@ +.gallery { + display: flex; + flex-direction: row; + + flex-flow: row wrap; + + justify-content: center; + + list-style-type: none; + margin-top: 1rem; + padding: 0px; +} + +.gallery li { + margin: 0.2rem; + margin-bottom: 1rem; + border: 1px solid var(--text_border); + float: left; + + border-radius: 1rem; + box-shadow: 0px 5px 5px 0px #00000040; + + background: var(--bg_2); + + height: auto; + + transition: 0.3s; + z-index: 1; +} + +.gallery li:hover { + border: 1px solid var(--text_1); + transform: scale(1.02); +} + +#main_content_wrapper .gallery img { + width: 100%; + height: 15rem; + margin: 0; + + border-radius: 1rem 1rem 0 0; + + object-fit: cover; +} + +@media screen and (max-width: 48rem) { + .gallery li { + width: 45%; + } + + #main_content_wrapper .gallery img { + height: 100%; + width: auto; + } + + #main_content_wrapper .gallery figure { + height: auto; + } +} + +.gallery figcaption { + padding: 0.1rem; + text-align: center; +} \ No newline at end of file diff --git a/static/icon.jpeg b/www/static/icon.jpeg similarity index 100% rename from static/icon.jpeg rename to www/static/icon.jpeg diff --git a/static/gallerystyle.css b/www/static/imagestyle.css similarity index 58% rename from static/gallerystyle.css rename to www/static/imagestyle.css index bb7ba01..149aee6 100644 --- a/static/gallerystyle.css +++ b/www/static/imagestyle.css @@ -1,18 +1,12 @@ -article { - margin-top: 0.8em; - margin-left: 10%; - margin-right: 10%; -} - -figcaption { +#main_content_wrapper figcaption h1 { text-align: center; border-bottom: solid 3px darkgrey; - font-size: 2em; + font-size: 1.8rem; - margin-bottom: 0.3em; + margin-bottom: 0.3rem; } #gallery_image { diff --git a/www/static/modest.css b/www/static/modest.css new file mode 100644 index 0000000..d45177d --- /dev/null +++ b/www/static/modest.css @@ -0,0 +1,203 @@ +/* +modest.css, licensed under MIT license, Crafted with <3 by John Otander (@4lpine). + +Taken from: https://github.com/markdowncss/modest + +Modified to fit Dergsite needs +*/ + +@media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; + box-shadow: none !important; + text-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } +} + +pre, +code { + font-family: Menlo, Monaco, "Courier New", monospace; +} + +.modest-no-decoration { + text-decoration: none; +} + +html { + font-size: 14px; +} + +@media screen and (min-width: 32rem) and (max-width: 48rem) { + html { + font-size: 16px; + } +} + +@media screen and (min-width: 48rem) { + html { + font-size: 17px; + } +} + +article { + line-height: 1.5; +} + +article p, +.modest-p { + font-size: 1rem; + margin-bottom: 1.3rem; +} + +article h1, +.modest-h1, +article h2, +.modest-h2, +article h3, +.modest-h3, +article h4, +.modest-h4 { + margin: 1.5em 0 .3em; + font-weight: inherit; + line-height: 1.42; + + padding-left: 1.5rem; +} + +article > :first-child { + margin-top: 0.3rem !important; +} + +article h1, +.modest-h1 { + margin-top: 0; + font-size: 1.998rem; +} + +article h2, +.modest-h2 { + font-size: 1.427rem; +} + +article h3, +.modest-h3 { + font-size: 1.299rem; +} + +article h4, +.modest-h4 { + font-size: 1.1rem; +} + +article h5, +.modest-h5 { + font-size: 1rem; +} + +article h6, +.modest-h6 { + font-size: .88rem; +} + +article small, +.modest-small { + font-size: .707rem; +} + +/* https://github.com/mrmrs/fluidity */ + +article h1, +article h2, +article h3 { + border-bottom: 1px solid var(--text_border); + padding-bottom: .3rem; +} + +blockquote { + padding-left: 0.8rem; + margin-left: 0.8rem; + margin-right: 4em; +} + +blockquote { + border-left: 4px solid var(--text_border); + text-align: justify; +} + +pre { + border-radius: 0.5rem; + + box-shadow: 2px 5px 5px 0px #00000040; + + border-left: 4px solid #206475; + background-color: var(--bg_2); + margin-bottom: 1.3rem; + margin-left: 0.8rem; + margin-right: 4em; +} +pre code { + border-radius: 0.5rem; +} + +@media screen and (max-width: 32rem) { + pre, blockquote { + margin-right: 1.5em; + } +} + +article ul, +article ol { + padding-left: 2em; +} \ No newline at end of file diff --git a/www/static/rrrorstyle.css b/www/static/rrrorstyle.css new file mode 100644 index 0000000..62c0e69 --- /dev/null +++ b/www/static/rrrorstyle.css @@ -0,0 +1,13 @@ + + +#rrr_header { + font-size: 2rem; + + border-bottom: 1px solid grey; + padding-bottom: 0.2rem; + margin-bottom: 0.3rem; +} + +#rrr_code { + font-size: 1.5rem; +} \ No newline at end of file diff --git a/www/static/solarized-dark.css b/www/static/solarized-dark.css new file mode 100644 index 0000000..b4c0da1 --- /dev/null +++ b/www/static/solarized-dark.css @@ -0,0 +1,84 @@ +/* + +Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #002b36; + color: #839496; +} + +.hljs-comment, +.hljs-quote { + color: #586e75; +} + +/* Solarized Green */ +.hljs-keyword, +.hljs-selector-tag, +.hljs-addition { + color: #859900; +} + +/* Solarized Cyan */ +.hljs-number, +.hljs-string, +.hljs-meta .hljs-meta-string, +.hljs-literal, +.hljs-doctag, +.hljs-regexp { + color: #2aa198; +} + +/* Solarized Blue */ +.hljs-title, +.hljs-section, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class { + color: #268bd2; +} + +/* Solarized Yellow */ +.hljs-attribute, +.hljs-attr, +.hljs-variable, +.hljs-template-variable, +.hljs-class .hljs-title, +.hljs-type { + color: #b58900; +} + +/* Solarized Orange */ +.hljs-symbol, +.hljs-bullet, +.hljs-subst, +.hljs-meta, +.hljs-meta .hljs-keyword, +.hljs-selector-attr, +.hljs-selector-pseudo, +.hljs-link { + color: #cb4b16; +} + +/* Solarized Red */ +.hljs-built_in, +.hljs-deletion { + color: #dc322f; +} + +.hljs-formula { + background: #073642; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} diff --git a/www/templates/fragments/filepath_bar.html b/www/templates/fragments/filepath_bar.html new file mode 100644 index 0000000..31b5265 --- /dev/null +++ b/www/templates/fragments/filepath_bar.html @@ -0,0 +1,33 @@ +
    + +
  • + {{ post.post_metadata.title }} +
  • +
    + + {% set split_post = post.post_path |split('/') %} + {% for i in range(0, split_post|length - 1) %} +
  • + {% if i != 0 %} + + > {{ split_post[i] }} + + {% else %} + root + {% endif %} +
  • + {% endfor %} + + +
  • + raw + api +
  • +
  • + + {{ fa['rss']|raw }} + +
  • +
    +
    \ No newline at end of file diff --git a/templates/gallery/gallery_entry.html b/www/templates/gallery/gallery_entry.html similarity index 100% rename from templates/gallery/gallery_entry.html rename to www/templates/gallery/gallery_entry.html diff --git a/templates/gallery/gallery_overview.html b/www/templates/gallery/gallery_overview.html similarity index 100% rename from templates/gallery/gallery_overview.html rename to www/templates/gallery/gallery_overview.html diff --git a/www/templates/pathed_content.html b/www/templates/pathed_content.html new file mode 100644 index 0000000..af5b566 --- /dev/null +++ b/www/templates/pathed_content.html @@ -0,0 +1,29 @@ + + +{% extends "root.html" %} + +{% block feed_links %} + +{{ parent() }} + + +{% endblock %} + +{% block second_title %} +

    {{ post.post_metadata.title }}

    +{% endblock %} + +{%block main_content%} + + {{ include('fragments/filepath_bar.html') }} + +
    + {%block content_article %} + {%endblock%} + + + This article was created on {{ post.post_create_time }}, last edited {{ post.post_update_time }}, and was viewed {{ post.post_access_count }} times~ + +
    + +{%endblock%} diff --git a/www/templates/post_types/directory.html b/www/templates/post_types/directory.html new file mode 100644 index 0000000..ffacda0 --- /dev/null +++ b/www/templates/post_types/directory.html @@ -0,0 +1,43 @@ + + +{% extends "pathed_content.html" %} + +{% block extra_head %} + +{%endblock%} + +{%block content_article%} + {% if subposts|length > 0 %} +

    Directory contents:

    + + + + + + + + {% for subpost in subposts %} + + + + + + + {% endfor %} +
    NameTitleModified
    + {{ fa[subpost.post_metadata.icon] | raw }} + + {{subpost.post_basename}} + + {{ subpost.post_metadata.title }} + + {{ subpost.post_update_time }} +
    + {%else%} +

    This directory appears to be empty...

    + This shouldn't happen, sorry! + {% endif %} + + {{ post.post_content|markdown_to_html }} + +{%endblock%} diff --git a/www/templates/post_types/gallery.html b/www/templates/post_types/gallery.html new file mode 100644 index 0000000..4e96b3d --- /dev/null +++ b/www/templates/post_types/gallery.html @@ -0,0 +1,59 @@ + + +{% extends "pathed_content.html" %} + +{% block extra_head %} + + +{%endblock%} + +{%block content_article%} + {% if subposts|length > 0 %} + +

    Art Subfolders:

    + + + + + + + {% for subpost in subposts %} + + + + + + + {% endfor %} +
    NameTitleModified
    + {{ fa[subpost.post_metadata.icon] | raw }} + + {{subpost.post_basename}} + + {{ subpost.post_metadata.title }} + + {{ subpost.post_update_time }} +
    + {% endif %} + + {{ content_html|raw }} + + {% if gallery_images|length > 0 %} + + {%else%} +

    How sad. There are no images yet... What a real shame :c

    + {% endif %} +{%endblock%} diff --git a/www/templates/post_types/image.html b/www/templates/post_types/image.html new file mode 100644 index 0000000..aff2115 --- /dev/null +++ b/www/templates/post_types/image.html @@ -0,0 +1,25 @@ + + +{% extends "pathed_content.html" %} + +{% block extra_head %} + +{%endblock%} + +{% block opengraph_tags %} + {{ parent() }} + + +{%endblock %} + +{%block content_article%} +
    + + + + +
    + {{ content_html|raw }} +
    +
    +{%endblock%} diff --git a/www/templates/post_types/markdown.html b/www/templates/post_types/markdown.html new file mode 100644 index 0000000..b9d147c --- /dev/null +++ b/www/templates/post_types/markdown.html @@ -0,0 +1,13 @@ + + +{% extends "pathed_content.html" %} + +{% block opengraph_tags %} + {{ parent() }} + + +{%endblock %} + +{%block content_article%} + {{ content_html|raw }} +{% endblock %} \ No newline at end of file diff --git a/www/templates/post_types/rrror.html b/www/templates/post_types/rrror.html new file mode 100644 index 0000000..0e258e1 --- /dev/null +++ b/www/templates/post_types/rrror.html @@ -0,0 +1,19 @@ + +{% extends "pathed_content.html" %} + +{% block extra_head %} + +{% endblock %} + +{% block second_title %} +

    (Broken)

    +{% endblock %} + +{% block content_article %} +

    The Dergs are confused:

    +

    {{ error_code }}

    + +
    + {{ error_description|markdown_to_html }} +
    +{% endblock %} \ No newline at end of file diff --git a/www/templates/root.html b/www/templates/root.html new file mode 100644 index 0000000..01a87d6 --- /dev/null +++ b/www/templates/root.html @@ -0,0 +1,90 @@ + + + + {{og.site_name}} - {{og.title}} + + + + + + + + + {% block feed_links %} + + {% endblock %} + + {% block extra_head %}{% endblock %} + + {% block opengraph_tags %} + + + + + + + + + + + + + + + + + + + + {% endblock %} + + + + + {%if age_gate %} + +
    +
    +

    + This website may contain content meant for an 18+ audience. +

    + + +
    +
    + + + {% endif %} + +
    + + full picture + + + +

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

    + {% block second_title %}{% endblock %} +
    + + +
  • About
  • +
  • Blog
  • +
  • Projects
  • +
  • Artworks
  • +
    +
    + +
    + {% block main_content %}

    Soon there shall be content!

    {% endblock %} +
    + + + + + diff --git a/www/templates/upload.html b/www/templates/upload.html new file mode 100644 index 0000000..bee8710 --- /dev/null +++ b/www/templates/upload.html @@ -0,0 +1,19 @@ + + +{% extends "root.html" %} + +{% block second_title %} +

    UPLOAD CONTENT

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